@rootzero/contracts 1.23.0 → 1.25.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 +78 -0
- package/Codec.sol +1 -1
- package/Commands.sol +1 -1
- package/Core.sol +3 -3
- package/Endpoints.sol +9 -1
- package/README.md +35 -24
- package/Utils.sol +1 -1
- package/annotations/Schema.sol +1 -7
- package/codec/Blocks.sol +153 -24
- package/codec/Decoders.sol +42 -5
- package/codec/Descriptors.sol +3 -1
- package/codec/Keys.sol +2 -0
- package/codec/Readers.sol +12 -0
- package/codec/Schema.sol +5 -4
- package/codec/Specs.sol +3 -0
- package/codec/Writers.sol +12 -1
- package/commands/Base.sol +1 -4
- package/commands/Debit.sol +2 -5
- package/commands/Repay.sol +110 -10
- package/commands/admin/Base.sol +1 -4
- package/core/Access.sol +8 -0
- package/core/Pipeline.sol +26 -7
- package/core/Types.sol +8 -0
- package/execution/Budget.sol +22 -0
- package/execution/Execution.sol +48 -4
- package/package.json +1 -1
- package/ports/Pipe.sol +4 -5
- package/utils/Utils.sol +10 -0
package/codec/Decoders.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
|
-
import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Position, Tx} from "../core/Types.sol";
|
|
4
|
+
import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Debt, Position, Tx} from "../core/Types.sol";
|
|
5
5
|
import {Blocks} from "./Blocks.sol";
|
|
6
6
|
import {Sizes, Specs} from "./Specs.sol";
|
|
7
7
|
import {Cursors, Cur} from "../utils/Cursors.sol";
|
|
@@ -32,10 +32,18 @@ library Decoders {
|
|
|
32
32
|
cur.state = Cursors.wrap(source[i:], 0, 0);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/// @notice Open
|
|
36
|
-
/// @param source Calldata
|
|
37
|
-
/// @return cur Cursor spanning the
|
|
35
|
+
/// @notice Open a non-empty calldata source as an ungrouped cursor.
|
|
36
|
+
/// @param source Calldata region to open.
|
|
37
|
+
/// @return cur Cursor spanning the complete source.
|
|
38
38
|
function open(bytes calldata source) internal pure returns (Cur memory cur) {
|
|
39
|
+
if (source.length == 0) revert Blocks.EmptyRun();
|
|
40
|
+
cur.state = Cursors.wrap(source, 0, 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// @notice Open the first homogeneous batch in `source`.
|
|
44
|
+
/// @param source Calldata block stream to open.
|
|
45
|
+
/// @return cur Counted cursor spanning the first homogeneous batch.
|
|
46
|
+
function batch(bytes calldata source) internal pure returns (Cur memory cur) {
|
|
39
47
|
(uint abs, uint limit) = Cursors.bounds(source);
|
|
40
48
|
if (abs == limit) revert Blocks.EmptyRun();
|
|
41
49
|
|
|
@@ -69,6 +77,18 @@ library Decoders {
|
|
|
69
77
|
cur.state = cur.state.seekAbs(end);
|
|
70
78
|
}
|
|
71
79
|
|
|
80
|
+
/// @notice Validate a known key and consume the next block from a cursor.
|
|
81
|
+
/// @dev Validates no payload-size constraint beyond proving the complete block
|
|
82
|
+
/// lies within the cursor's logical region.
|
|
83
|
+
/// @param cur Cursor advanced over the complete block.
|
|
84
|
+
/// @param key Expected block key.
|
|
85
|
+
/// @return abs Absolute position of the first payload byte.
|
|
86
|
+
/// @return end Absolute position immediately after the payload.
|
|
87
|
+
function consume(Cur memory cur, bytes4 key) internal pure returns (uint abs, uint end) {
|
|
88
|
+
(abs, end) = Blocks.expectKey(cur.state.absolute(), key);
|
|
89
|
+
cur.state = cur.state.seekAbs(end);
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
/// @notice Consume a matching empty block from a cursor when present.
|
|
73
93
|
/// @param cur Cursor advanced only when the matching block is empty.
|
|
74
94
|
/// @param key Expected block key.
|
|
@@ -268,7 +288,7 @@ library Decoders {
|
|
|
268
288
|
/// @return out Cursor spanning the complete encoded block.
|
|
269
289
|
function takeBlock(Cur memory cur, bytes4 key) internal pure returns (Cur memory out) {
|
|
270
290
|
uint abs = cur.state.absolute();
|
|
271
|
-
(, uint end) = consume(cur,
|
|
291
|
+
(, uint end) = consume(cur, key);
|
|
272
292
|
out.state = Cursors.create(abs, end - abs, 0, 0, 0);
|
|
273
293
|
}
|
|
274
294
|
|
|
@@ -427,6 +447,16 @@ library Decoders {
|
|
|
427
447
|
(asset, amount) = Blocks.unpackBalance(abs);
|
|
428
448
|
}
|
|
429
449
|
|
|
450
|
+
/// @notice Decode and consume one DEBT block.
|
|
451
|
+
/// @param cur Cursor advanced past the block.
|
|
452
|
+
/// @return liability Decoded liability identifier.
|
|
453
|
+
/// @return debt Decoded debt quantity.
|
|
454
|
+
function unpackDebt(Cur memory cur) internal pure returns (bytes32 liability, uint debt) {
|
|
455
|
+
uint abs;
|
|
456
|
+
(cur.state, abs) = cur.state.consume(Sizes.Debt);
|
|
457
|
+
(liability, debt) = Blocks.unpackDebt(abs);
|
|
458
|
+
}
|
|
459
|
+
|
|
430
460
|
/// @notice Decode one BALANCE block and associate it with `host`.
|
|
431
461
|
/// @param cur Cursor advanced past the block.
|
|
432
462
|
/// @param host Host identifier associated with the balance.
|
|
@@ -614,6 +644,13 @@ library Decoders {
|
|
|
614
644
|
(value.asset, value.amount) = unpackBalance(cur);
|
|
615
645
|
}
|
|
616
646
|
|
|
647
|
+
/// @notice Decode one DEBT block into its structured value.
|
|
648
|
+
/// @param cur Cursor advanced past the block.
|
|
649
|
+
/// @return value Structured liability and debt.
|
|
650
|
+
function unpackDebtValue(Cur memory cur) internal pure returns (Debt memory value) {
|
|
651
|
+
(value.liability, value.debt) = unpackDebt(cur);
|
|
652
|
+
}
|
|
653
|
+
|
|
617
654
|
/// @notice Decode and consume one HOST_ACCOUNT_ASSET block.
|
|
618
655
|
/// @param cur Cursor advanced past the block.
|
|
619
656
|
/// @return host Decoded host identifier.
|
package/codec/Descriptors.sol
CHANGED
|
@@ -6,6 +6,7 @@ import {Lanes} from "../utils/Lanes.sol";
|
|
|
6
6
|
|
|
7
7
|
/// @title Flags
|
|
8
8
|
/// @notice Packed endpoint behavior flags.
|
|
9
|
+
/// @dev Bits 6 and 7 are reserved for endpoint-defined custom flags.
|
|
9
10
|
library Flags {
|
|
10
11
|
/// @dev Endpoint accepts nonzero native value.
|
|
11
12
|
uint8 internal constant Funded = 1 << 0;
|
|
@@ -27,7 +28,8 @@ library Descriptors {
|
|
|
27
28
|
/// `[output key:4][min:4][max:4][hint:3][stride:1]`
|
|
28
29
|
/// `[reserved:4]`
|
|
29
30
|
/// `[transactions:1]`
|
|
30
|
-
/// `[flags:1]`. Flag bits: funded = 0, admin = 1
|
|
31
|
+
/// `[flags:1]`. Flag bits: funded = 0, admin = 1; bits 6 and 7 are
|
|
32
|
+
/// reserved for endpoint-defined custom flags.
|
|
31
33
|
/// @param state State lane specification.
|
|
32
34
|
/// @param input Direct input lane specification.
|
|
33
35
|
/// @param output Output writer specification.
|
package/codec/Keys.sol
CHANGED
|
@@ -13,6 +13,8 @@ library Keys {
|
|
|
13
13
|
bytes4 constant Amount = bytes4(keccak256("#amount"));
|
|
14
14
|
/// @dev Ledger balance - (bytes32 asset, uint amount)
|
|
15
15
|
bytes4 constant Balance = bytes4(keccak256("#balance"));
|
|
16
|
+
/// @dev Liability-only debt state - (bytes32 liability, uint debt)
|
|
17
|
+
bytes4 constant Debt = bytes4(keccak256("#debt"));
|
|
16
18
|
/// @dev Host-scoped input amount - (uint host, bytes32 asset, uint amount)
|
|
17
19
|
bytes4 constant Allocation = bytes4(keccak256("#allocation"));
|
|
18
20
|
/// @dev Host-scoped allowance cap - (uint host, bytes32 asset, uint amount)
|
package/codec/Readers.sol
CHANGED
|
@@ -133,6 +133,18 @@ library Readers {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/// @notice Consume a DEBT block and return its fields.
|
|
137
|
+
/// @param cur Reader; advanced past the block.
|
|
138
|
+
/// @return liability Liability identifier.
|
|
139
|
+
/// @return debt Debt quantity.
|
|
140
|
+
function unpackDebt(Reader memory cur) internal pure returns (bytes32 liability, uint debt) {
|
|
141
|
+
uint abs = consume(cur, Keys.Debt, 64, 64);
|
|
142
|
+
assembly ("memory-safe") {
|
|
143
|
+
liability := mload(abs)
|
|
144
|
+
debt := mload(add(abs, 0x20))
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
/// @notice Consume a HOST_ASSET block and return its fields.
|
|
137
149
|
/// @param cur Reader; advanced past the block.
|
|
138
150
|
/// @return host Host identifier.
|
package/codec/Schema.sol
CHANGED
|
@@ -53,12 +53,12 @@ pragma solidity ^0.8.33;
|
|
|
53
53
|
// - command input and state streams are each a single run of blocks under the
|
|
54
54
|
// current protocol convention; the block format may support other shapes in
|
|
55
55
|
// future protocol surfaces
|
|
56
|
-
// - `balance(...)`, `custody(...)`, and `position(...)` are live, linear state in the active command pipeline
|
|
56
|
+
// - `balance(...)`, `debt(...)`, `custody(...)`, and `position(...)` are live, linear state in the active command pipeline
|
|
57
57
|
// - pipeline state belongs to the active account while the pipeline is executing
|
|
58
|
-
// - while a balance or custody is in-flight as pipeline state, it is not simultaneously persisted
|
|
58
|
+
// - while a balance, debt, or custody is in-flight as pipeline state, it is not simultaneously persisted
|
|
59
59
|
// in another ledger/store by this protocol
|
|
60
|
-
// - a position pairs live
|
|
61
|
-
// - position state
|
|
60
|
+
// - debt carries only a live liability side; position pairs live balance and debt sides
|
|
61
|
+
// - debt and position state are transient and do not themselves create or erase an externally persisted obligation
|
|
62
62
|
// - positions support backward composition, but pipeline steps always execute in encoded order
|
|
63
63
|
// - commands must preserve, transform, settle, or intentionally consume pipeline state
|
|
64
64
|
// - input blocks such as `amount(...)`, `allocation(...)`, and `allowance(...)`
|
|
@@ -89,6 +89,7 @@ library Schemas {
|
|
|
89
89
|
|
|
90
90
|
string constant Amount = "bytes32 asset, uint amount";
|
|
91
91
|
string constant Balance = "bytes32 asset, uint amount";
|
|
92
|
+
string constant Debt = "bytes32 liability, uint debt";
|
|
92
93
|
string constant AccountAsset = "bytes32 account, bytes32 asset";
|
|
93
94
|
string constant HostAsset = "uint host, bytes32 asset";
|
|
94
95
|
|
package/codec/Specs.sol
CHANGED
|
@@ -27,6 +27,8 @@ library Sizes {
|
|
|
27
27
|
uint constant Amount = B64;
|
|
28
28
|
/// @dev BALANCE block: 8 header + 32 asset + 32 amount = 72 bytes
|
|
29
29
|
uint constant Balance = B64;
|
|
30
|
+
/// @dev DEBT block: 8 header + 32 liability + 32 debt = 72 bytes
|
|
31
|
+
uint constant Debt = B64;
|
|
30
32
|
/// @dev HOST_ASSET block: 8 header + 32 host + 32 asset = 72 bytes
|
|
31
33
|
uint constant HostAsset = B64;
|
|
32
34
|
/// @dev ALLOCATION/CUSTODY block: 8 header + 32 host + 32 asset + 32 amount = 104 bytes
|
|
@@ -63,6 +65,7 @@ library Specs {
|
|
|
63
65
|
uint constant Empty = uint(bytes32(Keys.Empty));
|
|
64
66
|
uint constant Amount = uint(bytes32(Keys.Amount)) | Exact64;
|
|
65
67
|
uint constant Balance = uint(bytes32(Keys.Balance)) | Exact64;
|
|
68
|
+
uint constant Debt = uint(bytes32(Keys.Debt)) | Exact64;
|
|
66
69
|
uint constant Allocation = uint(bytes32(Keys.Allocation)) | Exact96;
|
|
67
70
|
uint constant Allowance = uint(bytes32(Keys.Allowance)) | Exact96;
|
|
68
71
|
uint constant Custody = uint(bytes32(Keys.Custody)) | Exact96;
|
package/codec/Writers.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
|
-
import {AssetAmount, AccountAmount, HostAmount, Position, Tx} from "../core/Types.sol";
|
|
4
|
+
import {AssetAmount, AccountAmount, HostAmount, Debt, Position, Tx} from "../core/Types.sol";
|
|
5
5
|
import {Blocks} from "./Blocks.sol";
|
|
6
6
|
import {Buffers} from "./Buffers.sol";
|
|
7
7
|
import {Sizes, Specs} from "./Specs.sol";
|
|
@@ -204,6 +204,17 @@ library Writers {
|
|
|
204
204
|
appendBalance(writer, value.asset, value.amount);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
/// @notice Append a DEBT block.
|
|
208
|
+
function appendDebt(Writer memory writer, bytes32 liability, uint debt) internal pure {
|
|
209
|
+
uint i = reserve(writer, Sizes.Debt);
|
|
210
|
+
Blocks.writeDebt(writer.dst, i, liability, debt);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// @notice Append a structured DEBT value.
|
|
214
|
+
function appendDebt(Writer memory writer, Debt memory value) internal pure {
|
|
215
|
+
appendDebt(writer, value.liability, value.debt);
|
|
216
|
+
}
|
|
217
|
+
|
|
207
218
|
/// @notice Append an ACCOUNT_ASSET block.
|
|
208
219
|
/// @param writer Destination writer.
|
|
209
220
|
/// @param account Account identifier to encode.
|
package/commands/Base.sol
CHANGED
|
@@ -99,10 +99,7 @@ abstract contract CommandBase is CallerAccess, EndpointBase, ReceivedEvent {
|
|
|
99
99
|
uint descriptor,
|
|
100
100
|
uint batches
|
|
101
101
|
) internal view returns (Execution memory exec) {
|
|
102
|
-
bytes32 account;
|
|
103
|
-
bytes calldata state;
|
|
104
|
-
bytes calldata input;
|
|
105
|
-
(account, state, input) = unpackCommandContext(context);
|
|
102
|
+
(bytes32 account, bytes calldata state, bytes calldata input) = unpackCommandContext(context);
|
|
106
103
|
exec = Executions.open(state, input, descriptor, batches);
|
|
107
104
|
exec.account = account;
|
|
108
105
|
}
|
package/commands/Debit.sol
CHANGED
|
@@ -67,11 +67,8 @@ abstract contract DebitAccountInternal is DebitAccount {
|
|
|
67
67
|
) internal returns (bytes memory, bytes memory) {
|
|
68
68
|
if (value != 0) revert ValueNotAllowed();
|
|
69
69
|
if (state.length != 0) revert Executions.ZeroStride();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Cur memory cur = Decoders.wrap(input);
|
|
73
|
-
uint count = input.length / Sizes.Amount;
|
|
74
|
-
bytes memory output = new bytes(count * Sizes.Balance);
|
|
70
|
+
Cur memory cur = Decoders.open(input);
|
|
71
|
+
bytes memory output = new bytes(input.length);
|
|
75
72
|
uint i;
|
|
76
73
|
|
|
77
74
|
while (cur.more()) {
|
package/commands/Repay.sol
CHANGED
|
@@ -5,10 +5,13 @@ import {Execution, Executions, CommandBase, Flags, Lanes, Specs} from "./Base.so
|
|
|
5
5
|
import {RepayHook} from "../core/Settlement.sol";
|
|
6
6
|
import {Action} from "../annotations/Action.sol";
|
|
7
7
|
import {Actions} from "../utils/Actions.sol";
|
|
8
|
+
import {Blocks} from "../codec/Blocks.sol";
|
|
9
|
+
import {Reader, Readers} from "../codec/Readers.sol";
|
|
8
10
|
|
|
9
11
|
using Executions for Execution;
|
|
12
|
+
using Readers for Reader;
|
|
10
13
|
|
|
11
|
-
/// @notice Hook implemented by hosts that repay
|
|
14
|
+
/// @notice Hook implemented by hosts that repay liabilities using native value.
|
|
12
15
|
abstract contract RepayPayableHook {
|
|
13
16
|
/// @notice Override to repay one liability for `account` with a shared value budget.
|
|
14
17
|
/// @param account Account whose liability is being repaid.
|
|
@@ -19,21 +22,84 @@ abstract contract RepayPayableHook {
|
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/// @title Repay
|
|
22
|
-
/// @notice Command that
|
|
25
|
+
/// @notice Command that consumes DEBT state by repaying each liability.
|
|
23
26
|
abstract contract Repay is CommandBase, RepayHook, Action {
|
|
24
27
|
uint private immutable descriptor;
|
|
28
|
+
uint private immutable id;
|
|
29
|
+
|
|
30
|
+
constructor() {
|
|
31
|
+
(id, descriptor) = command("repay", Specs.Debt, Specs.Empty, Specs.Empty, 0, 0);
|
|
32
|
+
action(id, Actions.Repay);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @notice Return the registered REPAY command ID.
|
|
36
|
+
function repayId() internal view returns (uint) {
|
|
37
|
+
return id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// @notice Repay each liability in the DEBT state stream.
|
|
41
|
+
/// @param context Command context carrying the DEBT state stream.
|
|
42
|
+
/// @return Empty output state.
|
|
43
|
+
/// @return Empty transaction stream.
|
|
44
|
+
function repay(
|
|
45
|
+
bytes calldata context
|
|
46
|
+
) external onlyCommand returns (bytes memory, bytes memory) {
|
|
47
|
+
Execution memory exec = openCommand(context, descriptor, 0);
|
|
48
|
+
|
|
49
|
+
while (exec.more()) {
|
|
50
|
+
(bytes32 liability, uint debt) = exec.unpackDebt(Lanes.State);
|
|
51
|
+
repay(exec.account, liability, debt);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return closeCommand(exec);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @title RepayPayable
|
|
59
|
+
/// @notice Funded command that consumes DEBT state by repaying each liability.
|
|
60
|
+
abstract contract RepayPayable is CommandBase, RepayPayableHook, Action {
|
|
61
|
+
uint private immutable descriptor;
|
|
25
62
|
|
|
26
63
|
constructor() {
|
|
27
64
|
uint id;
|
|
28
|
-
(id, descriptor) = command("
|
|
29
|
-
action(id, Actions.
|
|
65
|
+
(id, descriptor) = command("repayPayable", Specs.Debt, Specs.Empty, Specs.Empty, 0, Flags.Funded);
|
|
66
|
+
action(id, Actions.Repay);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// @notice Repay each liability in the DEBT state stream.
|
|
70
|
+
/// @param context Command context carrying the DEBT state stream.
|
|
71
|
+
/// @return Empty output state.
|
|
72
|
+
/// @return Remaining native value as a refund transaction stream.
|
|
73
|
+
function repayPayable(
|
|
74
|
+
bytes calldata context
|
|
75
|
+
) external payable onlyCommand returns (bytes memory, bytes memory) {
|
|
76
|
+
Execution memory exec = openCommand(context, descriptor, 0);
|
|
77
|
+
|
|
78
|
+
while (exec.more()) {
|
|
79
|
+
(bytes32 liability, uint debt) = exec.unpackDebt(Lanes.State);
|
|
80
|
+
repay(exec.account, liability, debt, exec);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return closeCommand(exec);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// @title RepayPosition
|
|
88
|
+
/// @notice Command that repays POSITION liabilities and returns their assets as BALANCE state.
|
|
89
|
+
abstract contract RepayPosition is CommandBase, RepayHook, Action {
|
|
90
|
+
uint private immutable descriptor;
|
|
91
|
+
|
|
92
|
+
constructor() {
|
|
93
|
+
uint id;
|
|
94
|
+
(id, descriptor) = command("repayPosition", Specs.Position, Specs.Empty, Specs.Balance, 0, 0);
|
|
95
|
+
action(id, Actions.Repay);
|
|
30
96
|
}
|
|
31
97
|
|
|
32
98
|
/// @notice Repay each POSITION liability and return its asset as BALANCE state.
|
|
33
99
|
/// @param context Command context carrying the POSITION state stream.
|
|
34
100
|
/// @return BALANCE output state containing each released asset side.
|
|
35
101
|
/// @return Empty transaction stream.
|
|
36
|
-
function
|
|
102
|
+
function repayPosition(
|
|
37
103
|
bytes calldata context
|
|
38
104
|
) external onlyCommand returns (bytes memory, bytes memory) {
|
|
39
105
|
Execution memory exec = openCommand(context, descriptor, 0);
|
|
@@ -48,22 +114,24 @@ abstract contract Repay is CommandBase, RepayHook, Action {
|
|
|
48
114
|
}
|
|
49
115
|
}
|
|
50
116
|
|
|
51
|
-
/// @title
|
|
117
|
+
/// @title RepayPositionPayable
|
|
52
118
|
/// @notice Funded command that repays POSITION liabilities and returns their assets as BALANCE state.
|
|
53
|
-
abstract contract
|
|
119
|
+
abstract contract RepayPositionPayable is CommandBase, RepayPayableHook, Action {
|
|
54
120
|
uint private immutable descriptor;
|
|
55
121
|
|
|
56
122
|
constructor() {
|
|
57
123
|
uint id;
|
|
58
|
-
(id, descriptor) = command(
|
|
59
|
-
|
|
124
|
+
(id, descriptor) = command(
|
|
125
|
+
"repayPositionPayable", Specs.Position, Specs.Empty, Specs.Balance, 0, Flags.Funded
|
|
126
|
+
);
|
|
127
|
+
action(id, Actions.Repay);
|
|
60
128
|
}
|
|
61
129
|
|
|
62
130
|
/// @notice Repay each POSITION liability and return its asset as BALANCE state.
|
|
63
131
|
/// @param context Command context carrying the POSITION state stream.
|
|
64
132
|
/// @return BALANCE output state containing each released asset side.
|
|
65
133
|
/// @return Remaining native value as a refund transaction stream.
|
|
66
|
-
function
|
|
134
|
+
function repayPositionPayable(
|
|
67
135
|
bytes calldata context
|
|
68
136
|
) external payable onlyCommand returns (bytes memory, bytes memory) {
|
|
69
137
|
Execution memory exec = openCommand(context, descriptor, 0);
|
|
@@ -77,3 +145,35 @@ abstract contract RepayPayable is CommandBase, RepayPayableHook, Action {
|
|
|
77
145
|
return closeCommand(exec);
|
|
78
146
|
}
|
|
79
147
|
}
|
|
148
|
+
|
|
149
|
+
/// @title RepayInternal
|
|
150
|
+
/// @notice Extends the advertised repay command with memory-state pipeline dispatch.
|
|
151
|
+
/// @dev This adapter is not a separate command. It uses the command ID and repayment hook
|
|
152
|
+
/// inherited from `Repay` while accepting the state location used by `Pipeline`.
|
|
153
|
+
abstract contract RepayInternal is Repay {
|
|
154
|
+
/// @notice Execute the inherited repay command from an internal pipeline.
|
|
155
|
+
/// @param account Account whose liabilities are repaid.
|
|
156
|
+
/// @param state DEBT block stream held in pipeline memory.
|
|
157
|
+
/// @param input Empty input required by the command schema.
|
|
158
|
+
/// @param value Native value assigned to the command; must be zero.
|
|
159
|
+
/// @return output Empty output state.
|
|
160
|
+
/// @return transactions Empty transaction stream.
|
|
161
|
+
function executeRepay(
|
|
162
|
+
bytes32 account,
|
|
163
|
+
bytes memory state,
|
|
164
|
+
bytes calldata input,
|
|
165
|
+
uint128 value
|
|
166
|
+
) internal returns (bytes memory, bytes memory) {
|
|
167
|
+
if (value != 0) revert ValueNotAllowed();
|
|
168
|
+
if (input.length != 0) revert Executions.ZeroStride();
|
|
169
|
+
if (state.length == 0) revert Blocks.EmptyRun();
|
|
170
|
+
|
|
171
|
+
Reader memory reader = Readers.open(state);
|
|
172
|
+
while (reader.more()) {
|
|
173
|
+
(bytes32 liability, uint debt) = reader.unpackDebt();
|
|
174
|
+
repay(account, liability, debt);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return ("", "");
|
|
178
|
+
}
|
|
179
|
+
}
|
package/commands/admin/Base.sol
CHANGED
|
@@ -13,10 +13,7 @@ abstract contract AdminBase is NodeAccess, CommandBase {
|
|
|
13
13
|
uint descriptor,
|
|
14
14
|
uint batches
|
|
15
15
|
) internal view returns (Execution memory exec) {
|
|
16
|
-
bytes32 account;
|
|
17
|
-
bytes calldata state;
|
|
18
|
-
bytes calldata input;
|
|
19
|
-
(account, state, input) = unpackCommandContext(context);
|
|
16
|
+
(bytes32 account, bytes calldata state, bytes calldata input) = unpackCommandContext(context);
|
|
20
17
|
enforceAdmin(account, msg.sender);
|
|
21
18
|
exec = Executions.open(state, input, descriptor, batches);
|
|
22
19
|
exec.account = account;
|
package/core/Access.sol
CHANGED
|
@@ -12,6 +12,14 @@ import {addrOr} from "../utils/Utils.sol";
|
|
|
12
12
|
error AccessDenied();
|
|
13
13
|
error CommanderNotAllowed();
|
|
14
14
|
|
|
15
|
+
/// @notice Assert that `msg.sender` equals `expected` and return it.
|
|
16
|
+
/// @param expected Address required to be the current message sender.
|
|
17
|
+
/// @return The validated sender address.
|
|
18
|
+
function enforceSender(address expected) view returns (address) {
|
|
19
|
+
if (msg.sender != expected) revert AccessDenied();
|
|
20
|
+
return expected;
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
/// @title CallerAccess
|
|
16
24
|
/// @notice Authorization capability required by command entrypoints.
|
|
17
25
|
abstract contract CallerAccess is Runtime {
|
package/core/Pipeline.sol
CHANGED
|
@@ -3,15 +3,25 @@ pragma solidity ^0.8.33;
|
|
|
3
3
|
|
|
4
4
|
import {Decoders, Cur, Readers, Reader} from "../Codec.sol";
|
|
5
5
|
import {PostHook} from "./Settlement.sol";
|
|
6
|
-
import {
|
|
6
|
+
import {Budgets} from "../execution/Budget.sol";
|
|
7
7
|
|
|
8
8
|
using Decoders for Cur;
|
|
9
9
|
using Readers for Reader;
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
/// @notice Hook implemented by hosts that execute encoded step streams.
|
|
12
|
+
abstract contract PipeHook {
|
|
13
|
+
/// @notice Execute a step stream and return its remaining native-value budget.
|
|
14
|
+
function pipe(
|
|
15
|
+
bytes32 account,
|
|
16
|
+
bytes memory state,
|
|
17
|
+
bytes calldata steps,
|
|
18
|
+
uint budget
|
|
19
|
+
) internal virtual returns (uint remaining);
|
|
20
|
+
}
|
|
11
21
|
|
|
12
22
|
/// @title Pipeline
|
|
13
23
|
/// @notice Core pipeline functionality shared by higher-level surfaces.
|
|
14
|
-
abstract contract Pipeline is PostHook {
|
|
24
|
+
abstract contract Pipeline is PipeHook, PostHook {
|
|
15
25
|
/// @dev Thrown when the pipeline finishes with non-empty threaded state.
|
|
16
26
|
error UnexpectedState();
|
|
17
27
|
|
|
@@ -36,18 +46,26 @@ abstract contract Pipeline is PostHook {
|
|
|
36
46
|
|
|
37
47
|
/// @notice Execute a STEP block stream through the pipeline.
|
|
38
48
|
/// @dev Reverts with `UnexpectedState` if the final threaded state is non-empty.
|
|
39
|
-
/// Callers remain responsible for settling
|
|
49
|
+
/// Callers remain responsible for settling the returned unspent value.
|
|
40
50
|
/// @param account Account identifier used for each dispatched step.
|
|
41
51
|
/// @param state Initial state block stream passed to the first step.
|
|
42
52
|
/// @param steps STEP block stream to execute.
|
|
43
|
-
/// @param budget
|
|
44
|
-
|
|
53
|
+
/// @param budget Native-value budget shared across all steps.
|
|
54
|
+
/// @return remaining Native value remaining after every step executes.
|
|
55
|
+
function pipe(
|
|
56
|
+
bytes32 account,
|
|
57
|
+
bytes memory state,
|
|
58
|
+
bytes calldata steps,
|
|
59
|
+
uint budget
|
|
60
|
+
) internal virtual override returns (uint remaining) {
|
|
45
61
|
Cur memory cur = Decoders.open(steps);
|
|
46
62
|
|
|
47
63
|
while (cur.more()) {
|
|
48
64
|
(uint cmd, uint resources, bytes calldata input) = cur.unpackStep();
|
|
65
|
+
uint128 value;
|
|
66
|
+
(budget, value) = Budgets.useResourceValue(budget, resources);
|
|
49
67
|
Reader memory txs;
|
|
50
|
-
(state, txs.source) = dispatch(cmd, account, state, input,
|
|
68
|
+
(state, txs.source) = dispatch(cmd, account, state, input, value);
|
|
51
69
|
|
|
52
70
|
while (txs.more()) {
|
|
53
71
|
(bytes32 from, bytes32 to, bytes32 asset, uint amount) = txs.unpackTransaction();
|
|
@@ -56,5 +74,6 @@ abstract contract Pipeline is PostHook {
|
|
|
56
74
|
}
|
|
57
75
|
|
|
58
76
|
if (state.length != 0) revert UnexpectedState();
|
|
77
|
+
return budget;
|
|
59
78
|
}
|
|
60
79
|
}
|
package/core/Types.sol
CHANGED
|
@@ -67,6 +67,14 @@ struct HostAccountAmount {
|
|
|
67
67
|
uint amount;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/// @notice Liability and debt pair threaded as live pipeline state.
|
|
71
|
+
struct Debt {
|
|
72
|
+
/// @dev Identifier for the liability side.
|
|
73
|
+
bytes32 liability;
|
|
74
|
+
/// @dev Quantity owed on the liability side.
|
|
75
|
+
uint debt;
|
|
76
|
+
}
|
|
77
|
+
|
|
70
78
|
/// @notice Asset and liability pair threaded as live pipeline state.
|
|
71
79
|
struct Position {
|
|
72
80
|
/// @dev Identifier for the asset side.
|
package/execution/Budget.sol
CHANGED
|
@@ -29,6 +29,15 @@ library Budgets {
|
|
|
29
29
|
return value;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/// @notice Deduct an exact native value from a scalar budget.
|
|
33
|
+
/// @param budget Remaining native value in wei.
|
|
34
|
+
/// @param value Native value to consume in wei.
|
|
35
|
+
/// @return remaining Native value remaining after the deduction.
|
|
36
|
+
function useValue(uint budget, uint value) internal pure returns (uint remaining) {
|
|
37
|
+
if (value > budget) revert InsufficientValue();
|
|
38
|
+
remaining = budget - value;
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
/// @notice Deduct the EVM value lane of `resources` from `budget`.
|
|
33
42
|
/// @dev EVM resources use the low 128 bits as native value/endowment.
|
|
34
43
|
/// @param budget Mutable budget to debit.
|
|
@@ -38,6 +47,19 @@ library Budgets {
|
|
|
38
47
|
return uint128(useValue(budget, uint128(resources)));
|
|
39
48
|
}
|
|
40
49
|
|
|
50
|
+
/// @notice Deduct the EVM value lane of `resources` from a scalar budget.
|
|
51
|
+
/// @param budget Remaining native value in wei.
|
|
52
|
+
/// @param resources Packed resources whose low 128 bits contain native value.
|
|
53
|
+
/// @return remaining Native value remaining after the deduction.
|
|
54
|
+
/// @return value Native value consumed from the budget.
|
|
55
|
+
function useResourceValue(
|
|
56
|
+
uint budget,
|
|
57
|
+
uint resources
|
|
58
|
+
) internal pure returns (uint remaining, uint128 value) {
|
|
59
|
+
value = uint128(resources);
|
|
60
|
+
remaining = useValue(budget, value);
|
|
61
|
+
}
|
|
62
|
+
|
|
41
63
|
/// @notice Remove and return all remaining value from `budget`.
|
|
42
64
|
/// @param budget Mutable budget to drain.
|
|
43
65
|
/// @return value Native value removed from the budget.
|
package/execution/Execution.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
|
-
import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Position, Tx} from "../core/Types.sol";
|
|
4
|
+
import {AssetAmount, AccountAsset, HostAsset, AccountAmount, HostAmount, HostAccountAsset, Debt, Position, Tx} from "../core/Types.sol";
|
|
5
5
|
import {Blocks} from "../codec/Blocks.sol";
|
|
6
6
|
import {Buffers} from "../codec/Buffers.sol";
|
|
7
7
|
import {Sizes, Specs} from "../codec/Specs.sol";
|
|
@@ -246,6 +246,20 @@ library Executions {
|
|
|
246
246
|
exec.decoders = cur.seekAbs(end);
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/// @notice Validate a known key and consume the next block from an execution decoder lane.
|
|
250
|
+
/// @dev Validates no payload-size constraint beyond proving the complete block
|
|
251
|
+
/// lies within the selected lane's logical region.
|
|
252
|
+
/// @param exec Execution whose selected decoder cursor is advanced over the complete block.
|
|
253
|
+
/// @param lane Execution decoder lane to select.
|
|
254
|
+
/// @param key Expected block key.
|
|
255
|
+
/// @return abs Absolute position of the first payload byte.
|
|
256
|
+
/// @return end Absolute position immediately after the payload.
|
|
257
|
+
function consume(Execution memory exec, uint8 lane, bytes4 key) internal pure returns (uint abs, uint end) {
|
|
258
|
+
uint cur = exec.decoders.select(lane);
|
|
259
|
+
(abs, end) = Blocks.expectKey(cur.absolute(), key);
|
|
260
|
+
exec.decoders = cur.seekAbs(end);
|
|
261
|
+
}
|
|
262
|
+
|
|
249
263
|
/// @notice Validate and consume one block from a decoder lane, returning its complete encoding.
|
|
250
264
|
/// @param exec Execution whose selected decoder cursor is advanced past the block.
|
|
251
265
|
/// @param lane Execution decoder lane to select.
|
|
@@ -256,7 +270,7 @@ library Executions {
|
|
|
256
270
|
uint8 lane,
|
|
257
271
|
bytes4 key
|
|
258
272
|
) internal pure returns (bytes calldata data) {
|
|
259
|
-
(uint abs, uint end) = consume(exec, lane,
|
|
273
|
+
(uint abs, uint end) = consume(exec, lane, key);
|
|
260
274
|
data = msg.data[abs - Sizes.Header:end];
|
|
261
275
|
}
|
|
262
276
|
|
|
@@ -528,6 +542,18 @@ library Executions {
|
|
|
528
542
|
(value.asset, value.amount) = unpackBalance(exec, lane);
|
|
529
543
|
}
|
|
530
544
|
|
|
545
|
+
/// @notice Decode and consume one DEBT block from `lane`.
|
|
546
|
+
function unpackDebt(Execution memory exec, uint8 lane) internal pure returns (bytes32 liability, uint debt) {
|
|
547
|
+
uint abs;
|
|
548
|
+
(exec.decoders, abs) = exec.decoders.consume(lane, Sizes.Debt);
|
|
549
|
+
(liability, debt) = Blocks.unpackDebt(abs);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/// @notice Decode one DEBT block into its structured value.
|
|
553
|
+
function unpackDebtValue(Execution memory exec, uint8 lane) internal pure returns (Debt memory value) {
|
|
554
|
+
(value.liability, value.debt) = unpackDebt(exec, lane);
|
|
555
|
+
}
|
|
556
|
+
|
|
531
557
|
/// @notice Decode and consume one POSITION block from `lane`.
|
|
532
558
|
function unpackPosition(
|
|
533
559
|
Execution memory exec,
|
|
@@ -990,6 +1016,17 @@ library Executions {
|
|
|
990
1016
|
outputBalance(exec, value.asset, value.amount);
|
|
991
1017
|
}
|
|
992
1018
|
|
|
1019
|
+
/// @notice Append a DEBT block to execution output.
|
|
1020
|
+
function outputDebt(Execution memory exec, bytes32 liability, uint debt) internal pure {
|
|
1021
|
+
uint i = reserve(exec, Sizes.Debt);
|
|
1022
|
+
Blocks.writeDebt(exec.output, i, liability, debt);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/// @notice Append a structured DEBT value to execution output.
|
|
1026
|
+
function outputDebt(Execution memory exec, Debt memory value) internal pure {
|
|
1027
|
+
outputDebt(exec, value.liability, value.debt);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
993
1030
|
/// @notice Append a POSITION block to execution output.
|
|
994
1031
|
function outputPosition(
|
|
995
1032
|
Execution memory exec,
|
|
@@ -1380,13 +1417,20 @@ library Executions {
|
|
|
1380
1417
|
// Value and transaction writing
|
|
1381
1418
|
// -------------------------------------------------------------------------
|
|
1382
1419
|
|
|
1420
|
+
/// @notice Remove and return the remaining execution value budget.
|
|
1421
|
+
/// @param exec Execution whose budget is drained.
|
|
1422
|
+
/// @return budget Native value removed from the execution.
|
|
1423
|
+
function drainBudget(Execution memory exec) internal pure returns (uint budget) {
|
|
1424
|
+
budget = exec.budget;
|
|
1425
|
+
exec.budget = 0;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1383
1428
|
/// @notice Transfer the remaining value budget out of an execution.
|
|
1384
1429
|
/// @dev Clears `exec.budget` so the returned budget becomes its sole owner.
|
|
1385
1430
|
/// @param exec Execution whose budget is detached.
|
|
1386
1431
|
/// @return budget Detached budget containing the remaining value.
|
|
1387
1432
|
function takeBudget(Execution memory exec) internal pure returns (Budget memory budget) {
|
|
1388
|
-
budget.remaining = exec
|
|
1389
|
-
exec.budget = 0;
|
|
1433
|
+
budget.remaining = drainBudget(exec);
|
|
1390
1434
|
}
|
|
1391
1435
|
|
|
1392
1436
|
/// @notice Deduct an exact native value from the execution budget.
|