@rootzero/contracts 1.4.0 → 1.5.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 +24 -0
- package/Endpoints.sol +4 -3
- package/Events.sol +1 -1
- package/README.md +58 -31
- package/Utils.sol +4 -3
- package/blocks/Cursors.sol +83 -143
- package/blocks/Keys.sol +15 -15
- package/blocks/Schema.sol +27 -28
- package/blocks/Writers.sol +26 -33
- package/commands/Base.sol +2 -2
- package/commands/Burn.sol +3 -4
- package/commands/Credit.sol +3 -4
- package/commands/Debit.sol +4 -5
- package/commands/Deposit.sol +8 -10
- package/commands/Payout.sol +3 -6
- package/commands/Withdraw.sol +3 -4
- package/commands/admin/AllowAssets.sol +5 -6
- package/commands/admin/Allowance.sol +3 -4
- package/commands/admin/DenyAssets.sol +5 -6
- package/commands/admin/Execute.sol +2 -2
- package/core/Access.sol +2 -2
- package/core/Balances.sol +10 -11
- package/core/Calls.sol +7 -7
- package/core/Host.sol +2 -2
- package/core/Runtime.sol +3 -3
- package/core/Types.sol +0 -14
- package/docs/Schema.md +29 -10
- package/events/Asset.sol +17 -3
- package/events/Balance.sol +2 -3
- package/events/Locked.sol +2 -3
- package/events/Position.sol +2 -3
- package/events/Received.sol +2 -3
- package/events/Spent.sol +2 -3
- package/events/Unlocked.sol +2 -3
- package/guards/Base.sol +4 -4
- package/package.json +1 -1
- package/peer/AllowAssets.sol +3 -3
- package/peer/Allowance.sol +2 -2
- package/peer/Base.sol +4 -4
- package/peer/Credit.sol +10 -10
- package/peer/Debit.sol +10 -10
- package/peer/DenyAssets.sol +3 -3
- package/peer/Recover.sol +51 -0
- package/peer/Redeem.sol +48 -0
- package/peer/Settle.sol +3 -3
- package/queries/Assets.sol +7 -8
- package/queries/Balances.sol +8 -9
- package/queries/Base.sol +4 -4
- package/queries/Positions.sol +4 -6
- package/utils/Accounts.sol +76 -58
- package/utils/Assets.sol +55 -115
- package/utils/Ids.sol +33 -233
- package/utils/Layout.sol +11 -17
- package/utils/Nodes.sol +263 -0
- package/utils/Utils.sol +9 -24
- package/peer/BalancePull.sol +0 -49
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
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.5.0
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
- Refactored account, asset, and node IDs around the shared convention that
|
|
11
|
+
first byte `0x00` means opaque and nonzero means structured.
|
|
12
|
+
- Replaced the previous generic ID helpers with `Nodes` for structured host,
|
|
13
|
+
chain, command, peer, query, and guard node IDs.
|
|
14
|
+
- Changed assets to single-word IDs: `bytes32 asset` is now the unique asset key
|
|
15
|
+
without a separate metadata field or asset slot.
|
|
16
|
+
- `Payout` now passes destination accounts through to the hook unchanged; payout
|
|
17
|
+
account policy is the hook implementation's responsibility.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added `Ids` as the shared helper library for opaque IDs, including
|
|
22
|
+
`Ids.isOpaque`, `Ids.opaque`, `Ids.toKeccak`, and `Ids.matchKeccak`.
|
|
23
|
+
- Added opaque account, asset, and node helper wrappers in `Accounts`, `Assets`,
|
|
24
|
+
and `Nodes`.
|
|
25
|
+
- Added `Asset(uint indexed host, bytes32 asset, bytes preimage)` for declaring
|
|
26
|
+
opaque asset preimages.
|
|
27
|
+
- Documented opaque preimages as `[formatHash:1][payload...]`, where `0x01`
|
|
28
|
+
means keccak256.
|
|
29
|
+
|
|
6
30
|
## 1.4.0
|
|
7
31
|
|
|
8
32
|
### Breaking Changes
|
package/Endpoints.sol
CHANGED
|
@@ -36,11 +36,12 @@ import { Unauthorize } from "./commands/admin/Unauthorize.sol";
|
|
|
36
36
|
import { PeerBase, encodePeerCall } from "./peer/Base.sol";
|
|
37
37
|
import { PeerAllowAssets, IPeerAllowAssets } from "./peer/AllowAssets.sol";
|
|
38
38
|
import { PeerAllowance, IPeerAllowance } from "./peer/Allowance.sol";
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
39
|
+
import { PeerRedeemBalance, RedeemBalanceHook, IPeerRedeemBalance } from "./peer/Redeem.sol";
|
|
40
|
+
import { PeerCreditAccount, IPeerCreditAccount } from "./peer/Credit.sol";
|
|
41
|
+
import { PeerDebitAccount, IPeerDebitAccount } from "./peer/Debit.sol";
|
|
42
42
|
import { PeerDenyAssets, IPeerDenyAssets } from "./peer/DenyAssets.sol";
|
|
43
43
|
import { PeerPipePayable, IPeerPipePayable } from "./peer/Pipe.sol";
|
|
44
|
+
import { PeerRecover, RecoverHook, IPeerRecover } from "./peer/Recover.sol";
|
|
44
45
|
import { PeerDispatchPayable, IPeerDispatchPayable } from "./peer/Dispatch.sol";
|
|
45
46
|
import { PeerSettle, IPeerSettle } from "./peer/Settle.sol";
|
|
46
47
|
|
package/Events.sol
CHANGED
|
@@ -5,7 +5,7 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
// Import this file to get access to every event emitter in one import.
|
|
6
6
|
|
|
7
7
|
import { AdminEvent } from "./events/Admin.sol";
|
|
8
|
-
import { AssetStatusEvent } from "./events/Asset.sol";
|
|
8
|
+
import { AssetEvent, AssetStatusEvent } from "./events/Asset.sol";
|
|
9
9
|
import { Actions } from "./utils/Actions.sol";
|
|
10
10
|
import { BalanceEvent } from "./events/Balance.sol";
|
|
11
11
|
import { CommanderEvent } from "./events/Commander.sol";
|
package/README.md
CHANGED
|
@@ -36,14 +36,13 @@ pragma solidity ^0.8.33;
|
|
|
36
36
|
|
|
37
37
|
import { Host, Balances } from "@rootzero/contracts/Core.sol";
|
|
38
38
|
import { Deposit } from "@rootzero/contracts/Endpoints.sol";
|
|
39
|
-
import { Assets } from "@rootzero/contracts/Utils.sol";
|
|
40
39
|
|
|
41
40
|
contract ExampleHost is Host, Balances, Deposit {
|
|
42
41
|
constructor(address rootzero) Host(rootzero) {}
|
|
43
42
|
|
|
44
|
-
function deposit(bytes32 account, bytes32 asset,
|
|
45
|
-
uint balance = creditTo(account,
|
|
46
|
-
emit Balance(account, asset,
|
|
43
|
+
function deposit(bytes32 account, bytes32 asset, uint amount) internal override {
|
|
44
|
+
uint balance = creditTo(account, asset, amount);
|
|
45
|
+
emit Balance(account, asset, balance, int(amount), depositId);
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
```
|
|
@@ -58,7 +57,7 @@ implementations):
|
|
|
58
57
|
const host = await ethers.deployContract("ExampleHost", [deployer.address]);
|
|
59
58
|
|
|
60
59
|
const account = encodeUserAccount(user.address); // receiving account
|
|
61
|
-
const request = encodeAmountBlock(asset,
|
|
60
|
+
const request = encodeAmountBlock(asset, 100n); // what to deposit
|
|
62
61
|
await host.deposit({ account, state: "0x", request }); // emits Balance
|
|
63
62
|
```
|
|
64
63
|
|
|
@@ -79,10 +78,10 @@ The key is `bytes4(keccak256("#name"))`, and the payload layout is described by
|
|
|
79
78
|
a schema string. For example, the block that requests a deposit:
|
|
80
79
|
|
|
81
80
|
```txt
|
|
82
|
-
#amount { bytes32 asset,
|
|
81
|
+
#amount { bytes32 asset, uint amount }
|
|
83
82
|
```
|
|
84
83
|
|
|
85
|
-
is
|
|
84
|
+
is 72 bytes on the wire: an 8-byte header followed by two big-endian 32-byte
|
|
86
85
|
fields. There is no ABI encoding and no chain-specific type anywhere in the
|
|
87
86
|
format — field types are chain-neutral integers, bytes, and booleans. A deposit
|
|
88
87
|
request built for an EVM host is byte-for-byte the request a CosmWasm or Solana
|
|
@@ -114,8 +113,8 @@ import { concat } from "ethers";
|
|
|
114
113
|
import { encodeAmountBlock } from "./helpers/blocks";
|
|
115
114
|
|
|
116
115
|
const request = concat([
|
|
117
|
-
encodeAmountBlock(usdc,
|
|
118
|
-
encodeAmountBlock(dai,
|
|
116
|
+
encodeAmountBlock(usdc, 250_000_000n),
|
|
117
|
+
encodeAmountBlock(dai, 250n * 10n ** 18n),
|
|
119
118
|
]);
|
|
120
119
|
// deposit(request) returns two #balance blocks, one per #amount
|
|
121
120
|
```
|
|
@@ -124,29 +123,57 @@ Everything downstream keeps this shape: commands loop over request blocks,
|
|
|
124
123
|
settlement loops over transactions, pipelines loop over steps. Batching is
|
|
125
124
|
never a special case.
|
|
126
125
|
|
|
127
|
-
## IDs, Accounts, and
|
|
126
|
+
## IDs, Accounts, Assets, and Nodes
|
|
128
127
|
|
|
129
|
-
Everything the protocol touches
|
|
130
|
-
is identified by
|
|
128
|
+
Everything the protocol touches - accounts, assets, chains, hosts, endpoints -
|
|
129
|
+
is identified by one 32-byte word. The first byte selects the convention:
|
|
130
|
+
|
|
131
|
+
- `0x00`: opaque ID, encoded as `0x00 || bytes31(hash)`. The hash preimage is
|
|
132
|
+
not recoverable from the ID; use a lookup table or witness data when the
|
|
133
|
+
native account, asset metadata, or node target is needed.
|
|
134
|
+
- nonzero: structured ID. The value can be deconstructed according to its
|
|
135
|
+
layout.
|
|
136
|
+
|
|
137
|
+
Opaque preimages start with:
|
|
138
|
+
|
|
139
|
+
```txt
|
|
140
|
+
[uint8 formatHash][payload...]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Only the first byte is protocol-level convention for now. `0x01` means
|
|
144
|
+
keccak256. The remaining payload format is host/domain-specific until a future
|
|
145
|
+
standard defines it.
|
|
146
|
+
|
|
147
|
+
The field supplies the role for opaque IDs: a `bytes32 asset` with first byte
|
|
148
|
+
`0x00` is still an asset, but its native metadata must come from lookup or
|
|
149
|
+
witness data. The Solidity helpers below construct and deconstruct structured
|
|
150
|
+
EVM IDs.
|
|
151
|
+
|
|
152
|
+
Structured EVM IDs use:
|
|
131
153
|
|
|
132
154
|
```txt
|
|
133
155
|
[uint32 type][uint32 chainid][192-bit payload]
|
|
134
156
|
```
|
|
135
157
|
|
|
136
|
-
where `type` packs `[
|
|
137
|
-
what it is (an account, an asset, a node) and which
|
|
138
|
-
payload usually embeds the underlying address. User
|
|
139
|
-
chain-agnostic; admin and guardian accounts are chain-local.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
where `type` packs `[uint16 representation][uint8 category][uint8 subtype]`. A
|
|
159
|
+
structured ID announces what it is (an account, an asset, a node) and which
|
|
160
|
+
chain it lives on, and the payload usually embeds the underlying address. User
|
|
161
|
+
accounts are chain-agnostic; admin and guardian accounts are chain-local.
|
|
162
|
+
Assets are unique IDs in the same single-word form as accounts and nodes.
|
|
163
|
+
Nodes are hosts, commands, peers, queries, and guards.
|
|
164
|
+
|
|
165
|
+
Opaque asset declarations use `Asset(host, asset, preimage)`. The preimage
|
|
166
|
+
starts with a one-byte format/hash tag, letting offchain indexers or witnesses
|
|
167
|
+
verify and resolve `0x00 || bytes31(hash(preimage))` assets. `0x01` means
|
|
168
|
+
keccak256.
|
|
143
169
|
|
|
144
170
|
The `Utils.sol` entry point provides the constructors and inspectors:
|
|
145
171
|
|
|
146
172
|
```solidity
|
|
147
173
|
bytes32 account = Accounts.toUser(msg.sender); // chain-agnostic user account
|
|
148
174
|
bytes32 asset = Assets.toErc20(tokenAddress); // ERC-20 asset ID
|
|
149
|
-
uint hostId =
|
|
175
|
+
uint hostId = Nodes.toHost(address(this)); // host node ID
|
|
176
|
+
bytes32 opaque = Ids.toKeccak(preimage); // 0x00-prefixed opaque ID
|
|
150
177
|
```
|
|
151
178
|
|
|
152
179
|
## Hosts
|
|
@@ -197,9 +224,9 @@ function deposit(CommandContext calldata c) external onlyCommand returns (bytes
|
|
|
197
224
|
Writer memory writer = Writers.allocBalances(groups);
|
|
198
225
|
|
|
199
226
|
while (request.i < request.len) {
|
|
200
|
-
(bytes32 asset,
|
|
201
|
-
deposit(c.account, asset,
|
|
202
|
-
writer.appendBalance(asset,
|
|
227
|
+
(bytes32 asset, uint amount) = request.unpackAmount();
|
|
228
|
+
deposit(c.account, asset, amount); // host policy hook
|
|
229
|
+
writer.appendBalance(asset, amount);
|
|
203
230
|
}
|
|
204
231
|
|
|
205
232
|
request.complete();
|
|
@@ -270,8 +297,8 @@ standard `getBalances` query takes a run of positions and answers each one in
|
|
|
270
297
|
order:
|
|
271
298
|
|
|
272
299
|
```txt
|
|
273
|
-
request: #accountAsset { bytes32 account, bytes32 asset
|
|
274
|
-
response: #accountAmount { bytes32 account, bytes32 asset,
|
|
300
|
+
request: #accountAsset { bytes32 account, bytes32 asset }
|
|
301
|
+
response: #accountAmount { bytes32 account, bytes32 asset, uint amount }
|
|
275
302
|
```
|
|
276
303
|
|
|
277
304
|
Like commands, every query announces its request and response schemas at
|
|
@@ -283,7 +310,7 @@ Peers are the host-to-host surfaces, callable only by trusted hosts. The two
|
|
|
283
310
|
central ones are batches all the way down:
|
|
284
311
|
|
|
285
312
|
- `peerSettle` consumes `#transaction { bytes32 from, bytes32 to, bytes32 asset,
|
|
286
|
-
|
|
313
|
+
uint amount }` blocks, debiting `from` and crediting `to` per
|
|
287
314
|
block — how two hosts record settlement between their ledgers.
|
|
288
315
|
- `peerPipePayable` consumes `#pipe` blocks, each carrying an account, an
|
|
289
316
|
initial state, and a run of steps — a complete pipeline delivered by another
|
|
@@ -313,8 +340,8 @@ drop a trusted node immediately.
|
|
|
313
340
|
Hosts are self-describing. At deployment a host emits the ABI of every event it
|
|
314
341
|
uses (`EventAbi`), a discovery event per endpoint with its full schemas, and
|
|
315
342
|
labels for human-readable names. State changes then follow evented
|
|
316
|
-
conventions: `Balance` for every ledger change and flow events (`
|
|
317
|
-
`
|
|
343
|
+
conventions: `Balance` for every ledger change and flow events (`Received`,
|
|
344
|
+
`Spent`, `Locked`, `Unlocked`) for value movement, each tagged with the endpoint that
|
|
318
345
|
caused it. An indexer can reconstruct the entire repository — endpoints,
|
|
319
346
|
names, access sets, balances — from logs alone, with no artifact files.
|
|
320
347
|
|
|
@@ -328,8 +355,8 @@ Import from the package entry points rather than deep paths:
|
|
|
328
355
|
mixins and their hooks
|
|
329
356
|
- `@rootzero/contracts/Cursors.sol` — `Cur` cursor reader, `Writers`, `Schemas`,
|
|
330
357
|
`Keys`
|
|
331
|
-
- `@rootzero/contracts/Utils.sol` — `Ids`, `Assets`, `Accounts`,
|
|
332
|
-
value helpers
|
|
358
|
+
- `@rootzero/contracts/Utils.sol` — `Ids`, `Nodes`, `Assets`, `Accounts`,
|
|
359
|
+
layout and value helpers
|
|
333
360
|
- `@rootzero/contracts/Events.sol` — protocol event contracts
|
|
334
361
|
|
|
335
362
|
Repo layout:
|
|
@@ -340,7 +367,7 @@ Repo layout:
|
|
|
340
367
|
- `contracts/guards` — guardian direct actions
|
|
341
368
|
- `contracts/queries` — read-only query endpoints
|
|
342
369
|
- `contracts/blocks` — block schema, cursor parsing, writers
|
|
343
|
-
- `contracts/utils` —
|
|
370
|
+
- `contracts/utils` — ids, nodes, assets, accounts, layout, ECDSA
|
|
344
371
|
- `contracts/events` — event contracts and emitters
|
|
345
372
|
- `docs` — [`Schema.md`](docs/Schema.md) (wire format and schema DSL)
|
|
346
373
|
|
package/Utils.sol
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
|
-
// Aggregator: re-exports all utility libraries (Keys, Accounts, Actions, Assets, ECDSA, Ids, Layout, Utils, Value).
|
|
4
|
+
// Aggregator: re-exports all utility libraries (Keys, Accounts, Actions, Assets, ECDSA, Ids, Nodes, Layout, Utils, Value).
|
|
5
5
|
// Import this file to access the full utility surface without managing individual paths.
|
|
6
6
|
|
|
7
7
|
import { Keys } from "./blocks/Keys.sol";
|
|
@@ -9,10 +9,11 @@ import { Accounts } from "./utils/Accounts.sol";
|
|
|
9
9
|
import { Actions } from "./utils/Actions.sol";
|
|
10
10
|
import { Amounts, Assets } from "./utils/Assets.sol";
|
|
11
11
|
import { ECDSA } from "./utils/ECDSA.sol";
|
|
12
|
-
import { Ids
|
|
12
|
+
import { Ids } from "./utils/Ids.sol";
|
|
13
|
+
import { Nodes } from "./utils/Nodes.sol";
|
|
13
14
|
import { Layout } from "./utils/Layout.sol";
|
|
14
15
|
import { Schemas } from "./blocks/Schema.sol";
|
|
15
|
-
import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, divisible, hash32, intToBytes32, isFamily,
|
|
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
17
|
import { Budget, Values } from "./utils/Value.sol";
|
|
17
18
|
|
|
18
19
|
|