@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/core/Calls.sol
CHANGED
|
@@ -3,7 +3,7 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {AccessControl} from "./Access.sol";
|
|
5
5
|
import {CommandContext} from "../commands/Base.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {Nodes} from "../utils/Nodes.sol";
|
|
7
7
|
|
|
8
8
|
/// @dev Emitted when a trusted inter-node call fails.
|
|
9
9
|
/// @param addr Contract address that was called.
|
|
@@ -18,7 +18,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
18
18
|
/// @dev Encodes `msg.sender` as a host ID using the local-chain host layout.
|
|
19
19
|
/// @return Host node ID for `msg.sender`.
|
|
20
20
|
function caller() internal view returns (uint) {
|
|
21
|
-
return
|
|
21
|
+
return Nodes.toHost(msg.sender);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/// @notice Make a low-level call to an address.
|
|
@@ -47,7 +47,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/// @notice Make a trusted call to another node in the network.
|
|
50
|
-
/// Looks up the node's contract address via `ensureTrusted` + `
|
|
50
|
+
/// Looks up the node's contract address via `ensureTrusted` + `Nodes.addr`,
|
|
51
51
|
/// then issues a low-level call forwarding `value` ETH and `data`.
|
|
52
52
|
/// @param node Node ID of the callee (must be in the authorized set).
|
|
53
53
|
/// @param value Native value to forward in wei.
|
|
@@ -55,19 +55,19 @@ abstract contract NodeCalls is AccessControl {
|
|
|
55
55
|
/// @return out Return data from the successful call.
|
|
56
56
|
function callTo(uint node, uint128 value, bytes memory data) internal returns (bytes memory out) {
|
|
57
57
|
ensureTrusted(node);
|
|
58
|
-
address addr =
|
|
58
|
+
address addr = Nodes.addr(node);
|
|
59
59
|
return callAddr(addr, value, data);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/// @notice Make a trusted query to another node in the network.
|
|
63
|
-
/// Looks up the node's contract address via `ensureTrusted` + `
|
|
63
|
+
/// Looks up the node's contract address via `ensureTrusted` + `Nodes.addr`,
|
|
64
64
|
/// then issues a low-level `staticcall` with `data`.
|
|
65
65
|
/// @param node Node ID of the callee (must be in the authorized set).
|
|
66
66
|
/// @param data Encoded calldata to send.
|
|
67
67
|
/// @return out Return data from the successful query.
|
|
68
68
|
function queryTo(uint node, bytes memory data) internal view returns (bytes memory out) {
|
|
69
69
|
ensureTrusted(node);
|
|
70
|
-
address addr =
|
|
70
|
+
address addr = Nodes.addr(node);
|
|
71
71
|
return queryAddr(addr, data);
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -77,7 +77,7 @@ abstract contract NodeCalls is AccessControl {
|
|
|
77
77
|
/// @param ctx Command execution context.
|
|
78
78
|
/// @return Decoded command output block stream.
|
|
79
79
|
function callCommand(uint id, uint128 value, CommandContext memory ctx) internal returns (bytes memory) {
|
|
80
|
-
bytes4 selector =
|
|
80
|
+
bytes4 selector = Nodes.commandSelector(id);
|
|
81
81
|
bytes memory data = abi.encodeWithSelector(selector, ctx);
|
|
82
82
|
return abi.decode(callTo(id, value, data), (bytes));
|
|
83
83
|
}
|
package/core/Host.sol
CHANGED
|
@@ -10,7 +10,7 @@ import {ExecutePayable} from "../commands/admin/Execute.sol";
|
|
|
10
10
|
import {Label} from "../commands/admin/Label.sol";
|
|
11
11
|
import {Revoke} from "../guards/Revoke.sol";
|
|
12
12
|
import {IntroductionEvent} from "../events/Introduction.sol";
|
|
13
|
-
import {
|
|
13
|
+
import {Nodes} from "../utils/Nodes.sol";
|
|
14
14
|
|
|
15
15
|
/// @title IHostIntroduction
|
|
16
16
|
/// @notice Interface implemented by hosts that accept introductions from other hosts.
|
|
@@ -51,7 +51,7 @@ abstract contract Host is
|
|
|
51
51
|
/// @param peer Host node ID being introduced.
|
|
52
52
|
/// @param blocknum Block number at which the host was deployed.
|
|
53
53
|
function introduce(uint peer, uint blocknum) external {
|
|
54
|
-
emit Introduction(
|
|
54
|
+
emit Introduction(Nodes.matchHost(peer, msg.sender), blocknum);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/// @notice Accept native ETH transfers (e.g. from command value flows).
|
package/core/Runtime.sol
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {Assets} from "../utils/Assets.sol";
|
|
5
|
-
import {
|
|
5
|
+
import {Nodes} from "../utils/Nodes.sol";
|
|
6
6
|
|
|
7
7
|
/// @title Runtime
|
|
8
8
|
/// @notice Shared runtime for host identity and native asset identity.
|
|
9
9
|
abstract contract Runtime {
|
|
10
|
-
/// @dev This contract's host node ID, set to `
|
|
11
|
-
uint public immutable host =
|
|
10
|
+
/// @dev This contract's host node ID, set to `Nodes.toHost(address(this))` at construction.
|
|
11
|
+
uint public immutable host = Nodes.toHost(address(this));
|
|
12
12
|
/// @dev Asset ID for the native chain coin/token, bound to the current chain at deployment.
|
|
13
13
|
bytes32 internal immutable nativeAsset = Assets.toNative();
|
|
14
14
|
}
|
package/core/Types.sol
CHANGED
|
@@ -5,8 +5,6 @@ pragma solidity ^0.8.33;
|
|
|
5
5
|
struct AssetAmount {
|
|
6
6
|
/// @dev Asset identifier.
|
|
7
7
|
bytes32 asset;
|
|
8
|
-
/// @dev Asset metadata slot.
|
|
9
|
-
bytes32 meta;
|
|
10
8
|
/// @dev Token amount in the asset's native units.
|
|
11
9
|
uint amount;
|
|
12
10
|
}
|
|
@@ -17,8 +15,6 @@ struct AccountAsset {
|
|
|
17
15
|
bytes32 account;
|
|
18
16
|
/// @dev Asset identifier.
|
|
19
17
|
bytes32 asset;
|
|
20
|
-
/// @dev Asset metadata slot.
|
|
21
|
-
bytes32 meta;
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
/// @notice Account-scoped amount shape for requests, responses, and reporting.
|
|
@@ -27,8 +23,6 @@ struct AccountAmount {
|
|
|
27
23
|
bytes32 account;
|
|
28
24
|
/// @dev Asset identifier.
|
|
29
25
|
bytes32 asset;
|
|
30
|
-
/// @dev Asset metadata slot.
|
|
31
|
-
bytes32 meta;
|
|
32
26
|
/// @dev Token amount in the asset's native units.
|
|
33
27
|
uint amount;
|
|
34
28
|
}
|
|
@@ -39,8 +33,6 @@ struct HostAmount {
|
|
|
39
33
|
uint host;
|
|
40
34
|
/// @dev Asset identifier.
|
|
41
35
|
bytes32 asset;
|
|
42
|
-
/// @dev Asset metadata slot.
|
|
43
|
-
bytes32 meta;
|
|
44
36
|
/// @dev Token amount in the asset's native units.
|
|
45
37
|
uint amount;
|
|
46
38
|
}
|
|
@@ -53,8 +45,6 @@ struct HostAccountAsset {
|
|
|
53
45
|
bytes32 account;
|
|
54
46
|
/// @dev Asset identifier.
|
|
55
47
|
bytes32 asset;
|
|
56
|
-
/// @dev Asset metadata slot.
|
|
57
|
-
bytes32 meta;
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
/// @notice Host-scoped account amount shape.
|
|
@@ -65,8 +55,6 @@ struct HostAccountAmount {
|
|
|
65
55
|
bytes32 account;
|
|
66
56
|
/// @dev Asset identifier.
|
|
67
57
|
bytes32 asset;
|
|
68
|
-
/// @dev Asset metadata slot.
|
|
69
|
-
bytes32 meta;
|
|
70
58
|
/// @dev Token amount in the asset's native units.
|
|
71
59
|
uint amount;
|
|
72
60
|
}
|
|
@@ -79,8 +67,6 @@ struct Tx {
|
|
|
79
67
|
bytes32 to;
|
|
80
68
|
/// @dev Asset identifier.
|
|
81
69
|
bytes32 asset;
|
|
82
|
-
/// @dev Asset metadata slot.
|
|
83
|
-
bytes32 meta;
|
|
84
70
|
/// @dev Transfer amount in the asset's native units.
|
|
85
71
|
uint amount;
|
|
86
72
|
}
|
package/docs/Schema.md
CHANGED
|
@@ -21,7 +21,7 @@ The block key is:
|
|
|
21
21
|
bytes4(keccak256("#name"))
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
For example, `#amount { bytes32 asset,
|
|
24
|
+
For example, `#amount { bytes32 asset, uint amount }` uses the key
|
|
25
25
|
derived from `#amount`. Blocks must not be overloaded: one block name should have
|
|
26
26
|
one protocol meaning.
|
|
27
27
|
|
|
@@ -30,7 +30,7 @@ one protocol meaning.
|
|
|
30
30
|
A block starts with `#`. Fixed fields are written in braces:
|
|
31
31
|
|
|
32
32
|
```txt
|
|
33
|
-
#amount { bytes32 asset,
|
|
33
|
+
#amount { bytes32 asset, uint amount }
|
|
34
34
|
#account { bytes32 account }
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -46,7 +46,7 @@ Empty braces are invalid. A zero-payload block must omit braces.
|
|
|
46
46
|
A schema is a comma-separated list of items. Order is significant.
|
|
47
47
|
|
|
48
48
|
```txt
|
|
49
|
-
#amount { bytes32 asset,
|
|
49
|
+
#amount { bytes32 asset, uint amount },
|
|
50
50
|
maybe #account { bytes32 account }
|
|
51
51
|
```
|
|
52
52
|
|
|
@@ -76,10 +76,10 @@ alias to give those bytes a presentation name:
|
|
|
76
76
|
Cardinality is expressed with prefix keywords:
|
|
77
77
|
|
|
78
78
|
```txt
|
|
79
|
-
#balance { bytes32 asset,
|
|
80
|
-
maybe #balance { bytes32 asset,
|
|
81
|
-
many #balance { bytes32 asset,
|
|
82
|
-
maybe many #balance { bytes32 asset,
|
|
79
|
+
#balance { bytes32 asset, uint amount }
|
|
80
|
+
maybe #balance { bytes32 asset, uint amount }
|
|
81
|
+
many #balance { bytes32 asset, uint amount }
|
|
82
|
+
maybe many #balance { bytes32 asset, uint amount }
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
- no prefix: one required item
|
|
@@ -190,6 +190,25 @@ types may pack these words differently, but a given chain type must use one
|
|
|
190
190
|
stable format everywhere. For EVM chains, the low 128 bits are native value /
|
|
191
191
|
endowment in wei; higher bits are reserved for execution resources such as gas.
|
|
192
192
|
|
|
193
|
+
## Protocol IDs
|
|
194
|
+
|
|
195
|
+
Account, asset, and node ID fields use one 32-byte convention:
|
|
196
|
+
|
|
197
|
+
- first byte `0x00`: opaque ID, encoded as `0x00 || bytes31(hash)`. The full
|
|
198
|
+
preimage must come from a lookup table or witness data when native metadata is
|
|
199
|
+
needed.
|
|
200
|
+
- first byte nonzero: structured ID. The value may be deconstructed according
|
|
201
|
+
to its chain/runtime layout.
|
|
202
|
+
|
|
203
|
+
Opaque preimages must start with a one-byte format/hash tag; `0x01` means
|
|
204
|
+
keccak256. The remaining bytes are host/domain-specific until the protocol
|
|
205
|
+
standardizes a fuller preimage payload format.
|
|
206
|
+
|
|
207
|
+
The field name supplies the protocol role for opaque IDs. For example, a
|
|
208
|
+
`bytes32 asset` whose first byte is zero is still an asset in that block; it
|
|
209
|
+
just cannot be decoded without external context. Runtime helpers that inspect
|
|
210
|
+
the layout of an ID only apply to structured IDs.
|
|
211
|
+
|
|
193
212
|
## Identifiers
|
|
194
213
|
|
|
195
214
|
Block names use lower camelCase ASCII identifiers. Field names and aliases use
|
|
@@ -246,9 +265,9 @@ expands to:
|
|
|
246
265
|
Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
247
266
|
|
|
248
267
|
```txt
|
|
249
|
-
#amount { bytes32 asset,
|
|
250
|
-
#balance { bytes32 asset,
|
|
251
|
-
#custody { uint host, bytes32 asset,
|
|
268
|
+
#amount { bytes32 asset, uint amount }
|
|
269
|
+
#balance { bytes32 asset, uint amount }
|
|
270
|
+
#custody { uint host, bytes32 asset, uint amount }
|
|
252
271
|
#call { uint target, uint resources, #bytes as payload }
|
|
253
272
|
#step { uint target, uint resources, #bytes as request }
|
|
254
273
|
#context { bytes32 account, #bytes as state, #bytes as request }
|
package/events/Asset.sol
CHANGED
|
@@ -3,15 +3,29 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import { EventEmitter } from "./Emitter.sol";
|
|
5
5
|
|
|
6
|
+
/// @notice Emitted when a host declares the preimage for an opaque asset ID.
|
|
7
|
+
abstract contract AssetEvent is EventEmitter {
|
|
8
|
+
string private constant ABI = "event Asset(uint indexed host, bytes32 asset, bytes preimage)";
|
|
9
|
+
|
|
10
|
+
/// @param host Host node ID that manages this asset declaration.
|
|
11
|
+
/// @param asset Asset identifier, typically `0x00 || bytes31(hash(preimage))`.
|
|
12
|
+
/// @param preimage Canonical preimage used to derive or resolve the opaque asset ID.
|
|
13
|
+
/// The first byte is a format/hash tag; `0x01` means keccak256.
|
|
14
|
+
event Asset(uint indexed host, bytes32 asset, bytes preimage);
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
emit EventAbi(ABI);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
/// @notice Emitted when an asset support status is updated on a host.
|
|
7
22
|
abstract contract AssetStatusEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event AssetStatus(uint indexed host, bytes32 asset,
|
|
23
|
+
string private constant ABI = "event AssetStatus(uint indexed host, bytes32 asset, uint status)";
|
|
9
24
|
|
|
10
25
|
/// @param host Host node ID that manages this listing.
|
|
11
26
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
27
|
/// @param status Asset support status. Zero means unsupported; nonzero means supported.
|
|
14
|
-
event AssetStatus(uint indexed host, bytes32 asset,
|
|
28
|
+
event AssetStatus(uint indexed host, bytes32 asset, uint status);
|
|
15
29
|
|
|
16
30
|
constructor() {
|
|
17
31
|
emit EventAbi(ABI);
|
package/events/Balance.sol
CHANGED
|
@@ -5,15 +5,14 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an account balance changes.
|
|
7
7
|
abstract contract BalanceEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Balance(bytes32 indexed account, bytes32 asset,
|
|
8
|
+
string private constant ABI = "event Balance(bytes32 indexed account, bytes32 asset, uint balance, int change, uint access)";
|
|
9
9
|
|
|
10
10
|
/// @param account Account identifier whose balance changed.
|
|
11
11
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
12
|
/// @param balance New balance after the change.
|
|
14
13
|
/// @param change Signed delta applied to the balance (positive = credit, negative = debit).
|
|
15
14
|
/// @param access Command ID or context identifier associated with this change.
|
|
16
|
-
event Balance(bytes32 indexed account, bytes32 asset,
|
|
15
|
+
event Balance(bytes32 indexed account, bytes32 asset, uint balance, int change, uint access);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/events/Locked.sol
CHANGED
|
@@ -5,15 +5,14 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an account locks an asset in a protocol operation.
|
|
7
7
|
abstract contract LockedEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Locked(bytes32 indexed account, bytes32 asset,
|
|
8
|
+
string private constant ABI = "event Locked(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context)";
|
|
9
9
|
|
|
10
10
|
/// @param account Account identifier that locked the asset.
|
|
11
11
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
12
|
/// @param amount Amount locked.
|
|
14
13
|
/// @param action Primary operation hint from `Actions`.
|
|
15
14
|
/// @param context Reserved context value for future use.
|
|
16
|
-
event Locked(bytes32 indexed account, bytes32 asset,
|
|
15
|
+
event Locked(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/events/Position.sol
CHANGED
|
@@ -6,16 +6,15 @@ import {EventEmitter} from "./Emitter.sol";
|
|
|
6
6
|
/// @notice Emitted when the reported value of an asset-backed position changes or is observed.
|
|
7
7
|
/// A value of 0 should be interpreted as a closed position.
|
|
8
8
|
abstract contract PositionEvent is EventEmitter {
|
|
9
|
-
string private constant ABI = "event Position(bytes32 indexed account, bytes32 asset,
|
|
9
|
+
string private constant ABI = "event Position(bytes32 indexed account, bytes32 asset, uint value, uint32 action, uint context, uint queryId)";
|
|
10
10
|
|
|
11
11
|
/// @param account Account identifier that owns or is associated with the position.
|
|
12
12
|
/// @param asset Asset identifier for the asset class.
|
|
13
|
-
/// @param meta Asset metadata slot carrying the position context.
|
|
14
13
|
/// @param value Context-specific position value; 0 indicates a closed position.
|
|
15
14
|
/// @param action Primary operation hint from `Actions`.
|
|
16
15
|
/// @param context Reserved context value for future use.
|
|
17
16
|
/// @param queryId Query ID associated with the position lookup or reporting context.
|
|
18
|
-
event Position(bytes32 indexed account, bytes32 asset,
|
|
17
|
+
event Position(bytes32 indexed account, bytes32 asset, uint value, uint32 action, uint context, uint queryId);
|
|
19
18
|
|
|
20
19
|
constructor() {
|
|
21
20
|
emit EventAbi(ABI);
|
package/events/Received.sol
CHANGED
|
@@ -5,15 +5,14 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an account receives an asset in a protocol operation.
|
|
7
7
|
abstract contract ReceivedEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Received(bytes32 indexed account, bytes32 asset,
|
|
8
|
+
string private constant ABI = "event Received(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context)";
|
|
9
9
|
|
|
10
10
|
/// @param account Account identifier that received the asset.
|
|
11
11
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
12
|
/// @param amount Amount received.
|
|
14
13
|
/// @param action Primary operation hint from `Actions`.
|
|
15
14
|
/// @param context Reserved context value for future use.
|
|
16
|
-
event Received(bytes32 indexed account, bytes32 asset,
|
|
15
|
+
event Received(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/events/Spent.sol
CHANGED
|
@@ -5,15 +5,14 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an account spends an asset in a protocol operation.
|
|
7
7
|
abstract contract SpentEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Spent(bytes32 indexed account, bytes32 asset,
|
|
8
|
+
string private constant ABI = "event Spent(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context)";
|
|
9
9
|
|
|
10
10
|
/// @param account Account identifier that spent the asset.
|
|
11
11
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
12
|
/// @param amount Amount spent.
|
|
14
13
|
/// @param action Primary operation hint from `Actions`.
|
|
15
14
|
/// @param context Reserved context value for future use.
|
|
16
|
-
event Spent(bytes32 indexed account, bytes32 asset,
|
|
15
|
+
event Spent(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/events/Unlocked.sol
CHANGED
|
@@ -5,15 +5,14 @@ import { EventEmitter } from "./Emitter.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @notice Emitted when an account unlocks an asset in a protocol operation.
|
|
7
7
|
abstract contract UnlockedEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Unlocked(bytes32 indexed account, bytes32 asset,
|
|
8
|
+
string private constant ABI = "event Unlocked(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context)";
|
|
9
9
|
|
|
10
10
|
/// @param account Account identifier that unlocked the asset.
|
|
11
11
|
/// @param asset Asset identifier.
|
|
12
|
-
/// @param meta Asset metadata slot.
|
|
13
12
|
/// @param amount Amount unlocked.
|
|
14
13
|
/// @param action Primary operation hint from `Actions`.
|
|
15
14
|
/// @param context Reserved context value for future use.
|
|
16
|
-
event Unlocked(bytes32 indexed account, bytes32 asset,
|
|
15
|
+
event Unlocked(bytes32 indexed account, bytes32 asset, uint amount, uint32 action, uint context);
|
|
17
16
|
|
|
18
17
|
constructor() {
|
|
19
18
|
emit EventAbi(ABI);
|
package/guards/Base.sol
CHANGED
|
@@ -4,16 +4,16 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
import {AccessControl} from "../core/Access.sol";
|
|
5
5
|
import {GuardEvent} from "../events/Guard.sol";
|
|
6
6
|
import {LabeledEvent} from "../events/Labeled.sol";
|
|
7
|
-
import {
|
|
7
|
+
import {Nodes} from "../utils/Nodes.sol";
|
|
8
8
|
|
|
9
9
|
/// @notice ABI-encode a guard action call from a target guard ID and request block stream.
|
|
10
|
-
/// @dev Derives the function selector from `target` via `
|
|
10
|
+
/// @dev Derives the function selector from `target` via `Nodes.guardSelector(target)`.
|
|
11
11
|
/// Reverts if `target` is not a valid guard ID.
|
|
12
12
|
/// @param target Destination guard action node ID embedding the target selector.
|
|
13
13
|
/// @param request Input block stream for the guard invocation.
|
|
14
14
|
/// @return ABI-encoded calldata for the guard action entry point.
|
|
15
15
|
function encodeGuardCall(uint target, bytes calldata request) pure returns (bytes memory) {
|
|
16
|
-
bytes4 selector =
|
|
16
|
+
bytes4 selector = Nodes.guardSelector(target);
|
|
17
17
|
return abi.encodeWithSelector(selector, request);
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -31,6 +31,6 @@ abstract contract GuardBase is AccessControl, GuardEvent, LabeledEvent {
|
|
|
31
31
|
/// @param selector Guard action entrypoint selector.
|
|
32
32
|
/// @return Guard action node ID.
|
|
33
33
|
function guardId(bytes4 selector) internal view returns (uint) {
|
|
34
|
-
return
|
|
34
|
+
return Nodes.toGuard(selector, address(this));
|
|
35
35
|
}
|
|
36
36
|
}
|
package/package.json
CHANGED
package/peer/AllowAssets.sol
CHANGED
|
@@ -12,7 +12,7 @@ interface IPeerAllowAssets {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/// @title PeerAllowAssets
|
|
15
|
-
/// @notice Peer that permits a list of
|
|
15
|
+
/// @notice Peer that permits a list of assets on behalf of a peer host.
|
|
16
16
|
/// Each ASSET block in the request calls `allowAsset`. Restricted to trusted peers.
|
|
17
17
|
abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook, IPeerAllowAssets {
|
|
18
18
|
uint internal immutable peerAllowAssetsId = peerId(this.peerAllowAssets.selector);
|
|
@@ -29,8 +29,8 @@ abstract contract PeerAllowAssets is PeerBase, AllowAssetsHook, IPeerAllowAssets
|
|
|
29
29
|
(Cur memory assets, , ) = Cursors.init(request, 1);
|
|
30
30
|
|
|
31
31
|
while (assets.i < assets.len) {
|
|
32
|
-
|
|
33
|
-
allowAsset(asset
|
|
32
|
+
bytes32 asset = assets.unpackAsset();
|
|
33
|
+
allowAsset(asset);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
assets.complete();
|
package/peer/Allowance.sol
CHANGED
|
@@ -31,8 +31,8 @@ abstract contract PeerAllowance is PeerBase, AllowanceHook, IPeerAllowance {
|
|
|
31
31
|
uint peer = caller();
|
|
32
32
|
|
|
33
33
|
while (amounts.i < amounts.len) {
|
|
34
|
-
(bytes32 asset,
|
|
35
|
-
allowance(peer, asset,
|
|
34
|
+
(bytes32 asset, uint amount) = amounts.unpackAmount();
|
|
35
|
+
allowance(peer, asset, amount);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
amounts.complete();
|
package/peer/Base.sol
CHANGED
|
@@ -4,16 +4,16 @@ pragma solidity ^0.8.33;
|
|
|
4
4
|
import { NodeCalls } from "../core/Calls.sol";
|
|
5
5
|
import { PeerEvent } from "../events/Peer.sol";
|
|
6
6
|
import { LabeledEvent } from "../events/Labeled.sol";
|
|
7
|
-
import {
|
|
7
|
+
import { Nodes } from "../utils/Nodes.sol";
|
|
8
8
|
|
|
9
9
|
/// @notice ABI-encode a peer call from a target peer ID and request block stream.
|
|
10
|
-
/// @dev Derives the function selector from `target` via `
|
|
10
|
+
/// @dev Derives the function selector from `target` via `Nodes.peerSelector(target)`.
|
|
11
11
|
/// Reverts if `target` is not a valid peer ID.
|
|
12
12
|
/// @param target Destination peer node ID embedding the target selector.
|
|
13
13
|
/// @param request Input block stream for the peer invocation.
|
|
14
14
|
/// @return ABI-encoded calldata for the peer entry point.
|
|
15
15
|
function encodePeerCall(uint target, bytes calldata request) pure returns (bytes memory) {
|
|
16
|
-
bytes4 selector =
|
|
16
|
+
bytes4 selector = Nodes.peerSelector(target);
|
|
17
17
|
return abi.encodeWithSelector(selector, request);
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -36,6 +36,6 @@ abstract contract PeerBase is NodeCalls, PeerEvent, LabeledEvent {
|
|
|
36
36
|
/// @param selector Peer entrypoint selector.
|
|
37
37
|
/// @return Peer node ID.
|
|
38
38
|
function peerId(bytes4 selector) internal view returns (uint) {
|
|
39
|
-
return
|
|
39
|
+
return Nodes.toPeer(selector, address(this));
|
|
40
40
|
}
|
|
41
41
|
}
|
package/peer/Credit.sol
CHANGED
|
@@ -7,30 +7,30 @@ import { Cursors, Cur, Forms } from "../Cursors.sol";
|
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
10
|
-
interface
|
|
11
|
-
function
|
|
10
|
+
interface IPeerCreditAccount {
|
|
11
|
+
function peerCreditAccount(bytes calldata request) external returns (bytes memory);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/// @title
|
|
14
|
+
/// @title PeerCreditAccount
|
|
15
15
|
/// @notice Peer that lets a trusted peer credit supplied accounts directly.
|
|
16
16
|
/// Each ACCOUNT_AMOUNT block calls `creditAccount` for its account.
|
|
17
|
-
abstract contract
|
|
18
|
-
uint internal immutable
|
|
17
|
+
abstract contract PeerCreditAccount is PeerBase, CreditAccountHook, IPeerCreditAccount {
|
|
18
|
+
uint internal immutable peerCreditAccountId = peerId(this.peerCreditAccount.selector);
|
|
19
19
|
|
|
20
20
|
constructor() {
|
|
21
|
-
emit Peer(host,
|
|
22
|
-
emit Labeled(
|
|
21
|
+
emit Peer(host, peerCreditAccountId, "1:0", Forms.AccountAmount, "", false);
|
|
22
|
+
emit Labeled(peerCreditAccountId, bytes32(0), "peerCreditAccount");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/// @notice Execute the peer-credit call.
|
|
26
26
|
/// @param request ACCOUNT_AMOUNT block stream supplied by the trusted peer.
|
|
27
27
|
/// @return Empty response bytes.
|
|
28
|
-
function
|
|
28
|
+
function peerCreditAccount(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
29
29
|
(Cur memory amounts, , ) = Cursors.init(request, 1);
|
|
30
30
|
|
|
31
31
|
while (amounts.i < amounts.len) {
|
|
32
|
-
(bytes32 account, bytes32 asset,
|
|
33
|
-
creditAccount(account, asset,
|
|
32
|
+
(bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
|
|
33
|
+
creditAccount(account, asset, amount);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
amounts.complete();
|
package/peer/Debit.sol
CHANGED
|
@@ -7,30 +7,30 @@ import { Cursors, Cur, Forms } from "../Cursors.sol";
|
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
9
|
|
|
10
|
-
interface
|
|
11
|
-
function
|
|
10
|
+
interface IPeerDebitAccount {
|
|
11
|
+
function peerDebitAccount(bytes calldata request) external returns (bytes memory);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/// @title
|
|
14
|
+
/// @title PeerDebitAccount
|
|
15
15
|
/// @notice Peer that lets a trusted peer debit supplied accounts directly.
|
|
16
16
|
/// Each ACCOUNT_AMOUNT block calls `debitAccount` for its account.
|
|
17
|
-
abstract contract
|
|
18
|
-
uint internal immutable
|
|
17
|
+
abstract contract PeerDebitAccount is PeerBase, DebitAccountHook, IPeerDebitAccount {
|
|
18
|
+
uint internal immutable peerDebitAccountId = peerId(this.peerDebitAccount.selector);
|
|
19
19
|
|
|
20
20
|
constructor() {
|
|
21
|
-
emit Peer(host,
|
|
22
|
-
emit Labeled(
|
|
21
|
+
emit Peer(host, peerDebitAccountId, "1:0", Forms.AccountAmount, "", false);
|
|
22
|
+
emit Labeled(peerDebitAccountId, bytes32(0), "peerDebitAccount");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/// @notice Execute the peer-debit call.
|
|
26
26
|
/// @param request ACCOUNT_AMOUNT block stream supplied by the trusted peer.
|
|
27
27
|
/// @return Empty response bytes.
|
|
28
|
-
function
|
|
28
|
+
function peerDebitAccount(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
29
29
|
(Cur memory amounts, , ) = Cursors.init(request, 1);
|
|
30
30
|
|
|
31
31
|
while (amounts.i < amounts.len) {
|
|
32
|
-
(bytes32 account, bytes32 asset,
|
|
33
|
-
debitAccount(account, asset,
|
|
32
|
+
(bytes32 account, bytes32 asset, uint amount) = amounts.unpackAccountAmount();
|
|
33
|
+
debitAccount(account, asset, amount);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
amounts.complete();
|
package/peer/DenyAssets.sol
CHANGED
|
@@ -12,7 +12,7 @@ interface IPeerDenyAssets {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/// @title PeerDenyAssets
|
|
15
|
-
/// @notice Peer that blocks a list of
|
|
15
|
+
/// @notice Peer that blocks a list of assets on behalf of a peer host.
|
|
16
16
|
/// Each ASSET block in the request calls `denyAsset`. Restricted to trusted peers.
|
|
17
17
|
abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook, IPeerDenyAssets {
|
|
18
18
|
uint internal immutable peerDenyAssetsId = peerId(this.peerDenyAssets.selector);
|
|
@@ -29,8 +29,8 @@ abstract contract PeerDenyAssets is PeerBase, DenyAssetsHook, IPeerDenyAssets {
|
|
|
29
29
|
(Cur memory assets, , ) = Cursors.init(request, 1);
|
|
30
30
|
|
|
31
31
|
while (assets.i < assets.len) {
|
|
32
|
-
|
|
33
|
-
denyAsset(asset
|
|
32
|
+
bytes32 asset = assets.unpackAsset();
|
|
33
|
+
denyAsset(asset);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
assets.complete();
|
package/peer/Recover.sol
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
using Cursors for Cur;
|
|
8
|
+
|
|
9
|
+
interface IPeerRecover {
|
|
10
|
+
function peerRecover(bytes calldata request) external returns (bytes memory);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
abstract contract RecoverHook {
|
|
14
|
+
/// @notice Override to recover one incoming pipe context from a peer host.
|
|
15
|
+
/// @dev Implementations may refund, retry, compensate, reconcile, or otherwise
|
|
16
|
+
/// handle the supplied state and remaining steps.
|
|
17
|
+
/// @param account Account identifier for the recovery context.
|
|
18
|
+
/// @param state Embedded recovery state block stream.
|
|
19
|
+
/// @param steps Embedded recovery step block stream.
|
|
20
|
+
function recover(bytes32 account, bytes calldata state, bytes calldata steps) internal virtual;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @title PeerRecover
|
|
24
|
+
/// @notice Peer endpoint for recovering an interrupted or failed pipe context.
|
|
25
|
+
/// @dev Commonly used when a cross-chain pipe fails at its destination, but the
|
|
26
|
+
/// hook is host-defined and may implement any recovery strategy. Each PIPE
|
|
27
|
+
/// block carries chain resources plus a CONTEXT block; the nested context
|
|
28
|
+
/// is passed to the recovery hook and resources are not allocated.
|
|
29
|
+
abstract contract PeerRecover is PeerBase, RecoverHook, IPeerRecover {
|
|
30
|
+
uint internal immutable peerRecoverId = peerId(this.peerRecover.selector);
|
|
31
|
+
|
|
32
|
+
constructor() {
|
|
33
|
+
emit Peer(host, peerRecoverId, "1:0", Schemas.Pipe, "", false);
|
|
34
|
+
emit Labeled(peerRecoverId, bytes32(0), "peerRecover");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// @notice Forward peer-supplied recovery pipes to the host-defined recovery hook.
|
|
38
|
+
/// @param request PIPE block stream supplied by the trusted peer.
|
|
39
|
+
/// @return Empty response bytes.
|
|
40
|
+
function peerRecover(bytes calldata request) external onlyPeer returns (bytes memory) {
|
|
41
|
+
(Cur memory input, , ) = Cursors.init(request, 1);
|
|
42
|
+
|
|
43
|
+
while (input.i < input.len) {
|
|
44
|
+
(, bytes32 account, bytes calldata state, bytes calldata steps) = input.unpackPipe();
|
|
45
|
+
recover(account, state, steps);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
input.complete();
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|