@rootzero/contracts 1.22.0 → 1.23.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 +68 -0
- package/Endpoints.sol +2 -3
- package/Events.sol +1 -1
- package/README.md +13 -11
- package/annotations/Action.sol +1 -1
- package/annotations/Label.sol +1 -1
- package/annotations/Schema.sol +1 -1
- package/codec/Blocks.sol +39 -30
- package/commands/Allocate.sol +5 -8
- package/commands/Base.sol +29 -13
- package/commands/Burn.sol +5 -7
- package/commands/Credit.sol +5 -7
- package/commands/Debit.sol +5 -7
- package/commands/Deposit.sol +10 -14
- package/commands/Payout.sol +5 -8
- package/commands/Provision.sol +10 -14
- package/commands/Recover.sol +4 -6
- package/commands/Relay.sol +14 -21
- package/commands/Repay.sol +10 -14
- package/commands/Settle.sol +10 -14
- package/commands/Withdraw.sol +5 -7
- package/commands/admin/AllowAssets.sol +5 -7
- package/commands/admin/Allowance.sol +5 -7
- package/commands/admin/Annotate.sol +5 -7
- package/commands/admin/Appoint.sol +5 -7
- package/commands/admin/Authorize.sol +5 -7
- package/commands/admin/Base.sol +12 -3
- package/commands/admin/DenyAssets.sol +5 -7
- package/commands/admin/Dismiss.sol +5 -7
- package/commands/admin/Execute.sol +5 -7
- package/commands/admin/Unauthorize.sol +5 -7
- package/core/Calls.sol +72 -3
- package/core/Portal.sol +2 -2
- package/events/Positioned.sol +22 -0
- package/execution/Execution.sol +42 -0
- package/package.json +1 -1
- package/ports/Allowance.sol +22 -12
- package/ports/Assets.sol +99 -0
- package/utils/Cursors.sol +3 -3
- package/utils/Nodes.sol +1 -1
- package/utils/Utils.sol +31 -31
- package/events/Commander.sol +0 -19
- package/ports/AllowAssets.sol +0 -34
- package/ports/DenyAssets.sol +0 -34
|
@@ -22,21 +22,19 @@ abstract contract Unauthorize is AdminBase {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/// @notice Unauthorize each NODE block in the admin input.
|
|
25
|
-
/// @param
|
|
25
|
+
/// @param context Admin command context carrying the NODE input stream.
|
|
26
26
|
/// @return Empty output state.
|
|
27
27
|
/// @return Empty transaction stream.
|
|
28
28
|
function unauthorize(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
) external onlyAdmin(account) returns (bytes memory, bytes memory) {
|
|
33
|
-
Execution memory exec = openCommand(state, input, descriptor, 0);
|
|
29
|
+
bytes calldata context
|
|
30
|
+
) external returns (bytes memory, bytes memory) {
|
|
31
|
+
Execution memory exec = openAdminCommand(context, descriptor, 0);
|
|
34
32
|
|
|
35
33
|
while (exec.more()) {
|
|
36
34
|
uint node = exec.unpackNode(Lanes.Input);
|
|
37
35
|
setNode(node, false);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
return
|
|
38
|
+
return closeCommand(exec);
|
|
41
39
|
}
|
|
42
40
|
}
|
package/core/Calls.sol
CHANGED
|
@@ -3,6 +3,9 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {TrustAccess} from "./Access.sol";
|
|
5
5
|
import {Nodes} from "../utils/Nodes.sol";
|
|
6
|
+
import {Keys} from "../codec/Keys.sol";
|
|
7
|
+
import {Sizes} from "../codec/Specs.sol";
|
|
8
|
+
import {max32} from "../utils/Utils.sol";
|
|
6
9
|
|
|
7
10
|
/// @dev Emitted when a trusted inter-node call fails.
|
|
8
11
|
/// @param addr Contract address that was called.
|
|
@@ -81,6 +84,50 @@ abstract contract NodeCalls is RawNodeCalls, TrustAccess {
|
|
|
81
84
|
/// @title CommandCalls
|
|
82
85
|
/// @notice Trusted command-call helpers for contracts that route command nodes.
|
|
83
86
|
abstract contract CommandCalls is NodeCalls {
|
|
87
|
+
/// @dev Build `command(bytes)` calldata and its nested CONTEXT block in one allocation.
|
|
88
|
+
/// Threaded state is copied from memory and step input directly from calldata.
|
|
89
|
+
function encodeCommandCall(
|
|
90
|
+
bytes4 selector,
|
|
91
|
+
bytes32 account,
|
|
92
|
+
bytes memory state,
|
|
93
|
+
bytes calldata input
|
|
94
|
+
) internal pure returns (bytes memory data) {
|
|
95
|
+
uint contextLen = max32(Sizes.B32 + 2 * Sizes.Header + state.length + input.length);
|
|
96
|
+
uint paddedContextLen = (contextLen + 31) & ~uint(31);
|
|
97
|
+
uint dataLen = 4 + 64 + paddedContextLen;
|
|
98
|
+
|
|
99
|
+
// Reserve one scratch word because the final eight-byte block header is
|
|
100
|
+
// written with mstore. Exclude that word from the returned calldata.
|
|
101
|
+
data = new bytes(dataLen + 32);
|
|
102
|
+
|
|
103
|
+
uint contextKey = uint32(Keys.Context);
|
|
104
|
+
uint bytesKey = uint32(Keys.Bytes);
|
|
105
|
+
assembly ("memory-safe") {
|
|
106
|
+
mstore(data, dataLen)
|
|
107
|
+
let out := add(data, 0x20)
|
|
108
|
+
|
|
109
|
+
// ABI envelope for command(bytes).
|
|
110
|
+
mstore(out, selector)
|
|
111
|
+
mstore(add(out, 0x04), 0x20)
|
|
112
|
+
mstore(add(out, 0x24), contextLen)
|
|
113
|
+
|
|
114
|
+
// CONTEXT(account, BYTES(state), BYTES(input)).
|
|
115
|
+
let context := add(out, 0x44)
|
|
116
|
+
mstore(context, or(shl(224, contextKey), shl(192, sub(contextLen, 8))))
|
|
117
|
+
mstore(add(context, 0x08), account)
|
|
118
|
+
|
|
119
|
+
let stateBlock := add(context, 0x28)
|
|
120
|
+
let stateLen := mload(state)
|
|
121
|
+
mstore(stateBlock, or(shl(224, bytesKey), shl(192, stateLen)))
|
|
122
|
+
mcopy(add(stateBlock, 0x08), add(state, 0x20), stateLen)
|
|
123
|
+
|
|
124
|
+
let inputBlock := add(add(stateBlock, 0x08), stateLen)
|
|
125
|
+
let inputLen := input.length
|
|
126
|
+
mstore(inputBlock, or(shl(224, bytesKey), shl(192, inputLen)))
|
|
127
|
+
calldatacopy(add(inputBlock, 0x08), input.offset, inputLen)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
84
131
|
/// @notice Encode and call a trusted command node.
|
|
85
132
|
/// @param command Command node ID embedding the target selector.
|
|
86
133
|
/// @param value Native value to forward in wei.
|
|
@@ -97,7 +144,7 @@ abstract contract CommandCalls is NodeCalls {
|
|
|
97
144
|
bytes calldata input
|
|
98
145
|
) internal returns (bytes memory nextState, bytes memory transactions) {
|
|
99
146
|
bytes4 selector = Nodes.commandSelector(command);
|
|
100
|
-
bytes memory data =
|
|
147
|
+
bytes memory data = encodeCommandCall(selector, account, state, input);
|
|
101
148
|
return abi.decode(trustedCall(command, value, data), (bytes, bytes));
|
|
102
149
|
}
|
|
103
150
|
}
|
|
@@ -110,7 +157,18 @@ abstract contract PortCalls is NodeCalls {
|
|
|
110
157
|
/// @param value Native value to forward in wei.
|
|
111
158
|
/// @param input Port input block stream.
|
|
112
159
|
/// @return success True if the low-level port call succeeded.
|
|
113
|
-
function tryCallPort(uint port, uint128 value, bytes
|
|
160
|
+
function tryCallPort(uint port, uint128 value, bytes memory input) internal returns (bool success) {
|
|
161
|
+
bytes4 selector = Nodes.portSelector(port);
|
|
162
|
+
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
163
|
+
return tryTrustedCall(port, value, data);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// @notice Try to encode and call a trusted port node by copying input from calldata.
|
|
167
|
+
/// @param port Port node ID embedding the target selector.
|
|
168
|
+
/// @param value Native value to forward in wei.
|
|
169
|
+
/// @param input Port input block stream.
|
|
170
|
+
/// @return success True if the low-level port call succeeded.
|
|
171
|
+
function tryCallPortCopy(uint port, uint128 value, bytes calldata input) internal returns (bool success) {
|
|
114
172
|
bytes4 selector = Nodes.portSelector(port);
|
|
115
173
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
116
174
|
return tryTrustedCall(port, value, data);
|
|
@@ -121,7 +179,18 @@ abstract contract PortCalls is NodeCalls {
|
|
|
121
179
|
/// @param value Native value to forward in wei.
|
|
122
180
|
/// @param input Port input block stream.
|
|
123
181
|
/// @return Decoded port output block stream.
|
|
124
|
-
function callPort(uint port, uint128 value, bytes
|
|
182
|
+
function callPort(uint port, uint128 value, bytes memory input) internal returns (bytes memory) {
|
|
183
|
+
bytes4 selector = Nodes.portSelector(port);
|
|
184
|
+
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
185
|
+
return abi.decode(trustedCall(port, value, data), (bytes));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// @notice Encode and call a trusted port node by copying input from calldata.
|
|
189
|
+
/// @param port Port node ID embedding the target selector.
|
|
190
|
+
/// @param value Native value to forward in wei.
|
|
191
|
+
/// @param input Port input block stream.
|
|
192
|
+
/// @return Decoded port output block stream.
|
|
193
|
+
function callPortCopy(uint port, uint128 value, bytes calldata input) internal returns (bytes memory) {
|
|
125
194
|
bytes4 selector = Nodes.portSelector(port);
|
|
126
195
|
bytes memory data = abi.encodeWithSelector(selector, input);
|
|
127
196
|
return abi.decode(trustedCall(port, value, data), (bytes));
|
package/core/Portal.sol
CHANGED
|
@@ -21,7 +21,7 @@ abstract contract Portal is PortCalls, NodeAccess, UnresolvedEvent, ResolvedEven
|
|
|
21
21
|
/// @param value Native EVM value assigned to the forwarding attempt.
|
|
22
22
|
/// @return miss Message digest recorded for recovery when forwarding fails; zero on success.
|
|
23
23
|
function forward(uint port, bytes32 key, bytes calldata message, uint128 value) internal returns (bytes32 miss) {
|
|
24
|
-
if (
|
|
24
|
+
if (tryCallPortCopy(port, value, message)) return bytes32(0);
|
|
25
25
|
|
|
26
26
|
miss = keccak256(message);
|
|
27
27
|
unresolved[key] = miss;
|
|
@@ -37,6 +37,6 @@ abstract contract Portal is PortCalls, NodeAccess, UnresolvedEvent, ResolvedEven
|
|
|
37
37
|
if (unresolved[key] != keccak256(witness)) revert BadWitness();
|
|
38
38
|
|
|
39
39
|
delete unresolved[key];
|
|
40
|
-
|
|
40
|
+
callPortCopy(port, value, witness);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice Emitted when an action produces an account position.
|
|
7
|
+
abstract contract PositionedEvent is EventEmitter {
|
|
8
|
+
string private constant ABI =
|
|
9
|
+
"event Positioned(bytes32 indexed account, bytes32 asset, uint amount, bytes32 liability, uint debt, uint32 action)";
|
|
10
|
+
|
|
11
|
+
/// @param account Account associated with the position.
|
|
12
|
+
/// @param asset Identifier for the asset side.
|
|
13
|
+
/// @param amount Quantity on the asset side.
|
|
14
|
+
/// @param liability Identifier for the liability side.
|
|
15
|
+
/// @param debt Quantity owed on the liability side.
|
|
16
|
+
/// @param action Primary operation hint from `Actions` that produced the position.
|
|
17
|
+
event Positioned(bytes32 indexed account, bytes32 asset, uint amount, bytes32 liability, uint debt, uint32 action);
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
emit EventAbi(ABI);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/execution/Execution.sol
CHANGED
|
@@ -15,6 +15,7 @@ import {max16} from "../utils/Utils.sol";
|
|
|
15
15
|
/// @dev `decoders` contains tagged input and state cursor lanes. Cursor operations
|
|
16
16
|
/// select one logical lane by swapping it into the lower 128 bits.
|
|
17
17
|
struct Execution {
|
|
18
|
+
bytes32 account;
|
|
18
19
|
uint budget;
|
|
19
20
|
uint decoders;
|
|
20
21
|
uint writers;
|
|
@@ -195,6 +196,33 @@ library Executions {
|
|
|
195
196
|
return exec.decoders.select(lane).absolute();
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
/// @dev Return the complete validated calldata source for a decoder lane.
|
|
200
|
+
/// @dev Does not consume or depend on the lane's current position. A lane
|
|
201
|
+
/// absent because its descriptor is EMPTY returns an empty calldata slice.
|
|
202
|
+
/// @param exec Execution whose input or state source is requested.
|
|
203
|
+
/// @param lane Input or state decoder lane.
|
|
204
|
+
/// @return data Complete calldata region represented by the lane.
|
|
205
|
+
function raw(Execution memory exec, uint8 lane) private pure returns (bytes calldata data) {
|
|
206
|
+
if (!exec.decoders.contains(lane)) return msg.data[0:0];
|
|
207
|
+
|
|
208
|
+
uint cur = exec.decoders.select(lane);
|
|
209
|
+
(, uint offset, uint len) = cur.decode();
|
|
210
|
+
if (len > msg.data.length || offset > msg.data.length - len) revert Blocks.MalformedBlocks();
|
|
211
|
+
return msg.data[offset:offset + len];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// @notice Return the complete validated command state without consuming it.
|
|
215
|
+
/// @dev Remains the complete original state after the state cursor advances.
|
|
216
|
+
function rawState(Execution memory exec) internal pure returns (bytes calldata) {
|
|
217
|
+
return raw(exec, Lanes.State);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// @notice Return the complete validated endpoint input without consuming it.
|
|
221
|
+
/// @dev Remains the complete original input after the input cursor advances.
|
|
222
|
+
function rawInput(Execution memory exec) internal pure returns (bytes calldata) {
|
|
223
|
+
return raw(exec, Lanes.Input);
|
|
224
|
+
}
|
|
225
|
+
|
|
198
226
|
/// @notice Return whether the next block on `lane` has `key` and an empty payload.
|
|
199
227
|
/// @param exec Execution whose decoder is inspected without advancing.
|
|
200
228
|
/// @param lane Decoder lane to inspect.
|
|
@@ -218,6 +246,20 @@ library Executions {
|
|
|
218
246
|
exec.decoders = cur.seekAbs(end);
|
|
219
247
|
}
|
|
220
248
|
|
|
249
|
+
/// @notice Validate and consume one block from a decoder lane, returning its complete encoding.
|
|
250
|
+
/// @param exec Execution whose selected decoder cursor is advanced past the block.
|
|
251
|
+
/// @param lane Execution decoder lane to select.
|
|
252
|
+
/// @param key Expected block key.
|
|
253
|
+
/// @return data Calldata view of the complete block, including its header.
|
|
254
|
+
function takeBlock(
|
|
255
|
+
Execution memory exec,
|
|
256
|
+
uint8 lane,
|
|
257
|
+
bytes4 key
|
|
258
|
+
) internal pure returns (bytes calldata data) {
|
|
259
|
+
(uint abs, uint end) = consume(exec, lane, Specs.create(key, 0, 0, 0));
|
|
260
|
+
data = msg.data[abs - Sizes.Header:end];
|
|
261
|
+
}
|
|
262
|
+
|
|
221
263
|
/// @notice Consume a matching empty block from an execution decoder lane when present.
|
|
222
264
|
/// @param exec Execution whose selected decoder advances only for a matching empty block.
|
|
223
265
|
/// @param lane Decoder lane to consume.
|
package/package.json
CHANGED
package/ports/Allowance.sol
CHANGED
|
@@ -2,35 +2,45 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {AllowanceHook} from "../commands/admin/Allowance.sol";
|
|
6
5
|
import {Specs} from "../Codec.sol";
|
|
7
6
|
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
8
7
|
|
|
9
8
|
using Executions for Execution;
|
|
10
9
|
|
|
11
|
-
/// @
|
|
12
|
-
|
|
13
|
-
///
|
|
14
|
-
///
|
|
15
|
-
|
|
10
|
+
/// @notice Hook implemented by hosts that handle allowance requests from peers.
|
|
11
|
+
abstract contract RequestAllowanceHook {
|
|
12
|
+
/// @notice Override to handle one allowance request from a peer host.
|
|
13
|
+
/// @dev The implementation is responsible for validating the request and
|
|
14
|
+
/// deciding what allowance, if any, to grant.
|
|
15
|
+
/// @param peer Peer host node ID requesting the allowance.
|
|
16
|
+
/// @param asset Asset identifier supplied by the peer.
|
|
17
|
+
/// @param amount Allowance amount requested in the asset's native units.
|
|
18
|
+
function requestAllowance(uint peer, bytes32 asset, uint amount) internal virtual;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @title RequestAllowancePort
|
|
22
|
+
/// @notice Port that lets trusted peers request their own asset allowances.
|
|
23
|
+
/// Each AMOUNT block is scoped to the caller and passed unchanged to
|
|
24
|
+
/// `requestAllowance(peer, asset, amount)`.
|
|
25
|
+
abstract contract RequestAllowancePort is PortBase, RequestAllowanceHook {
|
|
16
26
|
uint private immutable descriptor;
|
|
17
27
|
|
|
18
28
|
constructor() {
|
|
19
|
-
(, descriptor) = port("
|
|
29
|
+
(, descriptor) = port("portRequestAllowance", Specs.Amount, Specs.Empty, 0);
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
/// @notice
|
|
23
|
-
/// @param data AMOUNT block stream
|
|
32
|
+
/// @notice Request asset allowances for the calling peer.
|
|
33
|
+
/// @param data AMOUNT block stream supplied by the trusted peer.
|
|
24
34
|
/// @return Empty response bytes.
|
|
25
|
-
function
|
|
35
|
+
function portRequestAllowance(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
26
36
|
Execution memory exec = openInput(data, descriptor, 0);
|
|
27
37
|
uint peer = caller();
|
|
28
38
|
|
|
29
39
|
while (exec.more()) {
|
|
30
40
|
(bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
|
|
31
|
-
|
|
41
|
+
requestAllowance(peer, asset, amount);
|
|
32
42
|
}
|
|
33
|
-
|
|
43
|
+
|
|
34
44
|
return "";
|
|
35
45
|
}
|
|
36
46
|
}
|
package/ports/Assets.sol
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PortBase} from "./Base.sol";
|
|
5
|
+
import {AllowAssetsHook} from "../commands/admin/AllowAssets.sol";
|
|
6
|
+
import {DenyAssetsHook} from "../commands/admin/DenyAssets.sol";
|
|
7
|
+
import {Specs} from "../Codec.sol";
|
|
8
|
+
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
9
|
+
|
|
10
|
+
using Executions for Execution;
|
|
11
|
+
|
|
12
|
+
/// @title AllowAssetsPort
|
|
13
|
+
/// @notice Port that permits a list of assets on behalf of a peer host.
|
|
14
|
+
/// Each ASSET block in the input calls `allowAsset`. Restricted to trusted peers.
|
|
15
|
+
abstract contract AllowAssetsPort is PortBase, AllowAssetsHook {
|
|
16
|
+
uint private immutable descriptor;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
(, descriptor) = port("portAllowAssets", Specs.Asset, Specs.Empty, 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// @notice Execute the allow-assets peer call.
|
|
23
|
+
/// @param data ASSET block stream supplied by the trusted peer.
|
|
24
|
+
/// @return Empty response bytes.
|
|
25
|
+
function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
26
|
+
Execution memory exec = openInput(data, descriptor, 0);
|
|
27
|
+
|
|
28
|
+
while (exec.more()) {
|
|
29
|
+
bytes32 asset = exec.unpackAsset(Lanes.Input);
|
|
30
|
+
allowAsset(asset);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// @title DenyAssetsPort
|
|
38
|
+
/// @notice Port that blocks a list of assets on behalf of a peer host.
|
|
39
|
+
/// Each ASSET block in the input calls `denyAsset`. Restricted to trusted peers.
|
|
40
|
+
abstract contract DenyAssetsPort is PortBase, DenyAssetsHook {
|
|
41
|
+
uint private immutable descriptor;
|
|
42
|
+
|
|
43
|
+
constructor() {
|
|
44
|
+
(, descriptor) = port("portDenyAssets", Specs.Asset, Specs.Empty, 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// @notice Execute the deny-assets peer call.
|
|
48
|
+
/// @param data ASSET block stream supplied by the trusted peer.
|
|
49
|
+
/// @return Empty response bytes.
|
|
50
|
+
function portDenyAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
51
|
+
Execution memory exec = openInput(data, descriptor, 0);
|
|
52
|
+
|
|
53
|
+
while (exec.more()) {
|
|
54
|
+
bytes32 asset = exec.unpackAsset(Lanes.Input);
|
|
55
|
+
denyAsset(asset);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// @notice Hook implemented by hosts that fulfill asset requests from peers.
|
|
63
|
+
abstract contract RequestAssetHook {
|
|
64
|
+
/// @notice Override to handle one asset request from a peer host.
|
|
65
|
+
/// @dev The implementation is responsible for validating `asset`, enforcing
|
|
66
|
+
/// requester policy, and sending the approved amount to the requester.
|
|
67
|
+
/// @param peer Peer host node ID for this request.
|
|
68
|
+
/// @param asset Asset identifier supplied by the peer.
|
|
69
|
+
/// @param amount Amount requested in the asset's native units.
|
|
70
|
+
function requestAsset(uint peer, bytes32 asset, uint amount) internal virtual;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// @title RequestAssetPort
|
|
74
|
+
/// @notice Port that lets trusted peers request assets from the receiving host.
|
|
75
|
+
/// Each AMOUNT block is scoped to the caller and passed unchanged to
|
|
76
|
+
/// `requestAsset(peer, asset, amount)`. The hook validates support and performs
|
|
77
|
+
/// any accounting and transfer required by the host.
|
|
78
|
+
abstract contract RequestAssetPort is PortBase, RequestAssetHook {
|
|
79
|
+
uint private immutable descriptor;
|
|
80
|
+
|
|
81
|
+
constructor() {
|
|
82
|
+
(, descriptor) = port("portRequestAsset", Specs.Amount, Specs.Empty, 0);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @notice Request assets for the calling peer.
|
|
86
|
+
/// @param data AMOUNT block stream supplied by the trusted peer.
|
|
87
|
+
/// @return Empty response bytes.
|
|
88
|
+
function portRequestAsset(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
89
|
+
Execution memory exec = openInput(data, descriptor, 0);
|
|
90
|
+
uint peer = caller();
|
|
91
|
+
|
|
92
|
+
while (exec.more()) {
|
|
93
|
+
(bytes32 asset, uint amount) = exec.unpackAmount(Lanes.Input);
|
|
94
|
+
requestAsset(peer, asset, amount);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return "";
|
|
98
|
+
}
|
|
99
|
+
}
|
package/utils/Cursors.sol
CHANGED
|
@@ -65,9 +65,9 @@ library Cursors {
|
|
|
65
65
|
/// @param tag Cursor identity tag.
|
|
66
66
|
/// @return cur Packed cursor.
|
|
67
67
|
function create(uint offset, uint len, uint items, uint8 flags, uint8 tag) internal pure returns (uint cur) {
|
|
68
|
-
cur |= max32(offset) << 32;
|
|
69
|
-
cur |= max32(len) << 64;
|
|
70
|
-
cur |= max16(items) << 96;
|
|
68
|
+
cur |= uint(max32(offset)) << 32;
|
|
69
|
+
cur |= uint(max32(len)) << 64;
|
|
70
|
+
cur |= uint(max16(items)) << 96;
|
|
71
71
|
cur |= uint(flags) << 112;
|
|
72
72
|
cur |= uint(tag) << 120;
|
|
73
73
|
}
|
package/utils/Nodes.sol
CHANGED
|
@@ -198,7 +198,7 @@ library Nodes {
|
|
|
198
198
|
/// @return node Command node ID embedding both the selector and address.
|
|
199
199
|
function toCommand(string memory name, address target) internal view returns (uint node) {
|
|
200
200
|
node = toLocalBase(Command) | uint(uint160(target));
|
|
201
|
-
node |= uint(uint32(toSelector(name, "(
|
|
201
|
+
node |= uint(uint32(toSelector(name, "(bytes)"))) << 160;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
/// @notice Build a chain-local port ID for the given endpoint name and contract.
|
package/utils/Utils.sol
CHANGED
|
@@ -11,76 +11,76 @@ error NotDivisible();
|
|
|
11
11
|
/// @dev Thrown when an ID claims to carry an address but the embedded address is zero.
|
|
12
12
|
error ZeroAddress();
|
|
13
13
|
|
|
14
|
-
/// @notice Assert that `value` fits in uint8 and return it
|
|
15
|
-
function max8(uint value) pure returns (
|
|
14
|
+
/// @notice Assert that `value` fits in uint8 and return it as uint8.
|
|
15
|
+
function max8(uint value) pure returns (uint8) {
|
|
16
16
|
if (value > type(uint8).max) {
|
|
17
17
|
revert ValueOverflow();
|
|
18
18
|
}
|
|
19
|
-
return value;
|
|
19
|
+
return uint8(value);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/// @notice Assert that `value` fits in uint16 and return it
|
|
23
|
-
function max16(uint value) pure returns (
|
|
22
|
+
/// @notice Assert that `value` fits in uint16 and return it as uint16.
|
|
23
|
+
function max16(uint value) pure returns (uint16) {
|
|
24
24
|
if (value > type(uint16).max) {
|
|
25
25
|
revert ValueOverflow();
|
|
26
26
|
}
|
|
27
|
-
return value;
|
|
27
|
+
return uint16(value);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/// @notice Assert that `value` fits in uint24 and return it
|
|
31
|
-
function max24(uint value) pure returns (
|
|
30
|
+
/// @notice Assert that `value` fits in uint24 and return it as uint24.
|
|
31
|
+
function max24(uint value) pure returns (uint24) {
|
|
32
32
|
if (value > type(uint24).max) {
|
|
33
33
|
revert ValueOverflow();
|
|
34
34
|
}
|
|
35
|
-
return value;
|
|
35
|
+
return uint24(value);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
/// @notice Assert that `value` fits in uint32 and return it
|
|
39
|
-
function max32(uint value) pure returns (
|
|
38
|
+
/// @notice Assert that `value` fits in uint32 and return it as uint32.
|
|
39
|
+
function max32(uint value) pure returns (uint32) {
|
|
40
40
|
if (value > type(uint32).max) {
|
|
41
41
|
revert ValueOverflow();
|
|
42
42
|
}
|
|
43
|
-
return value;
|
|
43
|
+
return uint32(value);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/// @notice Assert that `value` fits in uint40 and return it
|
|
47
|
-
function max40(uint value) pure returns (
|
|
46
|
+
/// @notice Assert that `value` fits in uint40 and return it as uint40.
|
|
47
|
+
function max40(uint value) pure returns (uint40) {
|
|
48
48
|
if (value > type(uint40).max) {
|
|
49
49
|
revert ValueOverflow();
|
|
50
50
|
}
|
|
51
|
-
return value;
|
|
51
|
+
return uint40(value);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/// @notice Assert that `value` fits in uint64 and return it
|
|
55
|
-
function max64(uint value) pure returns (
|
|
54
|
+
/// @notice Assert that `value` fits in uint64 and return it as uint64.
|
|
55
|
+
function max64(uint value) pure returns (uint64) {
|
|
56
56
|
if (value > type(uint64).max) {
|
|
57
57
|
revert ValueOverflow();
|
|
58
58
|
}
|
|
59
|
-
return value;
|
|
59
|
+
return uint64(value);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/// @notice Assert that `value` fits in uint96 and return it
|
|
63
|
-
function max96(uint value) pure returns (
|
|
62
|
+
/// @notice Assert that `value` fits in uint96 and return it as uint96.
|
|
63
|
+
function max96(uint value) pure returns (uint96) {
|
|
64
64
|
if (value > type(uint96).max) {
|
|
65
65
|
revert ValueOverflow();
|
|
66
66
|
}
|
|
67
|
-
return value;
|
|
67
|
+
return uint96(value);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/// @notice Assert that `value` fits in uint128 and return it
|
|
71
|
-
function max128(uint value) pure returns (
|
|
70
|
+
/// @notice Assert that `value` fits in uint128 and return it as uint128.
|
|
71
|
+
function max128(uint value) pure returns (uint128) {
|
|
72
72
|
if (value > type(uint128).max) {
|
|
73
73
|
revert ValueOverflow();
|
|
74
74
|
}
|
|
75
|
-
return value;
|
|
75
|
+
return uint128(value);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/// @notice Assert that `value` fits in uint160 and return it
|
|
79
|
-
function max160(uint value) pure returns (
|
|
78
|
+
/// @notice Assert that `value` fits in uint160 and return it as uint160.
|
|
79
|
+
function max160(uint value) pure returns (uint160) {
|
|
80
80
|
if (value > type(uint160).max) {
|
|
81
81
|
revert ValueOverflow();
|
|
82
82
|
}
|
|
83
|
-
return value;
|
|
83
|
+
return uint160(value);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Packed fields
|
|
@@ -108,25 +108,25 @@ function clear64(uint value, uint shift) pure returns (uint) {
|
|
|
108
108
|
/// @notice Replace the 8-bit field beginning at `shift` with `field`.
|
|
109
109
|
/// @dev Reverts when `field` does not fit in 8 bits.
|
|
110
110
|
function replace8(uint value, uint shift, uint field) pure returns (uint) {
|
|
111
|
-
return clear8(value, shift) | (max8(field) << shift);
|
|
111
|
+
return clear8(value, shift) | (uint(max8(field)) << shift);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/// @notice Replace the 16-bit field beginning at `shift` with `field`.
|
|
115
115
|
/// @dev Reverts when `field` does not fit in 16 bits.
|
|
116
116
|
function replace16(uint value, uint shift, uint field) pure returns (uint) {
|
|
117
|
-
return clear16(value, shift) | (max16(field) << shift);
|
|
117
|
+
return clear16(value, shift) | (uint(max16(field)) << shift);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/// @notice Replace the 32-bit field beginning at `shift` with `field`.
|
|
121
121
|
/// @dev Reverts when `field` does not fit in 32 bits.
|
|
122
122
|
function replace32(uint value, uint shift, uint field) pure returns (uint) {
|
|
123
|
-
return clear32(value, shift) | (max32(field) << shift);
|
|
123
|
+
return clear32(value, shift) | (uint(max32(field)) << shift);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/// @notice Replace the 64-bit field beginning at `shift` with `field`.
|
|
127
127
|
/// @dev Reverts when `field` does not fit in 64 bits.
|
|
128
128
|
function replace64(uint value, uint shift, uint field) pure returns (uint) {
|
|
129
|
-
return clear64(value, shift) | (max64(field) << shift);
|
|
129
|
+
return clear64(value, shift) | (uint(max64(field)) << shift);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/// @notice Assert that `n` is evenly divisible by `divisor`.
|
package/events/Commander.sol
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
-
|
|
6
|
-
/// @notice Emitted when a commander is announced for a chain/domain.
|
|
7
|
-
abstract contract CommanderEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Commander(uint indexed host, uint chain, bytes32 native, bytes32 admin)";
|
|
9
|
-
|
|
10
|
-
/// @param host Commander host node ID for the chain.
|
|
11
|
-
/// @param chain Chain/domain node ID.
|
|
12
|
-
/// @param native Native asset ID for the chain.
|
|
13
|
-
/// @param admin Admin account for the commander host on the chain.
|
|
14
|
-
event Commander(uint indexed host, uint chain, bytes32 native, bytes32 admin);
|
|
15
|
-
|
|
16
|
-
constructor() {
|
|
17
|
-
emit EventAbi(ABI);
|
|
18
|
-
}
|
|
19
|
-
}
|
package/ports/AllowAssets.sol
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {AllowAssetsHook} from "../commands/admin/AllowAssets.sol";
|
|
6
|
-
import {Specs} from "../Codec.sol";
|
|
7
|
-
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
8
|
-
|
|
9
|
-
using Executions for Execution;
|
|
10
|
-
|
|
11
|
-
/// @title AllowAssetsPort
|
|
12
|
-
/// @notice Port that permits a list of assets on behalf of a peer host.
|
|
13
|
-
/// Each ASSET block in the input calls `allowAsset`. Restricted to trusted peers.
|
|
14
|
-
abstract contract AllowAssetsPort is PortBase, AllowAssetsHook {
|
|
15
|
-
uint private immutable descriptor;
|
|
16
|
-
|
|
17
|
-
constructor() {
|
|
18
|
-
(, descriptor) = port("portAllowAssets", Specs.Asset, Specs.Empty, 0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/// @notice Execute the allow-assets peer call.
|
|
22
|
-
/// @param data ASSET block stream supplied by the trusted peer.
|
|
23
|
-
/// @return Empty response bytes.
|
|
24
|
-
function portAllowAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
Execution memory exec = openInput(data, descriptor, 0);
|
|
26
|
-
|
|
27
|
-
while (exec.more()) {
|
|
28
|
-
bytes32 asset = exec.unpackAsset(Lanes.Input);
|
|
29
|
-
allowAsset(asset);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
}
|
package/ports/DenyAssets.sol
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {DenyAssetsHook} from "../commands/admin/DenyAssets.sol";
|
|
6
|
-
import {Specs} from "../Codec.sol";
|
|
7
|
-
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
8
|
-
|
|
9
|
-
using Executions for Execution;
|
|
10
|
-
|
|
11
|
-
/// @title DenyAssetsPort
|
|
12
|
-
/// @notice Port that blocks a list of assets on behalf of a peer host.
|
|
13
|
-
/// Each ASSET block in the input calls `denyAsset`. Restricted to trusted peers.
|
|
14
|
-
abstract contract DenyAssetsPort is PortBase, DenyAssetsHook {
|
|
15
|
-
uint private immutable descriptor;
|
|
16
|
-
|
|
17
|
-
constructor() {
|
|
18
|
-
(, descriptor) = port("portDenyAssets", Specs.Asset, Specs.Empty, 0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/// @notice Execute the deny-assets peer call.
|
|
22
|
-
/// @param data ASSET block stream supplied by the trusted peer.
|
|
23
|
-
/// @return Empty response bytes.
|
|
24
|
-
function portDenyAssets(bytes calldata data) external onlyPeer returns (bytes memory) {
|
|
25
|
-
Execution memory exec = openInput(data, descriptor, 0);
|
|
26
|
-
|
|
27
|
-
while (exec.more()) {
|
|
28
|
-
bytes32 asset = exec.unpackAsset(Lanes.Input);
|
|
29
|
-
denyAsset(asset);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
}
|