@rootzero/contracts 0.6.3 → 0.7.1

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 (47) hide show
  1. package/Commands.sol +8 -16
  2. package/Core.sol +4 -2
  3. package/Cursors.sol +2 -2
  4. package/Events.sol +4 -2
  5. package/Queries.sol +10 -0
  6. package/blocks/Cursors.sol +168 -8
  7. package/blocks/Keys.sol +4 -1
  8. package/blocks/Schema.sol +32 -11
  9. package/blocks/Writers.sol +536 -95
  10. package/commands/Base.sol +20 -6
  11. package/commands/Provision.sol +5 -12
  12. package/commands/Stake.sol +1 -84
  13. package/commands/admin/AllowAssets.sol +7 -5
  14. package/commands/admin/DenyAssets.sol +7 -5
  15. package/commands/admin/Relocate.sol +2 -2
  16. package/core/Access.sol +2 -4
  17. package/core/CursorBase.sol +43 -0
  18. package/core/Host.sol +1 -1
  19. package/core/HostBound.sol +14 -0
  20. package/core/Operation.sol +41 -50
  21. package/events/Erc721.sol +20 -0
  22. package/events/Query.sol +20 -0
  23. package/events/Swap.sol +20 -0
  24. package/package.json +1 -1
  25. package/peer/AllowAssets.sol +4 -9
  26. package/peer/AssetPull.sol +42 -0
  27. package/peer/Base.sol +11 -0
  28. package/peer/DenyAssets.sol +4 -9
  29. package/peer/Pull.sol +8 -11
  30. package/peer/Push.sol +6 -3
  31. package/queries/Assets.sol +46 -0
  32. package/queries/Balances.sol +46 -0
  33. package/queries/Base.sol +34 -0
  34. package/queries/Positions.sol +49 -0
  35. package/utils/Ids.sol +68 -1
  36. package/utils/Layout.sol +2 -0
  37. package/commands/Borrow.sol +0 -89
  38. package/commands/Liquidate.sol +0 -99
  39. package/commands/Liquidity.sol +0 -180
  40. package/commands/Mint.sol +0 -54
  41. package/commands/Reclaim.sol +0 -55
  42. package/commands/Redeem.sol +0 -99
  43. package/commands/Repay.sol +0 -99
  44. package/commands/Swap.sol +0 -90
  45. package/commands/Unstake.sol +0 -58
  46. package/events/Quote.sol +0 -21
  47. /package/events/{HostAnnounced.sol → Host.sol} +0 -0
package/commands/Base.sol CHANGED
@@ -20,6 +20,25 @@ struct CommandContext {
20
20
  bytes request;
21
21
  }
22
22
 
23
+ /// @notice ABI-encode a command call from a target command ID and execution context.
24
+ /// @dev Derives the function selector from `target` via `Ids.commandSelector(target)`.
25
+ /// Reverts if `target` is not a valid command ID.
26
+ /// @param target Destination command node ID embedding the target selector.
27
+ /// @param account Caller account identifier for the command context.
28
+ /// @param state Current state block stream passed to the command.
29
+ /// @param request Input block stream for the command invocation.
30
+ /// @return ABI-encoded calldata for the command entry point.
31
+ function encodeCommandCall(
32
+ uint target,
33
+ bytes32 account,
34
+ bytes memory state,
35
+ bytes calldata request
36
+ ) pure returns (bytes memory) {
37
+ bytes4 selector = Ids.commandSelector(target);
38
+ CommandContext memory ctx = CommandContext(target, account, state, request);
39
+ return abi.encodeWithSelector(selector, ctx);
40
+ }
41
+
23
42
  /// @title CommandBase
24
43
  /// @notice Abstract base for all rootzero command contracts.
25
44
  /// Provides access control modifiers, event emission, and the `commandId`
@@ -78,10 +97,7 @@ abstract contract CommandPayable is CommandBase {
78
97
  /// @dev Calls the amount-based `settleValue` hook only when some value remains.
79
98
  /// @param account Caller's account identifier for the current invocation.
80
99
  /// @param budget Mutable native-value budget used during command execution.
81
- function settleValue(
82
- bytes32 account,
83
- Budget memory budget
84
- ) internal {
100
+ function settleValue(bytes32 account, Budget memory budget) internal {
85
101
  uint remaining = Values.drain(budget);
86
102
  if (remaining != 0) settleValue(account, remaining);
87
103
  }
@@ -96,5 +112,3 @@ abstract contract CommandPayable is CommandBase {
96
112
  revert UnusedValue(remaining);
97
113
  }
98
114
  }
99
-
100
-
@@ -29,11 +29,7 @@ abstract contract ProvisionPayableHook {
29
29
  /// @param account Caller's account identifier.
30
30
  /// @param custody Destination host plus asset amount to provision.
31
31
  /// @param budget Mutable native-value budget drawn from `msg.value`.
32
- function provision(
33
- bytes32 account,
34
- HostAmount memory custody,
35
- Budget memory budget
36
- ) internal virtual;
32
+ function provision(bytes32 account, HostAmount memory custody, Budget memory budget) internal virtual;
37
33
  }
38
34
 
39
35
  /// @title Provision
@@ -46,9 +42,7 @@ abstract contract Provision is CommandBase, ProvisionHook {
46
42
  emit Command(host, PROVISION, Schemas.Custody, provisionId, State.Empty, State.Custodies, false);
47
43
  }
48
44
 
49
- function provision(
50
- CommandContext calldata c
51
- ) external onlyCommand(provisionId, c.target) returns (bytes memory) {
45
+ function provision(CommandContext calldata c) external onlyCommand(provisionId, c.target) returns (bytes memory) {
52
46
  (Cur memory request, uint count, ) = cursor(c.request, 1);
53
47
  Writer memory writer = Writers.allocCustodies(count);
54
48
 
@@ -107,12 +101,12 @@ abstract contract ProvisionFromBalance is CommandBase, ProvisionHook {
107
101
  (Cur memory state, uint stateCount, ) = cursor(c.state, 1);
108
102
  Cur memory request = cursor(c.request);
109
103
  Writer memory writer = Writers.allocCustodies(stateCount);
110
- uint toHost = request.nodeAfter(0);
111
- if (toHost == 0) revert Cursors.ZeroNode();
104
+ uint peer = request.nodeAfter(0);
105
+ if (peer == 0) revert Cursors.ZeroNode();
112
106
 
113
107
  while (state.i < state.bound) {
114
108
  (bytes32 asset, bytes32 meta, uint amount) = state.unpackBalance();
115
- HostAmount memory custody = HostAmount(toHost, asset, meta, amount);
109
+ HostAmount memory custody = HostAmount(peer, asset, meta, amount);
116
110
  provision(c.account, custody);
117
111
  writer.appendCustody(custody);
118
112
  }
@@ -120,4 +114,3 @@ abstract contract ProvisionFromBalance is CommandBase, ProvisionHook {
120
114
  return state.complete(writer);
121
115
  }
122
116
  }
123
-
@@ -2,94 +2,11 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { CommandContext, CommandBase, State } from "./Base.sol";
5
- import { AssetAmount, HostAmount, Cur, Cursors, Writer, Writers } from "../Cursors.sol";
5
+ import { HostAmount, Cur, Cursors } from "../Cursors.sol";
6
6
 
7
- string constant SBTB = "stakeBalanceToBalances";
8
- string constant SCTB = "stakeCustodyToBalances";
9
7
  string constant SCTP = "stakeCustodyToPosition";
10
8
 
11
9
  using Cursors for Cur;
12
- using Writers for Writer;
13
-
14
- /// @title StakeBalanceToBalances
15
- /// @notice Command that stakes BALANCE state positions and emits BALANCE outputs.
16
- /// The output-to-input ratio is set at construction via `scaledRatio`.
17
- abstract contract StakeBalanceToBalances is CommandBase {
18
- uint internal immutable stakeBalanceToBalancesId = commandId(SBTB);
19
- uint private immutable outScale;
20
-
21
- constructor(string memory input, uint scaledRatio) {
22
- outScale = scaledRatio;
23
- emit Command(host, SBTB, input, stakeBalanceToBalancesId, State.Balances, State.Balances, false);
24
- }
25
-
26
- /// @dev Override to stake a balance position and append resulting balances
27
- /// to `out`. `request` is the live auxiliary request cursor for this
28
- /// command; implementations validate and unpack it as needed and may
29
- /// append BALANCE outputs within the capacity implied by this command's
30
- /// configured `scaledRatio`.
31
- function stakeBalanceToBalances(
32
- bytes32 account,
33
- AssetAmount memory balance,
34
- Cur memory request,
35
- Writer memory out
36
- ) internal virtual;
37
-
38
- function stakeBalanceToBalances(
39
- CommandContext calldata c
40
- ) external onlyCommand(stakeBalanceToBalancesId, c.target) returns (bytes memory) {
41
- (Cur memory state, uint stateCount, ) = cursor(c.state, 1);
42
- Cur memory request = cursor(c.request);
43
- Writer memory writer = Writers.allocScaledBalances(stateCount, outScale);
44
-
45
- while (state.i < state.bound) {
46
- AssetAmount memory balance = state.unpackBalanceValue();
47
- stakeBalanceToBalances(c.account, balance, request, writer);
48
- }
49
-
50
- return state.complete(writer);
51
- }
52
- }
53
-
54
- /// @title StakeCustodyToBalances
55
- /// @notice Command that stakes CUSTODY state positions and emits BALANCE outputs.
56
- /// The output-to-input ratio is set at construction via `scaledRatio`.
57
- abstract contract StakeCustodyToBalances is CommandBase {
58
- uint internal immutable stakeCustodyToBalancesId = commandId(SCTB);
59
- uint private immutable outScale;
60
-
61
- constructor(string memory input, uint scaledRatio) {
62
- outScale = scaledRatio;
63
- emit Command(host, SCTB, input, stakeCustodyToBalancesId, State.Custodies, State.Balances, false);
64
- }
65
-
66
- /// @dev Override to stake a custody position and append resulting balances
67
- /// to `out`. `request` is the live auxiliary request cursor for this
68
- /// command; implementations validate and unpack it as needed and may
69
- /// append BALANCE outputs within the capacity implied by this command's
70
- /// configured `scaledRatio`.
71
- function stakeCustodyToBalances(
72
- bytes32 account,
73
- HostAmount memory custody,
74
- Cur memory request,
75
- Writer memory out
76
- ) internal virtual;
77
-
78
- function stakeCustodyToBalances(
79
- CommandContext calldata c
80
- ) external onlyCommand(stakeCustodyToBalancesId, c.target) returns (bytes memory) {
81
- (Cur memory state, uint stateCount, ) = cursor(c.state, 1);
82
- Cur memory request = cursor(c.request);
83
- Writer memory writer = Writers.allocScaledBalances(stateCount, outScale);
84
-
85
- while (state.i < state.bound) {
86
- HostAmount memory custody = state.unpackCustodyValue();
87
- stakeCustodyToBalances(c.account, custody, request, writer);
88
- }
89
-
90
- return state.complete(writer);
91
- }
92
- }
93
10
 
94
11
  /// @title StakeCustodyToPosition
95
12
  /// @notice Command that stakes CUSTODY state positions into a non-balance target
@@ -7,20 +7,22 @@ using Cursors for Cur;
7
7
 
8
8
  string constant NAME = "allowAssets";
9
9
 
10
+ abstract contract AllowAssetsHook {
11
+ /// @dev Override to allow a single asset/meta pair.
12
+ /// Called once per ASSET block in the request.
13
+ function allowAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
14
+ }
15
+
10
16
  /// @title AllowAssets
11
17
  /// @notice Admin command that permits a list of (asset, meta) pairs via a virtual hook.
12
18
  /// Each ASSET block in the request calls `allowAsset`. Only callable by the admin account.
13
- abstract contract AllowAssets is CommandBase {
19
+ abstract contract AllowAssets is CommandBase, AllowAssetsHook {
14
20
  uint internal immutable allowAssetsId = commandId(NAME);
15
21
 
16
22
  constructor() {
17
23
  emit Command(host, NAME, Schemas.Asset, allowAssetsId, State.Empty, State.Empty, false);
18
24
  }
19
25
 
20
- /// @dev Override to allow a single asset/meta pair.
21
- /// Called once per ASSET block in the request.
22
- function allowAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
23
-
24
26
  function allowAssets(
25
27
  CommandContext calldata c
26
28
  ) external onlyAdmin(c.account) onlyCommand(allowAssetsId, c.target) returns (bytes memory) {
@@ -7,20 +7,22 @@ using Cursors for Cur;
7
7
 
8
8
  string constant NAME = "denyAssets";
9
9
 
10
+ abstract contract DenyAssetsHook {
11
+ /// @dev Override to deny a single asset/meta pair.
12
+ /// Called once per ASSET block in the request.
13
+ function denyAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
14
+ }
15
+
10
16
  /// @title DenyAssets
11
17
  /// @notice Admin command that blocks a list of (asset, meta) pairs via a virtual hook.
12
18
  /// Each ASSET block in the request calls `denyAsset`. Only callable by the admin account.
13
- abstract contract DenyAssets is CommandBase {
19
+ abstract contract DenyAssets is CommandBase, DenyAssetsHook {
14
20
  uint internal immutable denyAssetsId = commandId(NAME);
15
21
 
16
22
  constructor() {
17
23
  emit Command(host, NAME, Schemas.Asset, denyAssetsId, State.Empty, State.Empty, false);
18
24
  }
19
25
 
20
- /// @dev Override to deny a single asset/meta pair.
21
- /// Called once per ASSET block in the request.
22
- function denyAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
23
-
24
26
  function denyAssets(
25
27
  CommandContext calldata c
26
28
  ) external onlyAdmin(c.account) onlyCommand(denyAssetsId, c.target) returns (bytes memory) {
@@ -26,8 +26,8 @@ abstract contract RelocatePayable is CommandPayable {
26
26
  Budget memory budget = Values.fromMsg();
27
27
 
28
28
  while (request.i < request.bound) {
29
- (uint host, uint amount) = request.unpackFunding();
30
- callTo(host, Values.use(budget, amount), "");
29
+ (uint peer, uint amount) = request.unpackFunding();
30
+ callTo(peer, Values.use(budget, amount), "");
31
31
  }
32
32
 
33
33
  request.complete();
package/core/Access.sol CHANGED
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { AccessEvent } from "../events/Access.sol";
5
+ import { HostBound } from "./HostBound.sol";
5
6
  import { Accounts } from "../utils/Accounts.sol";
6
7
  import { Ids } from "../utils/Ids.sol";
7
8
  import { addrOr } from "../utils/Utils.sol";
@@ -12,14 +13,12 @@ import { addrOr } from "../utils/Utils.sol";
12
13
  /// mapping of externally authorized node IDs. Inbound trust is host-based:
13
14
  /// authorized hosts, the commander, and this contract itself may interact
14
15
  /// with the host through the guarded command and peer entrypoints.
15
- abstract contract AccessControl is AccessEvent {
16
+ abstract contract AccessControl is HostBound, AccessEvent {
16
17
  /// @dev Trusted commander address. All calls from this address are implicitly trusted.
17
18
  /// Defaults to `address(this)` when no external commander is provided.
18
19
  address internal immutable commander;
19
20
  /// @dev Admin account ID derived from the commander address at construction time.
20
21
  bytes32 internal immutable adminAccount;
21
- /// @dev This host's node ID, set to `Ids.toHost(address(this))` at construction.
22
- uint public immutable host;
23
22
 
24
23
  /// @dev Mapping from node ID to authorization status.
25
24
  /// Authorised nodes may interact with the host as trusted callers or call targets.
@@ -33,7 +32,6 @@ abstract contract AccessControl is AccessEvent {
33
32
  constructor(address cmdr) {
34
33
  commander = addrOr(cmdr, address(this));
35
34
  adminAccount = Accounts.toAdmin(commander);
36
- host = Ids.toHost(address(this));
37
35
  }
38
36
 
39
37
  /// @notice Grant or revoke authorization for a node.
@@ -0,0 +1,43 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { Cur, Cursors } from "../Cursors.sol";
5
+
6
+ using Cursors for Cur;
7
+
8
+ /// @title CursorBase
9
+ /// @notice Shared cursor convenience helpers for contracts that consume block streams.
10
+ abstract contract CursorBase {
11
+ /// @notice Open a cursor over a calldata block stream.
12
+ /// @param source Calldata slice to parse.
13
+ /// @return cur Cursor positioned at the beginning of `source`.
14
+ function cursor(bytes calldata source) internal pure returns (Cur memory cur) {
15
+ return Cursors.open(source);
16
+ }
17
+
18
+ /// @notice Open a cursor and prime it for a grouped iteration pass in one call.
19
+ /// Equivalent to `open(source)` followed by `primeRun(group)`.
20
+ /// @param source Calldata slice to parse.
21
+ /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
22
+ /// @return cur Cursor with `bound` set to the end of the first run.
23
+ /// @return count Total number of blocks in the run (a multiple of `group`).
24
+ /// @return quotient Number of groups in the run (`count / group`).
25
+ function cursor(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint count, uint quotient) {
26
+ cur = Cursors.open(source);
27
+ (, count, quotient) = cur.primeRun(group);
28
+ }
29
+
30
+ /// @notice Open a cursor, prime it, and assert that its normalized quotient matches `expectedQuotient`.
31
+ /// Equivalent to `open(source)` followed by `primeRun(group)` and a direct quotient equality check.
32
+ /// Reverts with `Cursors.BadRatio` when the quotient does not match.
33
+ /// @param source Calldata slice to parse.
34
+ /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
35
+ /// @param expectedQuotient Required number of groups in the first run.
36
+ /// @return cur Cursor with `bound` set to the end of the first run.
37
+ function cursor(bytes calldata source, uint group, uint expectedQuotient) internal pure returns (Cur memory cur) {
38
+ cur = Cursors.open(source);
39
+ (, , uint quotient) = cur.primeRun(group);
40
+ if (quotient != expectedQuotient) revert Cursors.BadRatio();
41
+ }
42
+
43
+ }
package/core/Host.sol CHANGED
@@ -5,7 +5,7 @@ import { AccessControl } from "./Access.sol";
5
5
  import { Authorize } from "../commands/admin/Authorize.sol";
6
6
  import { Unauthorize } from "../commands/admin/Unauthorize.sol";
7
7
  import { RelocatePayable } from "../commands/admin/Relocate.sol";
8
- import { HostAnnouncedEvent } from "../events/HostAnnounced.sol";
8
+ import { HostAnnouncedEvent } from "../events/Host.sol";
9
9
  import { IHostDiscovery } from "../interfaces/IHostDiscovery.sol";
10
10
  import { Ids } from "../utils/Ids.sol";
11
11
 
@@ -0,0 +1,14 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {Assets} from "../utils/Assets.sol";
5
+ import {Ids} from "../utils/Ids.sol";
6
+
7
+ /// @title HostBound
8
+ /// @notice Minimal base for contracts that need host-scoped identity constants.
9
+ abstract contract HostBound {
10
+ /// @dev This contract's host node ID, set to `Ids.toHost(address(this))` at construction.
11
+ uint public immutable host = Ids.toHost(address(this));
12
+ /// @dev Asset ID for the native chain value (ETH), bound to the current chain at deployment.
13
+ bytes32 internal immutable valueAsset = Assets.toValue();
14
+ }
@@ -2,81 +2,72 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { AccessControl } from "./Access.sol";
5
- import { Assets } from "../utils/Assets.sol";
5
+ import { CursorBase } from "./CursorBase.sol";
6
6
  import { Ids } from "../utils/Ids.sol";
7
- import { Cur, Cursors } from "../Cursors.sol";
8
-
9
- using Cursors for Cur;
10
7
 
11
8
  /// @dev Emitted when a trusted inter-node call fails.
12
9
  /// @param addr Contract address that was called.
13
- /// @param node Node ID of the callee.
14
10
  /// @param selector 4-byte selector of the called function.
15
11
  /// @param err Revert data returned by the failed call.
16
- error FailedCall(address addr, uint node, bytes4 selector, bytes err);
12
+ error FailedCall(address addr, bytes4 selector, bytes err);
17
13
 
18
14
  /// @title OperationBase
19
15
  /// @notice Shared base for command and peer contracts.
20
16
  /// Provides convenience wrappers for cursor construction, quotient validation,
21
17
  /// and trusted inter-node calls. Inherits access control from `AccessControl`.
22
- abstract contract OperationBase is AccessControl {
23
- /// @dev Asset ID for the native chain value (ETH), bound to the current chain at deployment.
24
- bytes32 public immutable valueAsset = Assets.toValue();
25
-
26
- /// @notice Open a cursor over a calldata block stream.
27
- /// @param source Calldata slice to parse.
28
- /// @return cur Cursor positioned at the beginning of `source`.
29
- function cursor(bytes calldata source) internal pure returns (Cur memory cur) {
30
- return Cursors.open(source);
31
- }
32
-
33
- /// @notice Open a cursor and prime it for a grouped iteration pass in one call.
34
- /// Equivalent to `open(source)` followed by `primeRun(group)`.
35
- /// @param source Calldata slice to parse.
36
- /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
37
- /// @return cur Cursor with `bound` set to the end of the first run.
38
- /// @return count Total number of blocks in the run (a multiple of `group`).
39
- /// @return quotient Number of groups in the run (`count / group`).
40
- function cursor(bytes calldata source, uint group) internal pure returns (Cur memory cur, uint count, uint quotient) {
41
- cur = Cursors.open(source);
42
- (, count, quotient) = cur.primeRun(group);
18
+ abstract contract OperationBase is AccessControl, CursorBase {
19
+ /// @notice Return the host node ID corresponding to the current caller.
20
+ /// @dev Encodes `msg.sender` as a host ID using the local-chain host layout.
21
+ /// @return Host node ID for `msg.sender`.
22
+ function caller() internal view returns (uint) {
23
+ return Ids.toHost(msg.sender);
43
24
  }
44
25
 
45
- /// @notice Open a cursor, prime it, and assert that its normalized quotient matches `expectedQuotient`.
46
- /// Equivalent to `open(source)` followed by `primeRun(group)` and `checkQuotient(quotient, expectedQuotient)`.
47
- /// Reverts with `Cursors.BadRatio` when the quotient does not match.
48
- /// @param source Calldata slice to parse.
49
- /// @param group Expected block group size (e.g. 1 for single, 2 for paired).
50
- /// @param expectedQuotient Required number of groups in the first run.
51
- /// @return cur Cursor with `bound` set to the end of the first run.
52
- function cursor(bytes calldata source, uint group, uint expectedQuotient) internal pure returns (Cur memory cur) {
53
- cur = Cursors.open(source);
54
- (, , uint quotient) = cur.primeRun(group);
55
- if (quotient != expectedQuotient) revert Cursors.BadRatio();
26
+ /// @notice Make a low-level call to an address.
27
+ /// Forwards `value` ETH and `data` to `addr`.
28
+ /// Reverts with `FailedCall` if the call is unsuccessful.
29
+ /// @param addr Contract address to call.
30
+ /// @param value Native value to forward in wei.
31
+ /// @param data Encoded calldata to send.
32
+ /// @return out Return data from the successful call.
33
+ function callAddr(address addr, uint value, bytes memory data) internal returns (bytes memory out) {
34
+ bool success;
35
+ (success, out) = payable(addr).call{value: value}(data);
36
+ if (!success) revert FailedCall(addr, bytes4(data), out);
56
37
  }
57
38
 
58
- /// @notice Assert that two normalized group quotients are equal.
59
- /// Reverts with `Cursors.BadRatio` when `lq != rq`.
60
- /// @param lq Left-hand quotient.
61
- /// @param rq Right-hand quotient.
62
- function checkQuotient(uint lq, uint rq) internal pure {
63
- if (lq != rq) revert Cursors.BadRatio();
39
+ /// @notice Make a low-level read-only query to an address.
40
+ /// Issues a low-level `staticcall` with `data`.
41
+ /// Reverts with `FailedCall` if the call is unsuccessful.
42
+ /// @param addr Contract address to query.
43
+ /// @param data Encoded calldata to send.
44
+ /// @return out Return data from the successful query.
45
+ function queryAddr(address addr, bytes memory data) internal view returns (bytes memory out) {
46
+ bool success;
47
+ (success, out) = addr.staticcall(data);
48
+ if (!success) revert FailedCall(addr, bytes4(data), out);
64
49
  }
65
50
 
66
51
  /// @notice Make a trusted call to another node in the network.
67
52
  /// Looks up the node's contract address via `ensureTrusted` + `Ids.nodeAddr`,
68
53
  /// then issues a low-level call forwarding `value` ETH and `data`.
69
- /// Reverts with `FailedCall` if the call is unsuccessful.
70
54
  /// @param node Node ID of the callee (must be in the authorized set).
71
55
  /// @param value Native value to forward in wei.
72
56
  /// @param data Encoded calldata to send.
73
57
  /// @return out Return data from the successful call.
74
58
  function callTo(uint node, uint value, bytes memory data) internal returns (bytes memory out) {
75
- bool success;
76
59
  address addr = Ids.nodeAddr(ensureTrusted(node));
77
- (success, out) = payable(addr).call{value: value}(data);
78
- if (!success) {
79
- revert FailedCall(addr, node, bytes4(data), out);
80
- }
60
+ return callAddr(addr, value, data);
61
+ }
62
+
63
+ /// @notice Make a trusted query to another node in the network.
64
+ /// Looks up the node's contract address via `ensureTrusted` + `Ids.nodeAddr`,
65
+ /// then issues a low-level `staticcall` with `data`.
66
+ /// @param node Node ID of the callee (must be in the authorized set).
67
+ /// @param data Encoded calldata to send.
68
+ /// @return out Return data from the successful query.
69
+ function queryTo(uint node, bytes memory data) internal view returns (bytes memory out) {
70
+ address addr = Ids.nodeAddr(ensureTrusted(node));
71
+ return queryAddr(addr, data);
81
72
  }
82
73
  }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {EventEmitter} from "./Emitter.sol";
5
+
6
+ string constant ABI = "event Erc721Position(bytes32 indexed account, bytes32 asset, bytes32 meta, uint position, uint qid)";
7
+
8
+ /// @notice Emitted when the lifecycle state of an ERC-721-backed position changes.
9
+ abstract contract Erc721PositionEvent is EventEmitter {
10
+ /// @param account Account identifier that owns or is associated with the position.
11
+ /// @param asset Asset identifier for the ERC-721 class.
12
+ /// @param meta Asset metadata slot, typically carrying the token-specific position context.
13
+ /// @param position Context-specific position value; positive means active and 0 means closed.
14
+ /// @param qid Query ID associated with the position lookup or reporting context.
15
+ event Erc721Position(bytes32 indexed account, bytes32 asset, bytes32 meta, uint position, uint qid);
16
+
17
+ constructor() {
18
+ emit EventAbi(ABI);
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {EventEmitter} from "./Emitter.sol";
5
+
6
+ string constant ABI = "event Query(uint indexed host, string name, string input, string output, uint qid)";
7
+
8
+ /// @notice Emitted once per query during host deployment to publish its request and response schemas.
9
+ abstract contract QueryEvent is EventEmitter {
10
+ /// @param host Host node ID that owns this query.
11
+ /// @param name Human-readable query name.
12
+ /// @param input Schema DSL string describing the query request shape.
13
+ /// @param output Schema DSL string describing the query response shape.
14
+ /// @param qid Query node ID.
15
+ event Query(uint indexed host, string name, string input, string output, uint qid);
16
+
17
+ constructor() {
18
+ emit EventAbi(ABI);
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import { EventEmitter } from "./Emitter.sol";
5
+
6
+ string constant ABI = "event Swap(bytes32 indexed account, bytes32 assetIn, uint amountIn, bytes32 assetOut, uint amountOut)";
7
+
8
+ /// @notice Emitted when an account swaps one asset for another.
9
+ abstract contract SwapEvent is EventEmitter {
10
+ /// @param account Account identifier performing the swap.
11
+ /// @param assetIn Input asset identifier.
12
+ /// @param amountIn Input amount spent.
13
+ /// @param assetOut Output asset identifier.
14
+ /// @param amountOut Output amount received.
15
+ event Swap(bytes32 indexed account, bytes32 assetIn, uint amountIn, bytes32 assetOut, uint amountOut);
16
+
17
+ constructor() {
18
+ emit EventAbi(ABI);
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rootzero/contracts",
3
- "version": "0.6.3",
3
+ "version": "0.7.1",
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",
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.33;
3
3
 
4
4
  import { PeerBase } from "./Base.sol";
5
+ import { AllowAssetsHook } from "../commands/admin/AllowAssets.sol";
5
6
  import { Cursors, Cur, Schemas } from "../Cursors.sol";
6
7
 
7
8
  using Cursors for Cur;
@@ -10,27 +11,21 @@ string constant NAME = "peerAllowAssets";
10
11
 
11
12
  /// @title PeerAllowAssets
12
13
  /// @notice Peer that permits a list of (asset, meta) pairs on behalf of a remote host.
13
- /// Each ASSET block in the request calls `peerAllowAsset`. Restricted to trusted peers.
14
- abstract contract PeerAllowAssets is PeerBase {
14
+ /// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
15
+ abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook {
15
16
  uint internal immutable peerAllowAssetsId = peerId(NAME);
16
17
 
17
18
  constructor() {
18
19
  emit Peer(host, NAME, Schemas.Asset, peerAllowAssetsId, false);
19
20
  }
20
21
 
21
- /// @notice Override to permit a single (asset, meta) pair.
22
- /// @param asset Asset identifier.
23
- /// @param meta Asset metadata slot.
24
- /// @return True if the asset was newly allowed, false if it was already allowed.
25
- function peerAllowAsset(bytes32 asset, bytes32 meta) internal virtual returns (bool);
26
-
27
22
  /// @notice Execute the allow-assets peer call.
28
23
  function peerAllowAssets(bytes calldata request) external onlyPeer returns (bytes memory) {
29
24
  (Cur memory assets, , ) = cursor(request, 1);
30
25
 
31
26
  while (assets.i < assets.bound) {
32
27
  (bytes32 asset, bytes32 meta) = assets.unpackAsset();
33
- peerAllowAsset(asset, meta);
28
+ allowAsset(asset, meta);
34
29
  }
35
30
 
36
31
  assets.complete();
@@ -0,0 +1,42 @@
1
+ // SPDX-License-Identifier: GPL-3.0-only
2
+ pragma solidity ^0.8.33;
3
+
4
+ import {PeerBase} from "./Base.sol";
5
+ import {Cursors, Cur, Schemas} from "../Cursors.sol";
6
+
7
+ string constant NAME = "peerAssetPull";
8
+
9
+ using Cursors for Cur;
10
+
11
+ /// @title PeerAssetPull
12
+ /// @notice Peer that pulls requested asset amounts from a remote host into this one.
13
+ /// Each AMOUNT block in the request calls `peerAssetPull(peer, asset, meta, amount)`, where `peer`
14
+ /// is derived from `msg.sender`. Restricted to trusted peers.
15
+ abstract contract PeerAssetPull is PeerBase {
16
+ uint internal immutable peerAssetPullId = peerId(NAME);
17
+
18
+ constructor() {
19
+ emit Peer(host, NAME, Schemas.Amount, peerAssetPullId, false);
20
+ }
21
+
22
+ /// @notice Override to process one incoming amount-based asset pull request from a remote host.
23
+ /// @param peer Host node ID derived from the caller address.
24
+ /// @param asset Requested asset identifier.
25
+ /// @param meta Requested asset metadata slot.
26
+ /// @param amount Requested amount in the asset's native units.
27
+ function peerAssetPull(uint peer, bytes32 asset, bytes32 meta, uint amount) internal virtual;
28
+
29
+ /// @notice Execute the asset-pull peer call.
30
+ function peerAssetPull(bytes calldata request) external onlyPeer returns (bytes memory) {
31
+ (Cur memory assets, , ) = cursor(request, 1);
32
+ uint peer = caller();
33
+
34
+ while (assets.i < assets.bound) {
35
+ (bytes32 asset, bytes32 meta, uint amount) = assets.unpackAmount();
36
+ peerAssetPull(peer, asset, meta, amount);
37
+ }
38
+
39
+ assets.complete();
40
+ return "";
41
+ }
42
+ }