@crossecute/protocol 0.0.0-stage → 0.1.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.
Files changed (118) hide show
  1. package/README.md +26 -2
  2. package/abi/Blake2b256.json +1 -0
  3. package/abi/Blake2bScheme.json +1 -0
  4. package/abi/ChainRegistry.json +1 -0
  5. package/abi/Commitment.json +1 -0
  6. package/abi/Conduits.json +1 -0
  7. package/abi/DivergentConduits.json +1 -0
  8. package/abi/DivergentTransceiver.json +1 -0
  9. package/abi/DivergentTransceiverExtension.json +1 -0
  10. package/abi/Envelope.json +1 -0
  11. package/abi/Erc7930.json +1 -0
  12. package/abi/Executor.json +1 -0
  13. package/abi/IBootstrapAllowlist.json +1 -0
  14. package/abi/ICancel.json +1 -0
  15. package/abi/IChainRegistryRefs.json +1 -0
  16. package/abi/ICommitFinalize.json +1 -0
  17. package/abi/ICommitmentScheme.json +1 -0
  18. package/abi/IConduitReceiverReport.json +1 -0
  19. package/abi/IConduitTransceiver.json +1 -0
  20. package/abi/IERC7786GatewaySource.json +1 -0
  21. package/abi/IERC7786Recipient.json +1 -0
  22. package/abi/IExecute.json +1 -0
  23. package/abi/ILzOftConduitFactory.json +1 -0
  24. package/abi/IProviderIdTable.json +1 -0
  25. package/abi/IProxy.json +1 -0
  26. package/abi/IReceiverInit.json +1 -0
  27. package/abi/IRefValidator.json +1 -0
  28. package/abi/IReportFloat.json +1 -0
  29. package/abi/ITransmitterInit.json +1 -0
  30. package/abi/IVmDeriver.json +1 -0
  31. package/abi/KeccakScheme.json +1 -0
  32. package/abi/Move.json +1 -0
  33. package/abi/MoveValidator.json +1 -0
  34. package/abi/OpStackTransmitterBase.json +1 -0
  35. package/abi/OutboundBase.json +1 -0
  36. package/abi/OwnableTransmitter.json +1 -0
  37. package/abi/ProviderAddress.json +1 -0
  38. package/abi/ProviderAttribute.json +1 -0
  39. package/abi/ProviderChainId.json +1 -0
  40. package/abi/ProviderReceiver.json +1 -0
  41. package/abi/ProviderTransceiver.json +1 -0
  42. package/abi/Proxy.json +1 -0
  43. package/abi/ProxyDeployer.json +1 -0
  44. package/abi/ProxyInitCode.json +1 -0
  45. package/abi/ReceiverBase.json +1 -0
  46. package/abi/Roles.json +1 -0
  47. package/abi/RolesEnumerable.json +1 -0
  48. package/abi/SchemeFold.json +1 -0
  49. package/abi/Sha256Scheme.json +1 -0
  50. package/abi/StarknetDerive.json +1 -0
  51. package/abi/StarknetValidator.json +1 -0
  52. package/abi/SuiDerive.json +1 -0
  53. package/abi/TransceiverBase.json +1 -0
  54. package/abi/TransceiverCore.json +1 -0
  55. package/abi/TransceiverExtension.json +1 -0
  56. package/abi/TransmitterBase.json +1 -0
  57. package/abi/Treasury.json +1 -0
  58. package/abi/TronConduits.json +1 -0
  59. package/abi/TronTransceiver.json +1 -0
  60. package/abi/TronTransceiverExtension.json +1 -0
  61. package/abi/VmDeriver.json +1 -0
  62. package/abi/ZkSyncConduits.json +1 -0
  63. package/abi/ZkSyncTransceiver.json +1 -0
  64. package/abi/ZkSyncTransceiverExtension.json +1 -0
  65. package/abi/index.d.ts +63 -0
  66. package/abi/index.js +63 -0
  67. package/contracts/addressing/ChainKey.sol +45 -0
  68. package/contracts/addressing/ChainType.sol +50 -0
  69. package/contracts/addressing/Erc7930.sol +209 -0
  70. package/contracts/addressing/Move.sol +212 -0
  71. package/contracts/conduit/Conduits.sol +43 -0
  72. package/contracts/conduit/Proxy.sol +144 -0
  73. package/contracts/conduit/ProxyDeployer.sol +42 -0
  74. package/contracts/conduit/ProxyInitCode.sol +47 -0
  75. package/contracts/derivation/AddressDerive.sol +326 -0
  76. package/contracts/derivation/BitcoinDerive.sol +33 -0
  77. package/contracts/derivation/Blake2b256.sol +122 -0
  78. package/contracts/derivation/IVmDeriver.sol +35 -0
  79. package/contracts/derivation/StarknetDerive.sol +93 -0
  80. package/contracts/derivation/SuiDerive.sol +102 -0
  81. package/contracts/derivation/VmDeriver.sol +291 -0
  82. package/contracts/messaging/Call.sol +43 -0
  83. package/contracts/messaging/Commitment.sol +131 -0
  84. package/contracts/messaging/DeliveryGas.sol +18 -0
  85. package/contracts/messaging/Envelope.sol +123 -0
  86. package/contracts/messaging/Executor.sol +52 -0
  87. package/contracts/messaging/IErc7786.sol +68 -0
  88. package/contracts/messaging/Payload.sol +90 -0
  89. package/contracts/messaging/Roles.sol +113 -0
  90. package/contracts/messaging/inbound/ReceiverBase.sol +359 -0
  91. package/contracts/messaging/outbound/OutboundBase.sol +221 -0
  92. package/contracts/messaging/outbound/OwnableTransmitter.sol +23 -0
  93. package/contracts/messaging/outbound/TransmitterBase.sol +604 -0
  94. package/contracts/messaging/transceiver/DivergentConduits.sol +92 -0
  95. package/contracts/messaging/transceiver/DivergentTransceiver.sol +97 -0
  96. package/contracts/messaging/transceiver/IBootstrapAllowlist.sol +8 -0
  97. package/contracts/messaging/transceiver/TransceiverBase.sol +541 -0
  98. package/contracts/messaging/transceiver/TransceiverCore.sol +307 -0
  99. package/contracts/messaging/transceiver/TransceiverExtension.sol +278 -0
  100. package/contracts/protocols/ProviderAddress.sol +32 -0
  101. package/contracts/protocols/ProviderAttribute.sol +65 -0
  102. package/contracts/protocols/ProviderChainId.sol +115 -0
  103. package/contracts/protocols/ProviderReceiver.sol +24 -0
  104. package/contracts/protocols/ProviderTransceiver.sol +48 -0
  105. package/contracts/protocols/layerzero/ILzOftConduitFactory.sol +10 -0
  106. package/contracts/protocols/op-stack/OpStackTransmitterBase.sol +11 -0
  107. package/contracts/registry/ChainRegistry.sol +667 -0
  108. package/contracts/registry/IChainRegistryRefs.sol +81 -0
  109. package/contracts/registry/ICommitmentScheme.sol +46 -0
  110. package/contracts/registry/IRefValidator.sol +13 -0
  111. package/contracts/registry/Provenance.sol +25 -0
  112. package/contracts/schemes/Blake2bScheme.sol +18 -0
  113. package/contracts/schemes/KeccakScheme.sol +19 -0
  114. package/contracts/schemes/Sha256Scheme.sol +15 -0
  115. package/contracts/treasury/Treasury.sol +76 -0
  116. package/contracts/validators/MoveValidator.sol +24 -0
  117. package/contracts/validators/StarknetValidator.sol +20 -0
  118. package/package.json +36 -4
@@ -0,0 +1,81 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {Provenance} from "./Provenance.sol";
5
+
6
+ /// @notice The CREATE2 inputs a message provider's contracts share on every chain.
7
+ ///
8
+ /// @dev The caller that deploys a transceiver is per (provider, chain), held by the registry: one
9
+ /// salt and caller put a transceiver at one address, and a rotated deployer moves only the
10
+ /// chains added after it. The salt can be mined for leading zero bytes, which are cheaper in
11
+ /// the calldata that names the address.
12
+ struct ProviderDeployment {
13
+ /// The mined salt every deployer passes to `ProxyDeployer.deploy`.
14
+ bytes32 salt;
15
+ /// keccak256 of `Proxy`'s initcode as solc builds it: transceivers and conduits are
16
+ /// both `Proxy`. Recorded rather than computed, since a zkSync or Tron registry's
17
+ /// own compiler builds a different one.
18
+ bytes32 proxyInitCodeHash;
19
+ }
20
+
21
+ /// @notice Whether sends may reach a provider's transceiver on a chain.
22
+ /// @dev `Pending` is recorded and predicted but not sent to; `Live` is set by the registry's owner
23
+ /// once the deployment there was checked independently of the deployer.
24
+ enum Status {
25
+ None,
26
+ Pending,
27
+ Live
28
+ }
29
+
30
+ /// @notice The slice of `ChainRegistry` a transceiver needs: where remote things live, and how
31
+ /// much each claim about them is worth.
32
+ ///
33
+ /// @dev Routes live on the transceiver. What it reads here is provenance, suspension, derived
34
+ /// counterpart addresses, location validation, and which chains must report their
35
+ /// receivers.
36
+ interface IChainRegistryRefs {
37
+ /// @notice What an address claim about `chainKey` is worth. Chain-scoped, so every
38
+ /// provider's transceiver reads the same answer.
39
+ function provenanceFor(bytes32 chainKey) external view returns (Provenance);
40
+
41
+ /// @notice Whether every transceiver on this chain refuses `chainKey`.
42
+ function isSuspended(bytes32 chainKey) external view returns (bool);
43
+
44
+ /// @notice The transceiver address this registry recomputes for `chainKey`, from the
45
+ /// deriver and inputs recorded for that chain. A transceiver stores the result.
46
+ function expectedTransceiver(bytes32 chainKey) external view returns (bytes memory);
47
+
48
+ /// @notice The CREATE2 inputs `messageProvider`'s contracts share; a zero salt means none is
49
+ /// recorded.
50
+ function providerDeployment(bytes32 messageProvider) external view returns (ProviderDeployment memory);
51
+
52
+ /// @notice Where a provider's transceiver lands on `chainKey`, recomputed from the recorded
53
+ /// factory, that chain's deployer, salt, and initcode hash. Reverts for a chain not
54
+ /// graded `Predetermined`, and for one the provider was never added on.
55
+ function predictTransceiver(bytes32 chainKey, bytes32 messageProvider) external view returns (address);
56
+
57
+ /// @notice Whether sends to `messageProvider`'s transceiver on `chainKey` are allowed.
58
+ function isLive(bytes32 chainKey, bytes32 messageProvider) external view returns (bool);
59
+
60
+ /// @notice Who deployed, or will deploy, `messageProvider`'s transceiver on `chainKey`; zero
61
+ /// where it was never added.
62
+ function deployedBy(bytes32 chainKey, bytes32 messageProvider) external view returns (address);
63
+
64
+ /// @notice The deployer a provider added on a chain from now on is recorded under.
65
+ function currentDeployer() external view returns (address);
66
+
67
+ /// @notice The canonical ERC-7930 chain identifier `chainKey` hashes from.
68
+ function chainIdentifier(bytes32 chainKey) external view returns (bytes memory);
69
+
70
+ /// @notice Reverts unless `interop` is a well-formed address on `chainKey`.
71
+ function validateLocation(bytes32 chainKey, bytes calldata interop) external view;
72
+
73
+ /// @notice The derivation inputs recorded for `chainKey`. Hash this to build the
74
+ /// `paramsCommitment` a `resolveCounterpart` transaction must carry.
75
+ function deriveParams(bytes32 chainKey) external view returns (bytes memory);
76
+
77
+ /// @notice Whether conduits on `chainKey` must report their own address home, which is
78
+ /// the only thing this directory says about receivers. Where one actually landed
79
+ /// is held by the transmitter that sends to it.
80
+ function requiresReceiverCallback(bytes32 chainKey) external view returns (bool);
81
+ }
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ /// @title ICommitmentScheme
5
+ /// @notice A chain's commitment hash primitive, for previewing a commitment on this chain.
6
+ ///
7
+ /// @dev The plugin supplies the primitive and `SchemeFold` performs the fold, so a wrong or
8
+ /// hostile plugin can yield a digest the destination refuses but cannot change a
9
+ /// commitment's shape. Nothing on the execution path reads it: a receiver enforces the
10
+ /// fold compiled into `ReceiverBase`.
11
+ ///
12
+ /// @dev `view`, not `pure`: Blake2b-256 calls the EIP-152 precompile through `staticcall`.
13
+ interface ICommitmentScheme {
14
+ /// @notice The destination's hash of `data`.
15
+ /// @dev Must be the primitive the destination's receiver applies, checked against a test
16
+ /// corpus. A mismatch leaves an approval outstanding until a `cancel` crosses.
17
+ function hash(bytes calldata data) external view returns (bytes32);
18
+ }
19
+
20
+ /// @title SchemeFold
21
+ /// @notice The commitment fold over a plugin's primitive.
22
+ /// @dev A second copy of `Commitment.hashCalls`'s fold. They cannot share code: `messaging`
23
+ /// imports `registry`, and the primitive is an enum arm in one and an external call in
24
+ /// the other. `test/CommitmentSchemePlugin.t.sol` pins the two equal for every enum
25
+ /// primitive.
26
+ library SchemeFold {
27
+ /// @dev Same selector as `Commitment.EmptyCommitment`.
28
+ error EmptyCommitment();
29
+
30
+ /// @notice The commitment `scheme`'s chain will require over `elements`.
31
+ /// @dev Seeded with the chainKey, so no plugin can bind a commitment to another chain. An
32
+ /// empty array reverts, as in `Commitment`, before the plugin is called.
33
+ function hashCalls(ICommitmentScheme scheme, bytes32 destinationChainKey, bytes[] memory elements)
34
+ internal
35
+ view
36
+ returns (bytes32 hashed)
37
+ {
38
+ uint256 len = elements.length;
39
+ if (len == 0) revert EmptyCommitment();
40
+ hashed = scheme.hash(abi.encode(destinationChainKey));
41
+ for (uint256 i = 0; i < len; i++) {
42
+ // forge-lint: disable-next-line(calls-loop) an owner-registered scheme
43
+ hashed = scheme.hash(abi.encodePacked(hashed, scheme.hash(elements[i])));
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @title IRefValidator
5
+ /// @notice The registry's per-chain value-range check on an address.
6
+ /// @dev ERC-7930 fixes structure, not ranges: `parseStrict` accepts 32 canonical bytes but
7
+ /// cannot reject a Starknet address above `L2_ADDRESS_UPPER_BOUND`. Set per chainKey by
8
+ /// `ChainRegistry.setValidator`.
9
+ interface IRefValidator {
10
+ /// @param interop Canonical ERC-7930 bytes, already structurally parsed.
11
+ /// @dev Reverts to reject. Called by `ChainRegistry.validateLocation`.
12
+ function validateRef(bytes calldata interop) external view;
13
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ /// @notice How much this chain can know about an address on another one.
5
+ ///
6
+ /// @dev Grades a chain, not a stored value: every provider's transceiver reads the same answer
7
+ /// from `ChainRegistry.provenanceFor`, so two transceivers cannot disagree about one chain.
8
+ ///
9
+ /// @dev Ordered by strength. `Predetermined`: this chain can recompute the address from inputs in a
10
+ /// signed transaction, because that chain's transceivers were deployed through
11
+ /// `ProxyDeployer`, which Arachnid's factory puts at one address, and this chain's
12
+ /// transceiver knows where: from the registry's prediction, the provider's salt under the
13
+ /// deployer recorded for that chain. `Unique`: it cannot, so
14
+ /// the value came over a bridge and is worth that bridge's security. A chain without
15
+ /// Arachnid's factory shares no address with any other, so it is `Unique` in every registry
16
+ /// and grades every other chain `Unique` in its own (#33).
17
+ ///
18
+ /// @dev Checks are ordinal comparisons against a bar, and the registry's grades and a
19
+ /// transceiver's `minCounterpartProvenance` persist in storage. Inserting a grade
20
+ /// renumbers every stored value above it after deployment: append, or migrate.
21
+ enum Provenance {
22
+ Unknown,
23
+ Unique,
24
+ Predetermined
25
+ }
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Blake2b256} from "../derivation/Blake2b256.sol";
5
+ import {ICommitmentScheme} from "../registry/ICommitmentScheme.sol";
6
+
7
+ /// @title Blake2bScheme
8
+ /// @notice Cardano's primitive. `Scheme.Blake2b256Scheme` in the enum the frozen
9
+ /// transmitters carry.
10
+ /// @dev The one that costs `pure`. `Blake2b256` reaches the EIP-152 precompile at 0x09
11
+ /// through `staticcall`, which Solidity forbids inside `pure`, so this member is
12
+ /// why `ICommitmentScheme.hash` is `view`, exactly as it is why the library's
13
+ /// scheme-parameterized overloads are.
14
+ contract Blake2bScheme is ICommitmentScheme {
15
+ function hash(bytes calldata data) external view override returns (bytes32) {
16
+ return Blake2b256.hash(data);
17
+ }
18
+ }
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {ICommitmentScheme} from "../registry/ICommitmentScheme.sol";
5
+
6
+ /// @title KeccakScheme
7
+ /// @notice The primitive every EVM destination uses, plus Solana, Aptos, Sui, NEAR, and
8
+ /// Cosmos. `Scheme.Keccak256` in the enum the frozen transmitters carry.
9
+ ///
10
+ /// @dev It exists to be the control. An EVM chain never needs the plugin path: its
11
+ /// receiver enforces with the keccak fold compiled into `ReceiverBase`. Registering
12
+ /// this one anyway means `ChainRegistry.commitmentFor` can be checked against
13
+ /// `Commitment.hashCalls` for the same inputs, which is the assertion that the
14
+ /// plugin seam reproduces the frozen path rather than merely resembling it.
15
+ contract KeccakScheme is ICommitmentScheme {
16
+ function hash(bytes calldata data) external pure override returns (bytes32) {
17
+ return keccak256(data);
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {ICommitmentScheme} from "../registry/ICommitmentScheme.sol";
5
+
6
+ /// @title Sha256Scheme
7
+ /// @notice TON's primitive. `Scheme.Sha256` in the enum the frozen transmitters carry.
8
+ /// @dev `sha256` is an EVM builtin (precompile 0x02, wrapped by the compiler), so this
9
+ /// stays computable on both sides, which is why TON was expressible from the start
10
+ /// while Starknet was not.
11
+ contract Sha256Scheme is ICommitmentScheme {
12
+ function hash(bytes calldata data) external pure override returns (bytes32) {
13
+ return sha256(data);
14
+ }
15
+ }
@@ -0,0 +1,76 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
5
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
+
8
+ /// @notice A transceiver's report float, which leaves only at its treasury's call.
9
+ interface IReportFloat {
10
+ function withdraw(uint256 amount) external;
11
+ }
12
+
13
+ /// @title Treasury
14
+ /// @notice Where a chain's outbound fees, bootstrap fees and report float land, and the one contract that can
15
+ /// move them out.
16
+ ///
17
+ /// @dev A destination, not an authority: it configures nothing and reaches no conduit. Its one
18
+ /// outgoing call pulls a transceiver's float, which can only come here. Its owner decides
19
+ /// where funds go next; each transceiver's write-once `treasury` decides where they arrive,
20
+ /// so a compromised withdrawal path cannot redirect fees at the source.
21
+ ///
22
+ /// @dev One per chain, shared by every provider there. Each transceiver names it once, at
23
+ /// initialization, so a replacement would receive no fees or floats from transceivers
24
+ /// already deployed: this one serves them for their whole life.
25
+ contract Treasury is Ownable {
26
+ using SafeERC20 for IERC20;
27
+
28
+ /// @notice Native currency left this treasury.
29
+ event NativeWithdrawn(address indexed to, uint256 amount);
30
+
31
+ /// @notice An ERC-20 balance left this treasury.
32
+ event TokenWithdrawn(address indexed token, address indexed to, uint256 amount);
33
+
34
+ /// @notice A transceiver's report float was pulled in.
35
+ event FloatCollected(address indexed from, uint256 amount);
36
+
37
+ /// @dev A withdrawal to the zero address would burn the balance.
38
+ error ZeroRecipient();
39
+
40
+ /// @dev The native transfer failed; carries the recipient's revert reason.
41
+ error NativeTransferFailed(address to, uint256 amount, bytes reason);
42
+
43
+ constructor(address initialOwner) Ownable(initialOwner) {}
44
+
45
+ /// @notice Accept the fees transmitters and transceivers collect with a plain `call`.
46
+ receive() external payable {}
47
+
48
+ /// @notice Pull `amount` of a transceiver's report float into this treasury.
49
+ /// @dev The transceiver pays only its treasury, so the owner chooses when, not where.
50
+ function collect(IReportFloat from, uint256 amount) external onlyOwner {
51
+ emit FloatCollected(address(from), amount);
52
+ from.withdraw(amount);
53
+ }
54
+
55
+ /// @notice Send `amount` of native currency to `to`.
56
+ function withdraw(address to, uint256 amount) external onlyOwner {
57
+ if (to == address(0)) revert ZeroRecipient();
58
+
59
+ // `call`, not `transfer`: a msig recipient needs more than the 2300-gas stipend, and
60
+ // there is no state here for a re-entrant call to confuse.
61
+ emit NativeWithdrawn(to, amount);
62
+
63
+ (bool ok, bytes memory reason) = to.call{value: amount}("");
64
+ if (!ok) revert NativeTransferFailed(to, amount, reason);
65
+ }
66
+
67
+ /// @notice Send `amount` of `token` to `to`.
68
+ /// @dev `SafeERC20` for tokens such as USDT whose `transfer` returns nothing.
69
+ function withdrawERC20(IERC20 token, address to, uint256 amount) external onlyOwner {
70
+ if (to == address(0)) revert ZeroRecipient();
71
+
72
+ token.safeTransfer(to, amount);
73
+
74
+ emit TokenWithdrawn(address(token), to, amount);
75
+ }
76
+ }
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Move} from "../addressing/Move.sol";
5
+ import {Erc7930} from "../addressing/Erc7930.sol";
6
+ import {IRefValidator} from "../registry/IRefValidator.sol";
7
+
8
+ /// @title MoveValidator
9
+ /// @notice Per-chain validator for Move references.
10
+ /// @dev Enforces the fixed 32-byte address width. Move addresses have a short display
11
+ /// form (`0x1` and the zero-padded 32-byte form are the same address, AIP-40),
12
+ /// which has caused real production bugs. Accepting a short form here would give
13
+ /// one address two distinct registry keys, so anything but 32 bytes is rejected.
14
+ contract MoveValidator is IRefValidator {
15
+ function validateRef(bytes calldata interop) external pure override {
16
+ Erc7930.Interop memory io = Erc7930.parseStrict(interop);
17
+ if (!Move.isMoveChain(io.chainType)) revert Move.NotMoveChain();
18
+ // Zero-length address is a Chain Identifier, which has no address to check.
19
+ if (io.addr.length == 0) return;
20
+ if (io.addr.length != Move.MOVE_ADDRESS_BYTES) {
21
+ revert Move.BadMoveAddressLength();
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {StarknetDerive} from "../derivation/StarknetDerive.sol";
5
+ import {Erc7930} from "../addressing/Erc7930.sol";
6
+ import {IRefValidator} from "../registry/IRefValidator.sol";
7
+
8
+ /// @title StarknetValidator
9
+ /// @notice Rejects Starknet references whose address is a well-formed 32-byte value
10
+ /// that no Starknet contract can ever occupy.
11
+ /// @dev This is the constraint the 7930 envelope structurally cannot catch: length is
12
+ /// correct, framing is canonical, and the value is still impossible.
13
+ contract StarknetValidator is IRefValidator {
14
+ function validateRef(bytes calldata interop) external pure override {
15
+ Erc7930.Interop memory io = Erc7930.parseStrict(interop);
16
+ // A zero-length address is a Chain Identifier, which has no felt to bound.
17
+ if (io.addr.length == 0) return;
18
+ StarknetDerive.validateAddress(StarknetDerive.toFelt(io.addr));
19
+ }
20
+ }
package/package.json CHANGED
@@ -1,6 +1,38 @@
1
1
  {
2
2
  "name": "@crossecute/protocol",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Crossecute protocol interfaces, libraries and ABIs for integrators",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/crossecute/protocol.git",
9
+ "directory": "contracts/evm/npm"
10
+ },
11
+ "type": "module",
12
+ "exports": {
13
+ "./abi": {
14
+ "types": "./abi/index.d.ts",
15
+ "default": "./abi/index.js"
16
+ },
17
+ "./abi/*.json": "./abi/*.json",
18
+ "./contracts/*": "./contracts/*"
19
+ },
20
+ "files": [
21
+ "abi",
22
+ "contracts",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "cd .. && forge build && node npm/pack.mjs",
27
+ "prepack": "npm run build"
28
+ },
29
+ "peerDependencies": {
30
+ "@openzeppelin/contracts": "5.4.0",
31
+ "@openzeppelin/contracts-upgradeable": "5.4.0"
32
+ },
33
+ "peerDependenciesMeta": {
34
+ "@openzeppelin/contracts-upgradeable": {
35
+ "optional": true
36
+ }
37
+ }
38
+ }