@rootzero/contracts 1.26.0 → 1.28.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 CHANGED
@@ -8,6 +8,54 @@ sections are immutable and must continue to describe the tagged release.
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.28.0
12
+
13
+ ### Breaking Changes
14
+
15
+ - Expanded STEP's native `value` field from `uint128` to `uint`, increasing an
16
+ empty STEP block from 64 to 80 bytes. Plain native values now use `uint`
17
+ throughout runtime APIs; packed `resources` remain separate `uint` words
18
+ whose EVM value lane is extracted explicitly with `useResourceValue`.
19
+ - Replaced the `CommandCalls` abstraction and its allocating `encodeCommandCall`
20
+ and `callCommand` helpers with the free `rawCommandCall` function. The caller
21
+ now supplies the decoded selector and target after validating and authorizing
22
+ the command, then uses the single-scratch assembly call path.
23
+ - Added the required `ExecuteHook` to `Pipeline` and removed its `dispatch` hook.
24
+ Steps whose command IDs target the current host use `execute`; other commands
25
+ are checked through `TrustAccess` and called directly with `rawCommandCall`.
26
+
27
+ ### Changed
28
+
29
+ - Added the free `unpackCommand` utility for validating a command node and
30
+ extracting its selector and target in one operation. Pipeline steps now use
31
+ it to validate command IDs and select local execution by target address.
32
+ - Simplified `NodeAccess.ensureTrusted` to a single mapping check; an unset zero
33
+ node already resolves to false like every other untrusted node.
34
+
35
+ ## 1.27.0
36
+
37
+ ### Breaking Changes
38
+
39
+ - Removed `BootstrapBudgetHook`. Bootstrap budget contributions now debit the
40
+ account's local native asset through the standard `DebitAccountHook`.
41
+ - Removed the external `bootstrap` endpoint and `BootstrapInternal`. Bootstrap
42
+ is now a pipeline-local command implemented directly by `Bootstrap` while
43
+ retaining its registered command ID and descriptor metadata.
44
+ - Replaced the codec-specific `Executions.ZeroStride` error with global
45
+ `UnexpectedState` and `UnexpectedInput` errors for pipeline-local lane
46
+ violations.
47
+
48
+ ### Changed
49
+
50
+ - Node authorization and revocation now reject foreign-chain and opaque node
51
+ IDs, keeping the trusted-node set local to the host chain.
52
+ - Asserting EVM, admin, and user account helpers now require a nonzero embedded
53
+ address while continuing to return the original account ID.
54
+ - Documented that either side of a position may be absent, using a zero
55
+ identifier and quantity like an omitted transaction endpoint.
56
+ - Bootstrap uses assigned step value before debiting any remaining native-asset
57
+ amount from the account and returns unused value as credit.
58
+
11
59
  ## 1.26.0
12
60
 
13
61
  ### Breaking Changes
package/Core.sol CHANGED
@@ -12,9 +12,9 @@ import { Balances, InsufficientFunds } from "./core/Balances.sol";
12
12
  import { Escrows, InsufficientEscrow } from "./core/Escrows.sol";
13
13
  import { NativeAsset, Runtime } from "./core/Runtime.sol";
14
14
  import { Admins, CommandHost, Guardians, Host, HostIntroduction, IHostIntroduction } from "./core/Host.sol";
15
- import { CommandCalls, FailedCall, NodeCalls, PortCalls, RawNodeCalls } from "./core/Calls.sol";
15
+ import { FailedCall, NodeCalls, PortCalls, RawNodeCalls, rawCommandCall } from "./core/Calls.sol";
16
16
  import { EndpointBase, InputEndpointBase } from "./core/Endpoint.sol";
17
- import { PipeHook, Pipeline } from "./core/Pipeline.sol";
17
+ import { ExecuteHook, PipeHook, Pipeline } from "./core/Pipeline.sol";
18
18
  import { Budget, Budgets } from "./execution/Budget.sol";
19
19
  import { CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook, Settlement } from "./core/Settlement.sol";
20
20
  import { Portal } from "./core/Portal.sol";
package/Endpoints.sol CHANGED
@@ -6,14 +6,14 @@ pragma solidity ^0.8.33;
6
6
 
7
7
  // Shared endpoint hooks
8
8
  import {Flags} from "./codec/Descriptors.sol";
9
- import {PipeHook} from "./core/Pipeline.sol";
9
+ import {ExecuteHook, PipeHook} from "./core/Pipeline.sol";
10
10
  import {CreditAccountHook, DebitAccountHook, PostHook, RepayHook, SettleHook} from "./core/Settlement.sol";
11
11
 
12
12
  // Commands
13
13
  import {CommandBase} from "./commands/Base.sol";
14
14
  import {Allocate, AllocateHook} from "./commands/Allocate.sol";
15
15
  import {Burn, BurnHook} from "./commands/Burn.sol";
16
- import {Bootstrap, BootstrapBudgetHook, BootstrapInternal} from "./commands/Bootstrap.sol";
16
+ import {Bootstrap} from "./commands/Bootstrap.sol";
17
17
  import {Cashout, CashoutHook, CashoutInternal} from "./commands/Cashout.sol";
18
18
  import {CreditAccount, CreditAccountInternal} from "./commands/Credit.sol";
19
19
  import {DebitAccount, DebitAccountInternal} from "./commands/Debit.sol";
package/README.md CHANGED
@@ -276,6 +276,14 @@ next. Balance carries `{ asset, amount }`, debt carries `{ liability, debt }`,
276
276
  and position carries their flat combination
277
277
  `{ asset, amount, liability, debt }`.
278
278
 
279
+ Either side of a position may be absent. An absent asset side is encoded as
280
+ `asset = 0, amount = 0`; an absent liability side is encoded as
281
+ `liability = 0, debt = 0`. This mirrors transaction blocks, where a zero `from`
282
+ or `to` omits that side of the transfer. A one-sided position remains useful
283
+ when a command must preserve position-shaped state for later composition;
284
+ otherwise the narrower `#balance` or `#debt` block expresses the same live
285
+ value more directly.
286
+
279
287
  `#debt` and `#position` are general live state rather than persisted
280
288
  lending-specific debt records. Debt carries value owed or required; position
281
289
  pairs that liability with value acquired or controlled. A command may preserve
@@ -357,7 +365,7 @@ A single command is rarely the whole story. A pipeline is a run of `#step`
357
365
  blocks executed in order within one transaction:
358
366
 
359
367
  ```txt
360
- step { uint cmd, uint128 value, #bytes as input }
368
+ step { uint cmd, uint value, #bytes as input }
361
369
  ```
362
370
 
363
371
  Each step names a command, the native value it may spend, and its input.
@@ -367,23 +375,26 @@ next step, allowing one command to fund later commands. The standard
367
375
  `bootstrap` command consumes a stream of
368
376
  `#bootstrap { bytes32 asset, uint amount, uint budget }` requests and atomically
369
377
  debits each asset through the standard account hook, introduces matching
370
- `#balance` state, and sources summed trusted credit through its dedicated budget
371
- hook. This is the core of
378
+ `#balance` state, and debits each nonzero budget contribution from the account's
379
+ native asset through the same hook. Its pipeline-local implementation uses assigned step value first when
380
+ bootstrapping the native asset, debits any remainder from the account, and
381
+ returns unused assigned value as credit. Bootstrap is registered with command
382
+ metadata but is only executable through local pipeline execution. This is the core of
372
383
  `Pipeline.pipe`:
373
384
 
374
385
  ```solidity
375
386
  while (cur.more()) {
376
- (uint cmd, uint128 value, bytes calldata input) = cur.unpackStep();
387
+ (uint cmd, uint value, bytes calldata input) = cur.unpackStep();
388
+ (bytes4 selector, address target) = unpackCommand(cmd);
377
389
  if (budget < value) revert InsufficientValue();
378
390
  unchecked { budget -= value; }
379
391
  uint credit;
380
- (state, credit) = dispatch(
381
- cmd,
382
- account,
383
- state,
384
- input,
385
- value
386
- );
392
+ if (target == address(this)) {
393
+ (state, credit) = execute(cmd, account, state, input, value);
394
+ } else {
395
+ ensureTrusted(cmd);
396
+ (state, credit) = rawCommandCall(selector, target, value, account, state, input);
397
+ }
387
398
  budget += credit;
388
399
  }
389
400
  if (state.length != 0) revert UnexpectedState();
@@ -396,21 +407,24 @@ settles that final value once.
396
407
  A transfer, for instance, is a two-step pipeline: `debitAccount` turns an
397
408
  `#amount` input into `#balance` state, and `payout` consumes that state
398
409
  toward a recipient. Because a pipeline is just blocks, it is also the unit of
399
- command batching. A step's `uint128 value` is drawn directly from the shared
400
- native-value budget. Transport envelopes retain separate chain-specific
401
- `resources` fields for adapters that also need gas or runtime parameters.
410
+ command batching. A step's plain `uint value` is drawn directly from the shared
411
+ native-value budget. Transport envelopes retain separate opaque, packed
412
+ chain-specific `resources` fields for adapters that also need gas or runtime
413
+ parameters. A `resources` word is never itself native value; EVM adapters use
414
+ `useResourceValue` to extract its low 128-bit value lane before spending it.
402
415
 
403
- Hosts that implement a pipeline locally can inherit `BootstrapInternal`,
416
+ Hosts that implement a pipeline locally can inherit `Bootstrap`,
404
417
  `CashoutInternal`, `DebitAccountInternal`, `CreditAccountInternal`,
405
418
  `SettleInternal`, and
406
- `RepayInternal` to advertise the canonical command endpoints while routing
419
+ `RepayInternal` to register canonical command metadata while routing
407
420
  their local command IDs through `executeBootstrap`, `executeCashout`,
408
421
  `executeDebitAccount`, `executeCreditAccount`, `executeSettle`, and
409
422
  `executeRepay`. The bootstrap, cashout, and debit adapters decode fixed-stride
410
423
  calldata input directly; the other three
411
424
  decode memory-backed pipeline state. All avoid an external self-call. Pass the
412
- step value into each adapter; all six reject nonzero value because the commands
413
- are non-funded.
425
+ step value into each adapter. Bootstrap is pipeline-local rather than an
426
+ externally callable command and may consume value for native-asset balance;
427
+ the other five reject nonzero value because those commands are non-funded.
414
428
 
415
429
  Positions also support backward-composed pipelines. In an exact-output route,
416
430
  the asset side can represent the desired result while the liability side
@@ -497,11 +511,11 @@ names, access sets, balances — from logs alone, with no artifact files.
497
511
  Import from the package entry points rather than deep paths:
498
512
 
499
513
  - `@rootzero/contracts/Core.sol` — `Host`, access control, `Balances`,
500
- `Settlement`, `PipeHook`, `Pipeline`, `Portal`, validator
514
+ `Settlement`, `ExecuteHook`, `PipeHook`, `Pipeline`, `Portal`, validator
501
515
  - `@rootzero/contracts/Commands.sol` — `CommandBase`, `Execution`, `Flags`,
502
516
  codec helpers, and shared value types for authoring custom commands
503
517
  - `@rootzero/contracts/Endpoints.sol` — command, admin, port, guard, and query
504
- mixins, their hooks (including `PipeHook`), and `Flags`
518
+ mixins, their hooks (including `ExecuteHook` and `PipeHook`), and `Flags`
505
519
  - `@rootzero/contracts/Codec.sol` — `Blocks`, calldata `Cur`/`Cursors`, memory
506
520
  `Memory`, `Writers`, `Schemas`, `Descriptors`, `Flags`, `Keys`, and
507
521
  `Specs`
package/Utils.sol CHANGED
@@ -10,7 +10,7 @@ import { Actions } from "./utils/Actions.sol";
10
10
  import { Amounts, Assets } from "./utils/Assets.sol";
11
11
  import { ECDSA } from "./utils/ECDSA.sol";
12
12
  import { Ids } from "./utils/Ids.sol";
13
- import { Nodes } from "./utils/Nodes.sol";
13
+ import { Nodes, unpackCommand } from "./utils/Nodes.sol";
14
14
  import { Layout } from "./utils/Layout.sol";
15
15
  import { BadAmount, InsufficientValue, InvalidAccount, InvalidAsset, InvalidContract, InvalidId, InvalidPreimage, MissingCursor, NotDivisible, OutOfBounds, UnauthorizedAsset, UnconsumedData, UnexpectedPosition, ValueOverflow, ZeroAddress, ZeroAmount} from "./utils/Errors.sol";
16
16
  import { addrOr, applyBps, beforeBps, bytes32ToInt, bytes32ToString, clear8, clear16, clear32, clear64, divisible, ensureAddr, ensureContract, hash32, intToBytes32, isFamily, matchesBase, MAX_BPS, max8, max16, max24, max32, max40, max64, max96, max128, max160, replace8, replace16, replace32, replace64, retryTicket, toLocalBase, toUnspecifiedBase } from "./utils/Utils.sol";
package/codec/Blocks.sol CHANGED
@@ -761,17 +761,17 @@ library Blocks {
761
761
  /// @param cmd Command identifier.
762
762
  /// @param value Native value assigned to the step.
763
763
  /// @param input Command input.
764
- function writeStep(bytes memory dst, uint i, uint cmd, uint128 value, bytes memory input) internal pure {
765
- uint len = 48 + Sizes.Header + input.length;
764
+ function writeStep(bytes memory dst, uint i, uint cmd, uint value, bytes memory input) internal pure {
765
+ uint len = 64 + Sizes.Header + input.length;
766
766
  uint key = uint32(Keys.Step);
767
767
  uint byteskey = uint32(Keys.Bytes);
768
768
  assembly ("memory-safe") {
769
769
  let p := add(add(dst, 0x20), i)
770
770
  mstore(p, or(shl(224, key), shl(192, len)))
771
771
  mstore(add(p, 0x08), cmd)
772
- mstore(add(p, 0x28), shl(128, value))
772
+ mstore(add(p, 0x28), value)
773
773
 
774
- let q := add(p, 0x38)
774
+ let q := add(p, 0x48)
775
775
  let inputlen := mload(input)
776
776
  mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
777
777
  mcopy(add(q, 0x08), add(input, 0x20), inputlen)
@@ -1031,17 +1031,17 @@ library Blocks {
1031
1031
  }
1032
1032
 
1033
1033
  /// @notice Encode a STEP block at `i`, copying its nested input from calldata.
1034
- function copyStep(bytes memory dst, uint i, uint cmd, uint128 value, bytes calldata input) internal pure {
1035
- uint len = max32(48 + Sizes.Header + input.length);
1034
+ function copyStep(bytes memory dst, uint i, uint cmd, uint value, bytes calldata input) internal pure {
1035
+ uint len = max32(64 + Sizes.Header + input.length);
1036
1036
  uint key = uint32(Keys.Step);
1037
1037
  uint byteskey = uint32(Keys.Bytes);
1038
1038
  assembly ("memory-safe") {
1039
1039
  let p := add(add(dst, 0x20), i)
1040
1040
  mstore(p, or(shl(224, key), shl(192, len)))
1041
1041
  mstore(add(p, 0x08), cmd)
1042
- mstore(add(p, 0x28), shl(128, value))
1042
+ mstore(add(p, 0x28), value)
1043
1043
 
1044
- let q := add(p, 0x38)
1044
+ let q := add(p, 0x48)
1045
1045
  let inputlen := input.length
1046
1046
  mstore(q, or(shl(224, byteskey), shl(192, inputlen)))
1047
1047
  calldatacopy(add(q, 0x08), input.offset, inputlen)
@@ -1836,14 +1836,14 @@ library Blocks {
1836
1836
  /// @return end Absolute position after the block.
1837
1837
  function unpackStep(
1838
1838
  uint abs
1839
- ) internal pure returns (uint cmd, uint128 value, bytes calldata input, uint end) {
1839
+ ) internal pure returns (uint cmd, uint value, bytes calldata input, uint end) {
1840
1840
  uint limit;
1841
1841
  (abs, limit) = expectKey(abs, Keys.Step);
1842
1842
  assembly ("memory-safe") {
1843
1843
  cmd := calldataload(abs)
1844
- value := shr(128, calldataload(add(abs, 0x20)))
1844
+ value := calldataload(add(abs, 0x20))
1845
1845
  }
1846
- (input, end) = unpackBytes(abs + 48);
1846
+ (input, end) = unpackBytes(abs + 64);
1847
1847
  if (end != limit) revert InvalidBlock();
1848
1848
  }
1849
1849
 
@@ -2203,14 +2203,14 @@ library Blocks {
2203
2203
  /// @param value Native value assigned to the step.
2204
2204
  /// @param input Raw nested input payload.
2205
2205
  /// @return encoded Encoded STEP block bytes.
2206
- function createStep(uint cmd, uint128 value, bytes memory input) internal pure returns (bytes memory encoded) {
2206
+ function createStep(uint cmd, uint value, bytes memory input) internal pure returns (bytes memory encoded) {
2207
2207
  uint len = max32(Sizes.Step + input.length);
2208
2208
  encoded = allocate(len);
2209
2209
  writeStep(encoded, 0, cmd, value, input);
2210
2210
  }
2211
2211
 
2212
2212
  /// @notice Encode a STEP block by copying its nested input from calldata.
2213
- function createStepCopy(uint cmd, uint128 value, bytes calldata input) internal pure returns (bytes memory encoded) {
2213
+ function createStepCopy(uint cmd, uint value, bytes calldata input) internal pure returns (bytes memory encoded) {
2214
2214
  uint len = max32(Sizes.Step + input.length);
2215
2215
  encoded = allocate(len);
2216
2216
  copyStep(encoded, 0, cmd, value, input);
@@ -740,7 +740,7 @@ library Decoders {
740
740
  /// @return input Decoded command input.
741
741
  function unpackStep(
742
742
  Cur memory cur
743
- ) internal pure returns (uint cmd, uint128 value, bytes calldata input) {
743
+ ) internal pure returns (uint cmd, uint value, bytes calldata input) {
744
744
  uint abs = cur.state.absolute();
745
745
  uint end;
746
746
  (cmd, value, input, end) = Blocks.unpackStep(abs);
package/codec/Keys.sol CHANGED
@@ -39,7 +39,7 @@ library Keys {
39
39
  bytes4 constant Account = bytes4(keccak256("#account"));
40
40
  /// @dev Transfer record passed through the pipeline - (bytes32 from, bytes32 to, bytes32 asset, uint amount)
41
41
  bytes4 constant Transaction = bytes4(keccak256("#transaction"));
42
- /// @dev Sub-command invocation - (uint cmd, uint128 value, #bytes as input)
42
+ /// @dev Sub-command invocation - (uint cmd, uint value, #bytes as input)
43
43
  bytes4 constant Step = bytes4(keccak256("#step"));
44
44
  /// @dev Portal relay input - (uint portal, uint resources, #bytes as input)
45
45
  bytes4 constant Relay = bytes4(keccak256("#relay"));
package/codec/Schema.sol CHANGED
@@ -29,10 +29,11 @@ pragma solidity ^0.8.33;
29
29
  // - `portal` fields identify destination portal hosts. By convention the value
30
30
  // is the portal implementation's host ID; core passes it through unchanged
31
31
  // and hooks may validate or resolve it for their transport
32
- // - `resources` fields are chain-specific resource words. A portal adapter
33
- // interprets them for the destination runtime. EVM resources use the low
34
- // 128 bits as native value.
35
- // - STEP encodes native `value` directly as uint128; it does not carry a
32
+ // - `resources` fields are opaque chain-specific packed words, not native
33
+ // values. A portal adapter interprets them for the destination runtime. EVM
34
+ // resources use the low 128 bits as native value, extracted explicitly with
35
+ // `useResourceValue` before spending.
36
+ // - STEP encodes native `value` directly as uint; it does not carry a
36
37
  // chain-specific resources word
37
38
  // - dotted field names and aliases, e.g. `dst.portal` or `#bytes as dst.payload`,
38
39
  // are offchain projection metadata only and do not change runtime encoding
@@ -60,6 +61,8 @@ pragma solidity ^0.8.33;
60
61
  // - while a balance, debt, or custody is in-flight as pipeline state, it is not simultaneously persisted
61
62
  // in another ledger/store by this protocol
62
63
  // - debt carries only a live liability side; position pairs live balance and debt sides
64
+ // - either position side may be absent by setting both its identifier and quantity to zero,
65
+ // analogous to omitting a transaction side with a zero `from` or `to`
63
66
  // - debt and position state are transient and do not themselves create or erase an externally persisted obligation
64
67
  // - positions support backward composition, but pipeline steps always execute in encoded order
65
68
  // - commands must preserve, transform, settle, or intentionally consume pipeline state
@@ -114,7 +117,7 @@ library Schemas {
114
117
 
115
118
  // Composite payloads
116
119
 
117
- string constant Step = "uint cmd, uint128 value, #bytes as input";
120
+ string constant Step = "uint cmd, uint value, #bytes as input";
118
121
  string constant Call = "uint target, uint resources, #bytes as payload";
119
122
  string constant Relay = "uint portal, uint resources, #bytes as input";
120
123
  string constant Dispatch = "uint portal, uint resources, #bytes as payload";
package/codec/Specs.sol CHANGED
@@ -21,8 +21,8 @@ library Sizes {
21
21
  uint constant B128 = Header + 4 * Word;
22
22
  /// @dev 8 header + 160 payload = 168 bytes total.
23
23
  uint constant B160 = Header + 5 * Word;
24
- /// @dev Minimum STEP size: 8 header + 32 command + 16 value + 8 nested BYTES header.
25
- uint constant Step = 2 * Header + Word + 16;
24
+ /// @dev Minimum STEP size: 8 header + 32 command + 32 value + 8 nested BYTES header.
25
+ uint constant Step = 2 * Header + 2 * Word;
26
26
  /// @dev STATUS block: 8 header + 32 status code = 40 bytes
27
27
  uint constant Status = B32;
28
28
  /// @dev CASHOUT block: 8 header + 32 native-asset amount = 40 bytes
@@ -64,7 +64,6 @@ library Specs {
64
64
  uint private constant Exact128 = 128 * SizeFields;
65
65
  uint private constant UnboundedHint128 = uint(128) << 136;
66
66
  uint private constant UnboundedMin40Hint256 = (uint(40) << 192) | (uint(256) << 136);
67
- uint private constant UnboundedMin56Hint256 = (uint(56) << 192) | (uint(256) << 136);
68
67
  uint private constant UnboundedMin72Hint256 = (uint(72) << 192) | (uint(256) << 136);
69
68
  uint private constant UnboundedMin48Hint512 = (uint(48) << 192) | (uint(512) << 136);
70
69
  uint private constant UnboundedMin104Hint256 = (uint(104) << 192) | (uint(256) << 136);
@@ -85,7 +84,7 @@ library Specs {
85
84
  uint constant String = uint(bytes32(Keys.String)) | UnboundedHint128;
86
85
  uint constant Account = uint(bytes32(Keys.Account)) | Exact32;
87
86
  uint constant Transaction = uint(bytes32(Keys.Transaction)) | Exact128;
88
- uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin56Hint256;
87
+ uint constant Step = uint(bytes32(Keys.Step)) | UnboundedMin72Hint256;
89
88
  uint constant Relay = uint(bytes32(Keys.Relay)) | UnboundedMin72Hint256;
90
89
  uint constant Context = uint(bytes32(Keys.Context)) | UnboundedMin48Hint512;
91
90
  uint constant Recover = uint(bytes32(Keys.Recover)) | UnboundedMin104Hint256;
package/codec/Writers.sol CHANGED
@@ -414,7 +414,7 @@ library Writers {
414
414
  /// @param cmd Command identifier to encode.
415
415
  /// @param value Native value to encode.
416
416
  /// @param input Command input to encode.
417
- function appendStep(Writer memory writer, uint cmd, uint128 value, bytes memory input) internal pure {
417
+ function appendStep(Writer memory writer, uint cmd, uint value, bytes memory input) internal pure {
418
418
  uint size = Sizes.Step + input.length;
419
419
  uint i = reserve(writer, size);
420
420
  Blocks.writeStep(writer.dst, i, cmd, value, input);
@@ -561,7 +561,7 @@ library Writers {
561
561
  }
562
562
 
563
563
  /// @notice Append a STEP block by copying its nested input from calldata.
564
- function copyStep(Writer memory writer, uint cmd, uint128 value, bytes calldata input) internal pure {
564
+ function copyStep(Writer memory writer, uint cmd, uint value, bytes calldata input) internal pure {
565
565
  uint size = Sizes.Step + input.length;
566
566
  uint i = reserve(writer, size);
567
567
  Blocks.copyStep(writer.dst, i, cmd, value, input);
@@ -1,33 +1,20 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-only
2
2
  pragma solidity ^0.8.33;
3
3
 
4
- import {Execution, Executions, CommandBase, Specs} from "./Base.sol";
4
+ import {CommandBase, Specs} from "./Base.sol";
5
5
  import {Blocks} from "../codec/Blocks.sol";
6
6
  import {Sizes} from "../codec/Specs.sol";
7
7
  import {Cursors} from "../utils/Cursors.sol";
8
8
  import {DebitAccountHook} from "../core/Settlement.sol";
9
-
10
- using Executions for Execution;
11
-
12
- /// @notice Hook implemented by hosts that source bootstrap pipeline budget.
13
- abstract contract BootstrapBudgetHook {
14
- /// @notice Source native-value budget for `account` during bootstrap.
15
- /// Called once per BOOTSTRAP input block.
16
- /// @dev Implementations must revert unless the contribution can be made available.
17
- /// A zero amount is valid and may return immediately without changing state.
18
- /// @param account Account funding the pipeline.
19
- /// @param amount Native value added to the pipeline budget.
20
- function bootstrapBudget(bytes32 account, uint amount) internal virtual;
21
- }
9
+ import {UnexpectedState} from "../utils/Errors.sol";
22
10
 
23
11
  /// @title Bootstrap
24
- /// @notice Command that atomically starts a pipeline with BALANCE state and native-value budget.
25
- abstract contract Bootstrap is CommandBase, DebitAccountHook, BootstrapBudgetHook {
26
- uint private immutable descriptor;
12
+ /// @notice Pipeline-local command that atomically starts with BALANCE state and native-value budget.
13
+ abstract contract Bootstrap is CommandBase, DebitAccountHook {
27
14
  uint private immutable id;
28
15
 
29
16
  constructor() {
30
- (id, descriptor) = command("bootstrap", Specs.Empty, Specs.Bootstrap, Specs.Balance, 0);
17
+ (id,) = command("bootstrap", Specs.Empty, Specs.Bootstrap, Specs.Balance, 0);
31
18
  }
32
19
 
33
20
  /// @notice Return the registered BOOTSTRAP command ID.
@@ -35,57 +22,56 @@ abstract contract Bootstrap is CommandBase, DebitAccountHook, BootstrapBudgetHoo
35
22
  return id;
36
23
  }
37
24
 
38
- /// @notice Source initial balances and budget contributions for a pipeline.
39
- /// @param context Command context carrying a BOOTSTRAP input stream.
40
- /// @return output One BALANCE block per BOOTSTRAP input.
41
- /// @return credit Sum of trusted native value contributed by every input.
25
+ /// @dev Bootstrap one local balance, using assigned value before debiting
26
+ /// any remaining native-asset amount from the account.
42
27
  function bootstrap(
43
- bytes calldata context
44
- ) external onlyCommand returns (bytes memory output, uint credit) {
45
- Execution memory exec = openCommand(context, descriptor);
46
-
47
- while (exec.more()) {
48
- (bytes32 asset, uint amount, uint budget) = exec.unpackBootstrap();
49
- debitAccount(exec.account, asset, amount);
50
- bootstrapBudget(exec.account, budget);
51
- exec.outputBalance(asset, amount);
52
- credit += budget;
28
+ bytes32 account,
29
+ bytes32 asset,
30
+ uint amount,
31
+ uint budget,
32
+ uint value
33
+ ) private returns (uint) {
34
+ if (asset == nativeAsset) {
35
+ uint funded = amount < value ? amount : value;
36
+ unchecked {
37
+ amount -= funded;
38
+ value -= funded;
39
+ }
40
+ amount += budget;
41
+ } else {
42
+ if (amount != 0) debitAccount(account, asset, amount);
43
+ amount = budget;
53
44
  }
54
45
 
55
- return exec.close(credit);
46
+ if (amount != 0) debitAccount(account, nativeAsset, amount);
47
+ return value + budget;
56
48
  }
57
- }
58
49
 
59
- /// @title BootstrapInternal
60
- /// @notice Extends bootstrap with optimized local pipeline dispatch.
61
- abstract contract BootstrapInternal is Bootstrap {
62
50
  /// @notice Execute bootstrap directly against a calldata BOOTSTRAP stream.
63
51
  /// @param account Account funding the pipeline.
64
52
  /// @param state Empty pipeline state required by the command schema.
65
53
  /// @param input BOOTSTRAP block stream.
66
- /// @param value Native value assigned to this command; must be zero.
54
+ /// @param value Native value available to fund native-asset balances.
67
55
  /// @return output One BALANCE block per BOOTSTRAP input.
68
- /// @return credit Sum of the sourced budget contributions.
56
+ /// @return credit Sourced budget contributions plus unused assigned value.
69
57
  function executeBootstrap(
70
58
  bytes32 account,
71
59
  bytes memory state,
72
60
  bytes calldata input,
73
- uint128 value
61
+ uint value
74
62
  ) internal returns (bytes memory output, uint credit) {
75
- if (value != 0) revert ValueNotAllowed();
76
- if (state.length != 0) revert Executions.ZeroStride();
63
+ if (state.length != 0) revert UnexpectedState();
77
64
  if (input.length % Sizes.Bootstrap != 0) revert Blocks.InvalidBlock();
78
65
 
79
66
  (uint abs, uint end) = Cursors.bounds(input);
80
67
  output = new bytes(input.length / Sizes.Bootstrap * Sizes.Balance);
68
+ credit = value;
81
69
  uint i;
82
70
 
83
71
  while (abs < end) {
84
72
  (bytes32 asset, uint amount, uint budget) = Blocks.unpackBootstrap(abs);
85
- debitAccount(account, asset, amount);
86
- bootstrapBudget(account, budget);
73
+ credit = bootstrap(account, asset, amount, budget, credit);
87
74
  Blocks.writeBalance(output, i, asset, amount);
88
- credit += budget;
89
75
  unchecked {
90
76
  abs += Sizes.Bootstrap;
91
77
  i += Sizes.Balance;
@@ -7,6 +7,7 @@ import {Sizes} from "../codec/Specs.sol";
7
7
  import {Cursors} from "../utils/Cursors.sol";
8
8
  import {Action} from "../annotations/Action.sol";
9
9
  import {Actions} from "../utils/Actions.sol";
10
+ import {UnexpectedState} from "../utils/Errors.sol";
10
11
 
11
12
  using Executions for Execution;
12
13
 
@@ -67,10 +68,10 @@ abstract contract CashoutInternal is Cashout {
67
68
  bytes32 account,
68
69
  bytes memory state,
69
70
  bytes calldata input,
70
- uint128 value
71
+ uint value
71
72
  ) internal returns (bytes memory, uint) {
72
73
  if (value != 0) revert ValueNotAllowed();
73
- if (state.length != 0) revert Executions.ZeroStride();
74
+ if (state.length != 0) revert UnexpectedState();
74
75
  if (input.length % Sizes.Cashout != 0) revert Blocks.InvalidBlock();
75
76
 
76
77
  (uint abs, uint end) = Cursors.bounds(input);
@@ -5,6 +5,7 @@ import {Execution, Executions, CommandBase, Specs} from "./Base.sol";
5
5
  import {CreditAccountHook} from "../core/Settlement.sol";
6
6
  import {Blocks, Memory} from "../codec/Blocks.sol";
7
7
  import {Sizes} from "../codec/Specs.sol";
8
+ import {UnexpectedInput} from "../utils/Errors.sol";
8
9
 
9
10
  using Executions for Execution;
10
11
 
@@ -58,10 +59,10 @@ abstract contract CreditAccountInternal is CreditAccount {
58
59
  bytes32 account,
59
60
  bytes memory state,
60
61
  bytes calldata input,
61
- uint128 value
62
+ uint value
62
63
  ) internal returns (bytes memory, uint) {
63
64
  if (value != 0) revert ValueNotAllowed();
64
- if (input.length != 0) revert Executions.ZeroStride();
65
+ if (input.length != 0) revert UnexpectedInput();
65
66
  if (state.length == 0) revert Blocks.EmptyRun();
66
67
 
67
68
  (uint abs, uint end) = Memory.bounds(state, Sizes.Balance);
@@ -6,6 +6,7 @@ import {DebitAccountHook} from "../core/Settlement.sol";
6
6
  import {Blocks} from "../codec/Blocks.sol";
7
7
  import {Sizes} from "../codec/Specs.sol";
8
8
  import {Cursors} from "../utils/Cursors.sol";
9
+ import {UnexpectedState} from "../utils/Errors.sol";
9
10
 
10
11
  using Executions for Execution;
11
12
 
@@ -61,10 +62,10 @@ abstract contract DebitAccountInternal is DebitAccount {
61
62
  bytes32 account,
62
63
  bytes memory state,
63
64
  bytes calldata input,
64
- uint128 value
65
+ uint value
65
66
  ) internal returns (bytes memory, uint) {
66
67
  if (value != 0) revert ValueNotAllowed();
67
- if (state.length != 0) revert Executions.ZeroStride();
68
+ if (state.length != 0) revert UnexpectedState();
68
69
  if (input.length == 0) revert Blocks.EmptyRun();
69
70
  if (input.length % Sizes.Amount != 0) revert Blocks.InvalidBlock();
70
71
 
@@ -9,7 +9,8 @@ using Executions for Execution;
9
9
  abstract contract RecoverPayableHook {
10
10
  /// @notice Override to recover a witness through `handler`.
11
11
  /// @param handler Port that should attempt recovery.
12
- /// @param resources Chain-specific resources assigned to the recovery attempt.
12
+ /// @param resources Opaque packed chain-specific resources, not plain native
13
+ /// value. EVM handlers extract the low 128-bit value lane with `useResourceValue`.
13
14
  /// @param key Recovery lookup key.
14
15
  /// @param witness Witness payload used to prove and replay recovery.
15
16
  /// @param funds Shared execution containing the source value budget.
@@ -13,8 +13,9 @@ abstract contract RelayPayableHook {
13
13
  /// the source state is consumed before destination success is known.
14
14
  /// @param portal Destination portal implementation's host ID. Implementations
15
15
  /// may validate or resolve it for their transport.
16
- /// @param resources Chain-specific destination resources. EVM adapters
17
- /// may interpret this as packed execution gas and destination value.
16
+ /// @param resources Opaque packed chain-specific destination resources, not
17
+ /// plain native value. EVM adapters extract the low 128-bit value lane with
18
+ /// `useResourceValue`; higher bits may encode execution gas or other data.
18
19
  /// @param account Destination command account.
19
20
  /// @param state Complete state forwarded into the destination context.
20
21
  /// @param input Input forwarded into the destination context.
@@ -7,6 +7,7 @@ import {Action} from "../annotations/Action.sol";
7
7
  import {Actions} from "../utils/Actions.sol";
8
8
  import {Blocks, Memory} from "../codec/Blocks.sol";
9
9
  import {Sizes} from "../codec/Specs.sol";
10
+ import {UnexpectedInput} from "../utils/Errors.sol";
10
11
 
11
12
  using Executions for Execution;
12
13
 
@@ -161,10 +162,10 @@ abstract contract RepayInternal is Repay {
161
162
  bytes32 account,
162
163
  bytes memory state,
163
164
  bytes calldata input,
164
- uint128 value
165
+ uint value
165
166
  ) internal returns (bytes memory, uint) {
166
167
  if (value != 0) revert ValueNotAllowed();
167
- if (input.length != 0) revert Executions.ZeroStride();
168
+ if (input.length != 0) revert UnexpectedInput();
168
169
  if (state.length == 0) revert Blocks.EmptyRun();
169
170
 
170
171
  (uint abs, uint end) = Memory.bounds(state, Sizes.Debt);