@rootzero/contracts 0.9.3 → 0.9.4
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/Commands.sol +3 -2
- package/Core.sol +3 -0
- package/Cursors.sol +1 -1
- package/README.md +18 -24
- package/blocks/Cursors.sol +294 -339
- package/blocks/Keys.sol +38 -57
- package/blocks/Schema.sol +55 -208
- package/blocks/Writers.sol +345 -135
- package/commands/Base.sol +6 -48
- package/commands/Burn.sol +2 -2
- package/commands/Credit.sol +3 -3
- package/commands/Debit.sol +3 -2
- package/commands/Deposit.sol +11 -8
- package/commands/Provision.sol +11 -8
- package/commands/Transfer.sol +2 -2
- package/commands/Withdraw.sol +3 -3
- package/commands/admin/AllowAssets.sol +1 -1
- package/commands/admin/Allowance.sol +1 -1
- package/commands/admin/Authorize.sol +1 -1
- package/commands/admin/DenyAssets.sol +1 -1
- package/commands/admin/Execute.sol +7 -6
- package/commands/admin/Unauthorize.sol +1 -1
- package/core/Access.sol +11 -0
- package/core/Context.sol +3 -4
- package/core/Payable.sol +57 -0
- package/core/Pipeline.sol +55 -0
- package/docs/Schema.md +194 -0
- package/events/Command.sol +1 -2
- package/package.json +2 -2
- package/peer/AllowAssets.sol +1 -1
- package/peer/Allowance.sol +1 -1
- package/peer/BalancePull.sol +1 -1
- package/peer/DenyAssets.sol +1 -1
- package/peer/Pipe.sol +38 -0
- package/peer/Settle.sol +1 -1
- package/queries/Assets.sol +4 -3
- package/queries/Balances.sol +2 -1
- package/queries/Positions.sol +12 -12
- package/utils/Value.sol +8 -14
- package/commands/Pipe.sol +0 -67
- package/docs/GETTING_STARTED.md +0 -294
package/docs/Schema.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Schema
|
|
2
|
+
|
|
3
|
+
Rootzero request and response data is encoded as a stream of typed blocks. A
|
|
4
|
+
schema string describes payload layout for discovery events and tooling; the
|
|
5
|
+
runtime block key is derived only from the block name.
|
|
6
|
+
|
|
7
|
+
## Wire Format
|
|
8
|
+
|
|
9
|
+
Every block uses the same header:
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
[bytes4 key][uint32 payloadLen][payload]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`payloadLen` is big-endian and counts only payload bytes. Child blocks and list
|
|
16
|
+
items use the same header format.
|
|
17
|
+
|
|
18
|
+
The block key is:
|
|
19
|
+
|
|
20
|
+
```txt
|
|
21
|
+
bytes4(keccak256("#name"))
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For example, `#amount { bytes32 asset, bytes32 meta, uint amount }` uses the key
|
|
25
|
+
derived from `#amount`. Blocks must not be overloaded: one block name should have
|
|
26
|
+
one protocol meaning.
|
|
27
|
+
|
|
28
|
+
## Block Syntax
|
|
29
|
+
|
|
30
|
+
A block starts with `#`. Fixed fields are written in braces:
|
|
31
|
+
|
|
32
|
+
```txt
|
|
33
|
+
#amount { bytes32 asset, bytes32 meta, uint amount }
|
|
34
|
+
#account { bytes32 account }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
A block without braces has no payload:
|
|
38
|
+
|
|
39
|
+
```txt
|
|
40
|
+
#unit
|
|
41
|
+
#bytes
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Empty braces are invalid. A zero-payload block must omit braces.
|
|
45
|
+
|
|
46
|
+
A schema is a comma-separated list of items. Order is significant.
|
|
47
|
+
|
|
48
|
+
```txt
|
|
49
|
+
#amount { bytes32 asset, bytes32 meta, uint amount },
|
|
50
|
+
maybe #account { bytes32 account }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Payload Layout
|
|
54
|
+
|
|
55
|
+
A block payload has fixed fields first, followed by an optional child-block tail.
|
|
56
|
+
Once a child block appears, no more fixed fields may follow.
|
|
57
|
+
|
|
58
|
+
```txt
|
|
59
|
+
#call { uint target, uint value, #bytes as payload }
|
|
60
|
+
#context { bytes32 account, uint value, #bytes as state, #bytes as request }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The tail is embedded directly as child block bytes. There is no wrapper around a
|
|
64
|
+
child-block tail.
|
|
65
|
+
|
|
66
|
+
Raw dynamic bytes are represented with the reserved `#bytes` child block. Use an
|
|
67
|
+
alias to give those bytes a presentation name:
|
|
68
|
+
|
|
69
|
+
```txt
|
|
70
|
+
#bytes as payload
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Modifiers
|
|
74
|
+
|
|
75
|
+
Cardinality is expressed with prefix keywords:
|
|
76
|
+
|
|
77
|
+
```txt
|
|
78
|
+
#balance { bytes32 asset, bytes32 meta, uint amount }
|
|
79
|
+
maybe #balance { bytes32 asset, bytes32 meta, uint amount }
|
|
80
|
+
many #balance { bytes32 asset, bytes32 meta, uint amount }
|
|
81
|
+
maybe many #balance { bytes32 asset, bytes32 meta, uint amount }
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- no prefix: one required item
|
|
85
|
+
- `maybe`: optional item
|
|
86
|
+
- `many`: one `#list` block whose payload contains repeated items
|
|
87
|
+
- `maybe many`: optional `#list` block
|
|
88
|
+
|
|
89
|
+
`maybe` emits no placeholder when absent. `many` wraps repeated items in one
|
|
90
|
+
generic list block; it does not repeat the item in place.
|
|
91
|
+
|
|
92
|
+
## Prime Items
|
|
93
|
+
|
|
94
|
+
The empty string `""` means no schema. Whitespace-only schemas are invalid.
|
|
95
|
+
|
|
96
|
+
For a non-empty schema, the first top-level item is the prime item. Prime items
|
|
97
|
+
may repeat at the top level for batching. Later top-level items are globals for
|
|
98
|
+
the whole batch and are not counted as per-operation prime blocks.
|
|
99
|
+
|
|
100
|
+
The prime item cannot be optional. If a command needs a per-operation marker with
|
|
101
|
+
no payload, use a zero-payload block such as `#unit`.
|
|
102
|
+
|
|
103
|
+
## Aliases
|
|
104
|
+
|
|
105
|
+
Aliases are presentation metadata for tooling. They do not change payload layout
|
|
106
|
+
or runtime keys.
|
|
107
|
+
|
|
108
|
+
```txt
|
|
109
|
+
maybe #account { bytes32 account } as recipient
|
|
110
|
+
#call { uint target, uint value, #bytes as payload }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Aliases may be used on any block item, including child blocks and prime items.
|
|
114
|
+
|
|
115
|
+
## Field Types
|
|
116
|
+
|
|
117
|
+
Supported field types are chain-neutral:
|
|
118
|
+
|
|
119
|
+
```txt
|
|
120
|
+
uint, uint8, uint16, uint32, uint64, uint128, uint256
|
|
121
|
+
int, int8, int16, int32, int64, int128, int256
|
|
122
|
+
bool
|
|
123
|
+
bytes1 through bytes32
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`uint` means `uint256`; `int` means `int256`. Other integer widths, unsized
|
|
127
|
+
`bytes`, `string`, and array syntax are not part of the core schema DSL.
|
|
128
|
+
|
|
129
|
+
Integers are encoded big-endian. Signed integers use two's-complement encoding
|
|
130
|
+
for their declared width. `bool` is one byte: `0x00` for false and `0x01` for
|
|
131
|
+
true. `bytesN` values are encoded as exactly `N` bytes with no padding.
|
|
132
|
+
|
|
133
|
+
## Identifiers
|
|
134
|
+
|
|
135
|
+
Block names, field names, and aliases use lower camelCase ASCII identifiers:
|
|
136
|
+
|
|
137
|
+
```txt
|
|
138
|
+
[a-z][a-zA-Z0-9]*
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Invalid examples:
|
|
142
|
+
|
|
143
|
+
```txt
|
|
144
|
+
Amount
|
|
145
|
+
asset_meta
|
|
146
|
+
asset-meta
|
|
147
|
+
0account
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Reserved words include `maybe`, `many`, `as`, all field type names, and the
|
|
151
|
+
reserved block names `bytes`, `data`, and `list`.
|
|
152
|
+
|
|
153
|
+
## Reserved Blocks
|
|
154
|
+
|
|
155
|
+
- `#bytes`: raw dynamic bytes, written without a body
|
|
156
|
+
- `#data`: generic/custom payload block
|
|
157
|
+
- `#list`: generic list wrapper emitted by `many`
|
|
158
|
+
|
|
159
|
+
Use `#data` when a local schema needs a stable generic key:
|
|
160
|
+
|
|
161
|
+
```txt
|
|
162
|
+
#data { uint foo, bytes32 tag }
|
|
163
|
+
#data { #bytes as payload }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If a schema string starts with a fixed field type, it is shorthand for one
|
|
167
|
+
top-level `#data` block:
|
|
168
|
+
|
|
169
|
+
```txt
|
|
170
|
+
uint foo, bytes32 tag
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
expands to:
|
|
174
|
+
|
|
175
|
+
```txt
|
|
176
|
+
#data { uint foo, bytes32 tag }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Standard Blocks
|
|
180
|
+
|
|
181
|
+
Common protocol schemas live in `contracts/blocks/Schema.sol`:
|
|
182
|
+
|
|
183
|
+
```txt
|
|
184
|
+
#amount { bytes32 asset, bytes32 meta, uint amount }
|
|
185
|
+
#balance { bytes32 asset, bytes32 meta, uint amount }
|
|
186
|
+
#custody { uint host, bytes32 asset, bytes32 meta, uint amount }
|
|
187
|
+
#payout { bytes32 account, bytes32 asset, bytes32 meta, uint amount }
|
|
188
|
+
#call { uint target, uint value, #bytes as payload }
|
|
189
|
+
#step { uint target, uint value, #bytes as request }
|
|
190
|
+
#context { bytes32 account, uint value, #bytes as state, #bytes as request }
|
|
191
|
+
#auth { uint cid, uint deadline, #bytes as proof }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`Keys.sol` contains the corresponding runtime keys.
|
package/events/Command.sol
CHANGED
|
@@ -14,8 +14,7 @@ abstract contract CommandEvent is EventEmitter {
|
|
|
14
14
|
/// @param shape Per-operation prime block counts encoded as `request:state:output`.
|
|
15
15
|
/// Blocks outside the prime runs are global batch blocks and are excluded
|
|
16
16
|
/// from the counts.
|
|
17
|
-
/// @param request Schema
|
|
18
|
-
/// when the request has global blocks but no prime blocks.
|
|
17
|
+
/// @param request Schema string describing the request shape.
|
|
19
18
|
/// @param state Block key expected for input state, or `Keys.Empty`.
|
|
20
19
|
/// @param output Block key produced for output state, or `Keys.Empty`.
|
|
21
20
|
/// @param acceptsValue Whether the command entrypoint accepts nonzero `msg.value`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootzero/contracts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
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",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"**/*.sol",
|
|
10
10
|
"README.md",
|
|
11
11
|
"LICENSE",
|
|
12
|
-
"docs/
|
|
12
|
+
"docs/Schema.md"
|
|
13
13
|
],
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
package/peer/AllowAssets.sol
CHANGED
package/peer/Allowance.sol
CHANGED
package/peer/BalancePull.sol
CHANGED
package/peer/DenyAssets.sol
CHANGED
package/peer/Pipe.sol
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
+
pragma solidity ^0.8.33;
|
|
3
|
+
|
|
4
|
+
import {PeerBase} from "./Base.sol";
|
|
5
|
+
import {Pipeline} from "../core/Pipeline.sol";
|
|
6
|
+
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
7
|
+
import {Budget} from "../utils/Value.sol";
|
|
8
|
+
|
|
9
|
+
using Cursors for Cur;
|
|
10
|
+
|
|
11
|
+
/// @title PeerPipePayable
|
|
12
|
+
/// @notice Peer that consumes CONTEXT blocks and executes each context request as a STEP stream.
|
|
13
|
+
/// Each CONTEXT block carries an account, state, and request; the request is passed to the
|
|
14
|
+
/// shared pipeline as the step stream.
|
|
15
|
+
abstract contract PeerPipePayable is PeerBase, Pipeline {
|
|
16
|
+
string private constant NAME = "peerPipePayable";
|
|
17
|
+
uint internal immutable peerPipePayableId = peerId(NAME);
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
emit Peer(host, peerPipePayableId, NAME, "1:0", Schemas.Context, "", true);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @notice Execute peer-supplied contexts through the shared payable pipe.
|
|
24
|
+
/// @dev Each context receives its own explicit value sub-budget. Any top-level
|
|
25
|
+
/// `msg.value` not assigned to a context remains on this host.
|
|
26
|
+
function peerPipePayable(bytes calldata request) external payable onlyPeer returns (bytes memory) {
|
|
27
|
+
(Cur memory input, ) = cursor(request, 1);
|
|
28
|
+
Budget memory budget = valueBudget();
|
|
29
|
+
|
|
30
|
+
while (input.i < input.bound) {
|
|
31
|
+
(bytes32 account, uint value, bytes calldata state, bytes calldata steps) = input.unpackContext();
|
|
32
|
+
pipe(account, state, steps, allocateValue(budget, value));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
input.close();
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
}
|
package/peer/Settle.sol
CHANGED
package/queries/Assets.sol
CHANGED
|
@@ -30,8 +30,8 @@ abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/// @notice Resolve allowlist status for a run of requested `(asset, meta)` tuples.
|
|
33
|
-
/// @param request Block-stream request consisting of
|
|
34
|
-
/// @return Block-stream response containing one
|
|
33
|
+
/// @param request Block-stream request consisting of `#asset { bytes32 asset, bytes32 meta }` blocks.
|
|
34
|
+
/// @return Block-stream response containing one `#status { bool ok }` per asset block.
|
|
35
35
|
function isAllowedAsset(bytes calldata request) external view returns (bytes memory) {
|
|
36
36
|
(Cur memory query, uint groups) = cursor(request, 1);
|
|
37
37
|
Writer memory response = Writers.allocStatuses(groups);
|
|
@@ -42,6 +42,7 @@ abstract contract IsAllowedAsset is QueryBase, IsAllowedAssetHook {
|
|
|
42
42
|
response.appendStatus(allowed);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
query.close();
|
|
46
|
+
return response.finish();
|
|
46
47
|
}
|
|
47
48
|
}
|
package/queries/Balances.sol
CHANGED
package/queries/Positions.sol
CHANGED
|
@@ -6,11 +6,12 @@ import {Forms} from "../blocks/Schema.sol";
|
|
|
6
6
|
import {QueryBase} from "./Base.sol";
|
|
7
7
|
|
|
8
8
|
using Cursors for Cur;
|
|
9
|
+
using Writers for Writer;
|
|
9
10
|
|
|
10
11
|
abstract contract GetPositionHook {
|
|
11
|
-
/// @notice
|
|
12
|
-
/// Concrete implementations must append exactly one
|
|
13
|
-
///
|
|
12
|
+
/// @notice Append the position response for one requested position.
|
|
13
|
+
/// Concrete implementations must append exactly one response block matching
|
|
14
|
+
/// the query output schema.
|
|
14
15
|
/// @param account Requested account identifier.
|
|
15
16
|
/// @param asset Requested asset identifier.
|
|
16
17
|
/// @param meta Requested asset metadata slot.
|
|
@@ -26,31 +27,30 @@ abstract contract GetPositionHook {
|
|
|
26
27
|
/// @title GetPosition
|
|
27
28
|
/// @notice Rootzero query that resolves one dynamic position response for each requested position.
|
|
28
29
|
/// The request is a run of `ACCOUNT_ASSET` form blocks.
|
|
29
|
-
/// The response returns one
|
|
30
|
+
/// The response returns one output-schema block per position entry, preserving request order.
|
|
30
31
|
abstract contract GetPosition is QueryBase, GetPositionHook {
|
|
31
32
|
string private constant NAME = "getPosition";
|
|
33
|
+
|
|
32
34
|
uint public immutable getPositionId = queryId(NAME);
|
|
33
|
-
uint internal immutable positionResponseSize;
|
|
34
35
|
|
|
35
|
-
constructor(string memory output
|
|
36
|
-
positionResponseSize = responseSize;
|
|
36
|
+
constructor(string memory output) {
|
|
37
37
|
emit Query(host, getPositionId, NAME, "1:1", Forms.AccountAsset, output);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/// @notice Resolve positions for a run of requested `(account, asset, meta)` tuples.
|
|
41
|
-
/// @dev Allocates from
|
|
42
|
-
/// can append one `RESPONSE` block directly into the output stream.
|
|
41
|
+
/// @dev Allocates from a per-block capacity hint and grows when position outputs exceed it.
|
|
43
42
|
/// @param request Block-stream request consisting of `accountAsset(account, asset, meta)*`.
|
|
44
|
-
/// @return Block-stream response containing one
|
|
43
|
+
/// @return Block-stream response containing one output-schema block per position block.
|
|
45
44
|
function getPosition(bytes calldata request) external view returns (bytes memory) {
|
|
46
45
|
(Cur memory query, uint groups) = cursor(request, 1);
|
|
47
|
-
Writer memory response = Writers.
|
|
46
|
+
Writer memory response = Writers.allocAny(groups);
|
|
48
47
|
|
|
49
48
|
while (query.i < query.bound) {
|
|
50
49
|
(bytes32 account, bytes32 asset, bytes32 meta) = query.unpackAccountAsset();
|
|
51
50
|
appendPosition(account, asset, meta, response);
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
query.close();
|
|
54
|
+
return response.finish();
|
|
55
55
|
}
|
|
56
56
|
}
|
package/utils/Value.sol
CHANGED
|
@@ -8,17 +8,11 @@ struct Budget {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/// @title Values
|
|
11
|
-
/// @notice Native-value
|
|
11
|
+
/// @notice Native-value budget mutation helpers.
|
|
12
12
|
library Values {
|
|
13
13
|
/// @dev Thrown when a call attempts to spend more native value than remains in the budget.
|
|
14
14
|
error InsufficientValue();
|
|
15
15
|
|
|
16
|
-
/// @notice Create a budget from the current call's `msg.value`.
|
|
17
|
-
/// @return Budget initialised with the full `msg.value`.
|
|
18
|
-
function fromMsg() internal view returns (Budget memory) {
|
|
19
|
-
return Budget({remaining: msg.value});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
/// @notice Deduct `amount` from the budget and return it.
|
|
23
17
|
/// Reverts if `amount` exceeds `budget.remaining`.
|
|
24
18
|
/// @param budget Mutable budget to deduct from.
|
|
@@ -30,12 +24,12 @@ library Values {
|
|
|
30
24
|
return amount;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
|
-
/// @notice Deduct
|
|
34
|
-
///
|
|
35
|
-
/// @
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return amount;
|
|
27
|
+
/// @notice Deduct `amount` from the budget and return it as a new sub-budget.
|
|
28
|
+
/// Reverts if `amount` exceeds `budget.remaining`.
|
|
29
|
+
/// @param budget Mutable parent budget to deduct from.
|
|
30
|
+
/// @param amount Native value to assign to the sub-budget, in wei.
|
|
31
|
+
/// @return A new budget with `amount` remaining.
|
|
32
|
+
function allocate(Budget memory budget, uint amount) internal pure returns (Budget memory) {
|
|
33
|
+
return Budget({remaining: use(budget, amount)});
|
|
40
34
|
}
|
|
41
35
|
}
|
package/commands/Pipe.sol
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {CommandBase, CommandContext, CommandPayable, Keys} from "./Base.sol";
|
|
5
|
-
import {Cursors, Cur, Schemas} from "../Cursors.sol";
|
|
6
|
-
import {Accounts} from "../utils/Accounts.sol";
|
|
7
|
-
import {Budget, Values} from "../utils/Value.sol";
|
|
8
|
-
|
|
9
|
-
using Cursors for Cur;
|
|
10
|
-
|
|
11
|
-
abstract contract PipePayableHook {
|
|
12
|
-
/// @notice Override to dispatch one piped command step.
|
|
13
|
-
/// Called once per STEP block. The returned bytes become the state passed to
|
|
14
|
-
/// the next step.
|
|
15
|
-
/// @param id Command node ID to invoke or handle.
|
|
16
|
-
/// @param account Account identifier for the piped command context.
|
|
17
|
-
/// @param state Current threaded state block stream.
|
|
18
|
-
/// @param request Step request block stream.
|
|
19
|
-
/// @param value Native value assigned to this step.
|
|
20
|
-
/// @return Updated state block stream for the next step.
|
|
21
|
-
function dispatchCommand(
|
|
22
|
-
uint id,
|
|
23
|
-
bytes32 account,
|
|
24
|
-
bytes memory state,
|
|
25
|
-
bytes calldata request,
|
|
26
|
-
uint value
|
|
27
|
-
) internal virtual returns (bytes memory);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/// @title PipePayable
|
|
31
|
-
/// @notice Command that sequences multiple sub-command STEP invocations in a single transaction.
|
|
32
|
-
/// Each STEP block carries a command node, native value to forward, and an embedded request.
|
|
33
|
-
/// State threads through the steps: each step's output becomes the next step's state.
|
|
34
|
-
/// Admin accounts are not permitted to use `pipePayable`.
|
|
35
|
-
abstract contract PipePayable is CommandPayable, PipePayableHook {
|
|
36
|
-
string private constant NAME = "pipePayable";
|
|
37
|
-
|
|
38
|
-
uint internal immutable pipePayableId = commandId(NAME);
|
|
39
|
-
|
|
40
|
-
constructor() {
|
|
41
|
-
emit Command(host, pipePayableId, NAME, "1:0:0", Schemas.Step, Keys.Empty, Keys.Empty, true);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function pipe(
|
|
45
|
-
bytes32 account,
|
|
46
|
-
bytes memory state,
|
|
47
|
-
bytes calldata steps,
|
|
48
|
-
Budget memory budget
|
|
49
|
-
) internal returns (bytes memory) {
|
|
50
|
-
(Cur memory input, ) = cursor(steps, 1);
|
|
51
|
-
|
|
52
|
-
while (input.i < input.bound) {
|
|
53
|
-
(uint target, uint value, bytes calldata request) = input.unpackStep();
|
|
54
|
-
uint spend = Values.use(budget, value);
|
|
55
|
-
state = dispatchCommand(target, account, state, request, spend);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
settleValue(account, budget);
|
|
59
|
-
input.complete();
|
|
60
|
-
return state;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/// @notice Execute the pipePayable command.
|
|
64
|
-
function pipePayable(CommandContext calldata c) external payable onlyCommand(c.account) returns (bytes memory) {
|
|
65
|
-
return pipe(Accounts.ensureNotAdmin(c.account), c.state, c.request, Values.fromMsg());
|
|
66
|
-
}
|
|
67
|
-
}
|