@crossecute/protocol 0.0.0 → 0.1.1
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/README.md +25 -1
- package/abi/Blake2b256.json +1 -0
- package/abi/Blake2bScheme.json +1 -0
- package/abi/ChainRegistry.json +1 -0
- package/abi/Commitment.json +1 -0
- package/abi/Conduits.json +1 -0
- package/abi/DivergentConduits.json +1 -0
- package/abi/DivergentTransceiver.json +1 -0
- package/abi/DivergentTransceiverExtension.json +1 -0
- package/abi/Envelope.json +1 -0
- package/abi/Erc7930.json +1 -0
- package/abi/Executor.json +1 -0
- package/abi/IBootstrapAllowlist.json +1 -0
- package/abi/ICancel.json +1 -0
- package/abi/IChainRegistryRefs.json +1 -0
- package/abi/ICommitFinalize.json +1 -0
- package/abi/ICommitmentScheme.json +1 -0
- package/abi/IConduitReceiverReport.json +1 -0
- package/abi/IConduitTransceiver.json +1 -0
- package/abi/IERC7786GatewaySource.json +1 -0
- package/abi/IERC7786Recipient.json +1 -0
- package/abi/IExecute.json +1 -0
- package/abi/ILzOftConduitFactory.json +1 -0
- package/abi/IProviderIdTable.json +1 -0
- package/abi/IProxy.json +1 -0
- package/abi/IReceiverInit.json +1 -0
- package/abi/IRefValidator.json +1 -0
- package/abi/IReportFloat.json +1 -0
- package/abi/ITransmitterInit.json +1 -0
- package/abi/IVmDeriver.json +1 -0
- package/abi/KeccakScheme.json +1 -0
- package/abi/Move.json +1 -0
- package/abi/MoveValidator.json +1 -0
- package/abi/OpStackTransmitterBase.json +1 -0
- package/abi/OutboundBase.json +1 -0
- package/abi/OwnableTransmitter.json +1 -0
- package/abi/ProviderAddress.json +1 -0
- package/abi/ProviderAttribute.json +1 -0
- package/abi/ProviderChainId.json +1 -0
- package/abi/ProviderReceiver.json +1 -0
- package/abi/ProviderTransceiver.json +1 -0
- package/abi/Proxy.json +1 -0
- package/abi/ProxyDeployer.json +1 -0
- package/abi/ProxyInitCode.json +1 -0
- package/abi/ReceiverBase.json +1 -0
- package/abi/Roles.json +1 -0
- package/abi/RolesEnumerable.json +1 -0
- package/abi/SchemeFold.json +1 -0
- package/abi/Sha256Scheme.json +1 -0
- package/abi/StarknetDerive.json +1 -0
- package/abi/StarknetValidator.json +1 -0
- package/abi/SuiDerive.json +1 -0
- package/abi/TransceiverBase.json +1 -0
- package/abi/TransceiverCore.json +1 -0
- package/abi/TransceiverExtension.json +1 -0
- package/abi/TransmitterBase.json +1 -0
- package/abi/Treasury.json +1 -0
- package/abi/TronConduits.json +1 -0
- package/abi/TronTransceiver.json +1 -0
- package/abi/TronTransceiverExtension.json +1 -0
- package/abi/VmDeriver.json +1 -0
- package/abi/ZkSyncConduits.json +1 -0
- package/abi/ZkSyncTransceiver.json +1 -0
- package/abi/ZkSyncTransceiverExtension.json +1 -0
- package/abi/index.d.ts +63 -0
- package/abi/index.js +63 -0
- package/contracts/addressing/ChainKey.sol +45 -0
- package/contracts/addressing/ChainType.sol +50 -0
- package/contracts/addressing/Erc7930.sol +209 -0
- package/contracts/addressing/Move.sol +212 -0
- package/contracts/conduit/Conduits.sol +43 -0
- package/contracts/conduit/Proxy.sol +144 -0
- package/contracts/conduit/ProxyDeployer.sol +42 -0
- package/contracts/conduit/ProxyInitCode.sol +47 -0
- package/contracts/derivation/AddressDerive.sol +326 -0
- package/contracts/derivation/BitcoinDerive.sol +33 -0
- package/contracts/derivation/Blake2b256.sol +122 -0
- package/contracts/derivation/IVmDeriver.sol +35 -0
- package/contracts/derivation/StarknetDerive.sol +93 -0
- package/contracts/derivation/SuiDerive.sol +102 -0
- package/contracts/derivation/VmDeriver.sol +291 -0
- package/contracts/messaging/Call.sol +43 -0
- package/contracts/messaging/Commitment.sol +131 -0
- package/contracts/messaging/DeliveryGas.sol +18 -0
- package/contracts/messaging/Envelope.sol +123 -0
- package/contracts/messaging/Executor.sol +52 -0
- package/contracts/messaging/IErc7786.sol +68 -0
- package/contracts/messaging/Payload.sol +90 -0
- package/contracts/messaging/Roles.sol +113 -0
- package/contracts/messaging/inbound/ReceiverBase.sol +359 -0
- package/contracts/messaging/outbound/OutboundBase.sol +221 -0
- package/contracts/messaging/outbound/OwnableTransmitter.sol +23 -0
- package/contracts/messaging/outbound/TransmitterBase.sol +604 -0
- package/contracts/messaging/transceiver/DivergentConduits.sol +92 -0
- package/contracts/messaging/transceiver/DivergentTransceiver.sol +97 -0
- package/contracts/messaging/transceiver/IBootstrapAllowlist.sol +8 -0
- package/contracts/messaging/transceiver/TransceiverBase.sol +541 -0
- package/contracts/messaging/transceiver/TransceiverCore.sol +307 -0
- package/contracts/messaging/transceiver/TransceiverExtension.sol +278 -0
- package/contracts/protocols/ProviderAddress.sol +32 -0
- package/contracts/protocols/ProviderAttribute.sol +65 -0
- package/contracts/protocols/ProviderChainId.sol +115 -0
- package/contracts/protocols/ProviderReceiver.sol +24 -0
- package/contracts/protocols/ProviderTransceiver.sol +48 -0
- package/contracts/protocols/layerzero/ILzOftConduitFactory.sol +10 -0
- package/contracts/protocols/op-stack/OpStackTransmitterBase.sol +11 -0
- package/contracts/registry/ChainRegistry.sol +667 -0
- package/contracts/registry/IChainRegistryRefs.sol +81 -0
- package/contracts/registry/ICommitmentScheme.sol +46 -0
- package/contracts/registry/IRefValidator.sol +13 -0
- package/contracts/registry/Provenance.sol +25 -0
- package/contracts/schemes/Blake2bScheme.sol +18 -0
- package/contracts/schemes/KeccakScheme.sol +19 -0
- package/contracts/schemes/Sha256Scheme.sol +15 -0
- package/contracts/treasury/Treasury.sol +76 -0
- package/contracts/validators/MoveValidator.sol +24 -0
- package/contracts/validators/StarknetValidator.sol +20 -0
- package/package.json +34 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @title Blake2b256
|
|
5
|
+
/// @notice Full BLAKE2b-256 built on the EIP-152 `blake2f` compression precompile (0x09).
|
|
6
|
+
///
|
|
7
|
+
/// @dev The precompile provides only the F compression function, not a hash. This library
|
|
8
|
+
/// supplies the parameter block, the message padding, the byte-offset counters, and
|
|
9
|
+
/// the final-block flag around it.
|
|
10
|
+
///
|
|
11
|
+
/// Endianness. EIP-152 takes and returns the state `h` and message `m` as
|
|
12
|
+
/// little-endian 64-bit words. Because we carry `h` as opaque 64 raw bytes and pass
|
|
13
|
+
/// message blocks through verbatim, no word swapping is ever needed: only the
|
|
14
|
+
/// parameter-block XOR and the `t` counter are written in explicit little-endian.
|
|
15
|
+
/// This is the single most common source of wrong BLAKE2b ports; it is avoided here
|
|
16
|
+
/// rather than handled.
|
|
17
|
+
///
|
|
18
|
+
/// Mutability. Functions here are `view`, not `pure`. Solidity forbids `staticcall`
|
|
19
|
+
/// inside `pure`, and there is no `blake2b` builtin the way there is for `sha256`
|
|
20
|
+
/// and `ripemd160`. Callers that need `pure` cannot use this; pass the digest in.
|
|
21
|
+
///
|
|
22
|
+
/// Gas. The precompile costs 1 gas per round, 12 rounds per 128-byte block, so the
|
|
23
|
+
/// compression itself is free in practice. Cost is dominated by memory and the
|
|
24
|
+
/// staticcall overhead: measured at roughly 3k gas for a single-block input.
|
|
25
|
+
library Blake2b256 {
|
|
26
|
+
/// @dev BLAKE2b IV, little-endian bytes, with h[0] already XORed by the parameter
|
|
27
|
+
/// block 0x01010020 (fanout=1, depth=1, key length=0, digest length=32).
|
|
28
|
+
/// 0x6a09e667f3bcc908 ^ 0x01010020 == 0x6a09e667f2bdc928.
|
|
29
|
+
bytes private constant IV_PARAM = hex"28c9bdf267e6096a" hex"3ba7ca8485ae67bb" hex"2bf894fe72f36e3c"
|
|
30
|
+
hex"f1361d5f3af54fa5" hex"d182e6ad7f520e51" hex"1f6c3e2b8c68059b" hex"6bbd41fbabd9831f" hex"79217e1319cde05b";
|
|
31
|
+
|
|
32
|
+
uint32 private constant ROUNDS = 12;
|
|
33
|
+
uint256 private constant BLOCK_SIZE = 128;
|
|
34
|
+
|
|
35
|
+
error PrecompileFailed();
|
|
36
|
+
|
|
37
|
+
/// @notice BLAKE2b-256 over an arbitrary-length input, unkeyed.
|
|
38
|
+
/// @dev Handles the empty input, exact-multiple-of-128 inputs, and multi-block
|
|
39
|
+
/// inputs. All three are separately covered by the test vectors.
|
|
40
|
+
function hash(bytes memory input) internal view returns (bytes32) {
|
|
41
|
+
bytes memory h = IV_PARAM; // 64 bytes, mutated in place across blocks
|
|
42
|
+
|
|
43
|
+
uint256 len = input.length;
|
|
44
|
+
if (len == 0) {
|
|
45
|
+
// Single all-zero final block with counter 0.
|
|
46
|
+
h = _compress(h, new bytes(BLOCK_SIZE), 0, true);
|
|
47
|
+
} else {
|
|
48
|
+
uint256 nBlocks = (len + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
49
|
+
for (uint256 i; i < nBlocks; ++i) {
|
|
50
|
+
bool last = (i == nBlocks - 1);
|
|
51
|
+
uint256 offset = i * BLOCK_SIZE;
|
|
52
|
+
// Counter is bytes compressed so far including this block. For the
|
|
53
|
+
// final block that is the true message length, not a padded length.
|
|
54
|
+
// forge-lint: disable-next-line(unsafe-typecast) a memory length fits in 64 bits
|
|
55
|
+
uint64 t = uint64(last ? len : offset + BLOCK_SIZE);
|
|
56
|
+
h = _compress(h, _blockAt(input, offset, len), t, last);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Digest is the first 32 bytes of the state, already in output byte order.
|
|
61
|
+
bytes32 out;
|
|
62
|
+
assembly {
|
|
63
|
+
out := mload(add(h, 32))
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// @dev Extracts a 128-byte block starting at `offset`, zero-padded if the message
|
|
69
|
+
/// ends early. Zero padding is part of the BLAKE2b spec, not an artifact.
|
|
70
|
+
function _blockAt(bytes memory input, uint256 offset, uint256 len) private pure returns (bytes memory blk) {
|
|
71
|
+
// forge-lint: disable-next-line(calls-loop) allocation, not a call
|
|
72
|
+
blk = new bytes(BLOCK_SIZE);
|
|
73
|
+
uint256 n = len - offset;
|
|
74
|
+
if (n > BLOCK_SIZE) n = BLOCK_SIZE;
|
|
75
|
+
for (uint256 i; i < n; ++i) {
|
|
76
|
+
blk[i] = input[offset + i];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// @dev One call to the EIP-152 F compression function.
|
|
81
|
+
/// Input layout is exactly 213 bytes:
|
|
82
|
+
/// rounds 4 bytes, big-endian uint32
|
|
83
|
+
/// h 64 bytes, 8 little-endian uint64
|
|
84
|
+
/// m 128 bytes, 16 little-endian uint64
|
|
85
|
+
/// t 16 bytes, t0 then t1, each little-endian uint64
|
|
86
|
+
/// f 1 byte, 0x01 on the final block
|
|
87
|
+
function _compress(bytes memory h, bytes memory blk, uint64 t0, bool last) private view returns (bytes memory out) {
|
|
88
|
+
// Every field is fixed-width.
|
|
89
|
+
// forge-lint: disable-start(encode-packed-collision)
|
|
90
|
+
bytes memory input = abi.encodePacked(
|
|
91
|
+
ROUNDS, // 4, big-endian by abi.encodePacked
|
|
92
|
+
h, // 64
|
|
93
|
+
blk, // 128
|
|
94
|
+
_le64(t0), // 8 (t0)
|
|
95
|
+
bytes8(0), // 8 (t1: messages beyond 2^64 bytes are not representable)
|
|
96
|
+
last ? bytes1(0x01) : bytes1(0x00) // 1
|
|
97
|
+
);
|
|
98
|
+
// forge-lint: disable-end(encode-packed-collision)
|
|
99
|
+
|
|
100
|
+
// forge-lint: disable-next-line(calls-loop) allocation, not a call
|
|
101
|
+
out = new bytes(64);
|
|
102
|
+
bool ok;
|
|
103
|
+
assembly {
|
|
104
|
+
// Without EIP-152, 0x09 may be empty (success, no data), revert (zkSync Era), or
|
|
105
|
+
// hold another precompile (Tron's BatchValidateSign returns 32 bytes), so the size
|
|
106
|
+
// is checked too, or a digest could be zeros or foreign output.
|
|
107
|
+
ok := staticcall(gas(), 0x09, add(input, 32), 213, add(out, 32), 64)
|
|
108
|
+
ok := and(ok, eq(returndatasize(), 64))
|
|
109
|
+
}
|
|
110
|
+
if (!ok) revert PrecompileFailed();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// @dev uint64 -> 8 little-endian bytes.
|
|
114
|
+
function _le64(uint64 x) private pure returns (bytes8 r) {
|
|
115
|
+
uint64 v;
|
|
116
|
+
for (uint256 i; i < 8; ++i) {
|
|
117
|
+
v = (v << 8) | (x & 0xff);
|
|
118
|
+
x >>= 8;
|
|
119
|
+
}
|
|
120
|
+
r = bytes8(v);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @notice Uniform address derivation across every VM the protocol targets.
|
|
5
|
+
///
|
|
6
|
+
/// @dev The normalization problem. Every VM's derivation takes different inputs: CREATE2
|
|
7
|
+
/// wants (deployer, salt, initCodeHash), a Solana PDA wants (seeds[], bump,
|
|
8
|
+
/// programId), CosmWasm wants (checksum, creator, salt, initMsg). There is no
|
|
9
|
+
/// argument list that fits all of them, so the parameters stay opaque: the caller
|
|
10
|
+
/// passes an ABI-encoded blob and the deriver decodes the shape its scheme requires.
|
|
11
|
+
///
|
|
12
|
+
/// What is uniform is the two ends. Every implementation takes a canonical ERC-7930
|
|
13
|
+
/// chain identifier plus a params blob, and returns a canonical ERC-7930 conduit
|
|
14
|
+
/// envelope for the same chain. The registry can therefore store the result without
|
|
15
|
+
/// knowing which VM produced it.
|
|
16
|
+
///
|
|
17
|
+
/// @dev Kept apart from `VmDeriver` so that `ChainRegistry`, which every chain deploys, does
|
|
18
|
+
/// not import `BitcoinDerive`: zksolc rejects any unit containing `ripemd160`.
|
|
19
|
+
///
|
|
20
|
+
/// @dev Must be `view`, not `pure`. Sui's BLAKE2b-256 goes through precompile 0x09, which
|
|
21
|
+
/// is a `staticcall`. One non-pure member forces the whole interface to `view`.
|
|
22
|
+
interface IVmDeriver {
|
|
23
|
+
/// @param chainIdentifier Canonical ERC-7930 chain identifier (zero-length address).
|
|
24
|
+
/// @param params `abi.encode(Scheme, bytes)`: see `VmDeriver.Scheme`.
|
|
25
|
+
/// @return interop Canonical ERC-7930 conduit envelope on that same chain.
|
|
26
|
+
function deriveAddress(bytes calldata chainIdentifier, bytes calldata params)
|
|
27
|
+
external
|
|
28
|
+
view
|
|
29
|
+
returns (bytes memory interop);
|
|
30
|
+
|
|
31
|
+
/// @notice Whether this deriver can serve `scheme` on `chainType`.
|
|
32
|
+
/// @dev Lets the registry reject a misconfiguration at wiring time rather than at
|
|
33
|
+
/// the first resolve.
|
|
34
|
+
function supportsScheme(uint16 chainType, uint8 scheme) external pure returns (bool);
|
|
35
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @title StarknetDerive
|
|
5
|
+
/// @notice What can and cannot be computed about Starknet from the EVM.
|
|
6
|
+
///
|
|
7
|
+
/// @dev Constants are sourced, not recalled. All values below come from
|
|
8
|
+
/// starkware-libs/cairo-lang:
|
|
9
|
+
/// - starknet/definitions/constants.py :: L2_ADDRESS_UPPER_BOUND
|
|
10
|
+
/// - starknet/public/abi.py :: ADDR_BOUND, MASK_250, starknet_keccak
|
|
11
|
+
/// - starknet/core/os/contract_address/contract_address.py
|
|
12
|
+
///
|
|
13
|
+
/// @dev What is derivable here
|
|
14
|
+
/// - `starknetKeccak` / `selector`: masked keccak256. Native opcode, ~free.
|
|
15
|
+
/// This is how Starknet entry-point selectors are computed, so a payload
|
|
16
|
+
/// targeting a Starknet function can commit to its selector on-chain.
|
|
17
|
+
/// - Range validation of addresses, class hashes, and felts.
|
|
18
|
+
///
|
|
19
|
+
/// @dev What is not
|
|
20
|
+
/// Contract addresses. The derivation is a Pedersen hash chain:
|
|
21
|
+
/// raw = H(H(H(H(H(0, prefix), deployer), salt), classHash), ctorHash), 5)
|
|
22
|
+
/// address = raw mod L2_ADDRESS_UPPER_BOUND
|
|
23
|
+
/// where prefix is the felt encoding of the ASCII "STARKNET_CONTRACT_ADDRESS"
|
|
24
|
+
/// and H is Pedersen over the STARK curve.
|
|
25
|
+
///
|
|
26
|
+
/// Pedersen is not merely expensive here, it is structurally awkward: it needs
|
|
27
|
+
/// roughly 500 precomputed curve points (~32kb), which exceeds EIP-170 outright,
|
|
28
|
+
/// so a real implementation means SSTORE2-chunked tables plus projective EC
|
|
29
|
+
/// arithmetic over a 252-bit prime, at 1e5-1e6 gas with a silent-failure mode.
|
|
30
|
+
///
|
|
31
|
+
/// Recommendation: do not build it. The payoff is upgrading Starknet from
|
|
32
|
+
/// `Unique` to `Predetermined`, and the address can be recorded from a signed payload
|
|
33
|
+
/// instead, where the grade states the weaker guarantee rather than hiding it.
|
|
34
|
+
library StarknetDerive {
|
|
35
|
+
/// @dev FIELD_PRIME = 2^251 + 17 * 2^192 + 1
|
|
36
|
+
uint256 internal constant FIELD_PRIME = 0x0800000000000011000000000000000000000000000000000000000000000001;
|
|
37
|
+
|
|
38
|
+
/// @dev L2_ADDRESS_UPPER_BOUND = 2^251 - 256. Note the "- 256": a naive `< 2^251`
|
|
39
|
+
/// check admits 256 values that can never be a Starknet contract address,
|
|
40
|
+
/// because `calculate_contract_address_from_hash` reduces mod this bound.
|
|
41
|
+
uint256 internal constant L2_ADDRESS_UPPER_BOUND = (1 << 251) - 256;
|
|
42
|
+
|
|
43
|
+
/// @dev MASK_250 = 2^250 - 1
|
|
44
|
+
uint256 internal constant MASK_250 = (1 << 250) - 1;
|
|
45
|
+
|
|
46
|
+
/// @dev CONTRACT_ADDRESS_PREFIX = felt encoding of ASCII "STARKNET_CONTRACT_ADDRESS"
|
|
47
|
+
/// (25 bytes, big-endian). Present so an off-chain derivation can be checked
|
|
48
|
+
/// against the same constant the contract enforces.
|
|
49
|
+
uint256 internal constant CONTRACT_ADDRESS_PREFIX = 0x535441524b4e45545f434f4e54524143545f41444452455353;
|
|
50
|
+
|
|
51
|
+
error AddressOutOfRange();
|
|
52
|
+
error FeltOutOfRange();
|
|
53
|
+
error ClassHashOutOfRange();
|
|
54
|
+
error BadAddressWidth();
|
|
55
|
+
|
|
56
|
+
/// @notice Starknet's keccak variant: keccak256 truncated to 250 bits so the
|
|
57
|
+
/// result fits a field element.
|
|
58
|
+
/// @dev cairo-lang: `from_bytes(keccak(data)) & MASK_250`.
|
|
59
|
+
function starknetKeccak(bytes memory data) internal pure returns (uint256) {
|
|
60
|
+
return uint256(keccak256(data)) & MASK_250;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// @notice Entry-point selector for a function name.
|
|
64
|
+
/// @dev Selectors for `__default__` / `__l1_default__` are 0 by special case;
|
|
65
|
+
/// that is a caller concern, not encoded here.
|
|
66
|
+
function selector(string memory functionName) internal pure returns (uint256) {
|
|
67
|
+
return starknetKeccak(bytes(functionName));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/// @notice A contract address must be strictly below L2_ADDRESS_UPPER_BOUND.
|
|
71
|
+
function validateAddress(uint256 addr) internal pure {
|
|
72
|
+
if (addr >= L2_ADDRESS_UPPER_BOUND) revert AddressOutOfRange();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// @notice A generic field element must be below FIELD_PRIME. Class hashes use
|
|
76
|
+
/// this wider bound (CLASS_HASH_UPPER_BOUND == FIELD_SIZE), not the
|
|
77
|
+
/// address bound.
|
|
78
|
+
function validateFelt(uint256 v) internal pure {
|
|
79
|
+
if (v >= FIELD_PRIME) revert FeltOutOfRange();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function validateClassHash(uint256 v) internal pure {
|
|
83
|
+
if (v >= FIELD_PRIME) revert ClassHashOutOfRange();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// @notice Read a 32-byte Starknet address out of ERC-7930 address bytes.
|
|
87
|
+
function toFelt(bytes memory addr) internal pure returns (uint256 v) {
|
|
88
|
+
if (addr.length != 32) revert BadAddressWidth();
|
|
89
|
+
assembly {
|
|
90
|
+
v := mload(add(addr, 32))
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Blake2b256} from "./Blake2b256.sol";
|
|
5
|
+
|
|
6
|
+
/// @title SuiDerive
|
|
7
|
+
/// @notice Sui address and object-ID derivation, executable on Ethereum.
|
|
8
|
+
/// @dev Every constant below was read from MystenLabs/sui rather than recalled.
|
|
9
|
+
library SuiDerive {
|
|
10
|
+
using Blake2b256 for bytes;
|
|
11
|
+
|
|
12
|
+
/* ------------------------------- flags -------------------------------- */
|
|
13
|
+
// crates/sui-types/src/crypto.rs :: SignatureScheme::flag()
|
|
14
|
+
uint8 internal constant FLAG_ED25519 = 0x00;
|
|
15
|
+
uint8 internal constant FLAG_SECP256K1 = 0x01;
|
|
16
|
+
uint8 internal constant FLAG_SECP256R1 = 0x02;
|
|
17
|
+
uint8 internal constant FLAG_MULTISIG = 0x03;
|
|
18
|
+
uint8 internal constant FLAG_BLS12381 = 0x04; // not valid for a user address
|
|
19
|
+
uint8 internal constant FLAG_ZKLOGIN = 0x05;
|
|
20
|
+
uint8 internal constant FLAG_PASSKEY = 0x06;
|
|
21
|
+
|
|
22
|
+
/* --------------------------- intent scopes ---------------------------- */
|
|
23
|
+
// crates/shared-crypto/src/intent.rs :: HashingIntentScope
|
|
24
|
+
// Note: these are 0xf0/0xf1, not 0/1. Assuming a zero-based enum here silently
|
|
25
|
+
// produces well-formed but wrong object IDs.
|
|
26
|
+
uint8 internal constant SCOPE_CHILD_OBJECT_ID = 0xf0;
|
|
27
|
+
uint8 internal constant SCOPE_REGULAR_OBJECT_ID = 0xf1;
|
|
28
|
+
|
|
29
|
+
error InvalidFlag();
|
|
30
|
+
error LengthMismatch();
|
|
31
|
+
|
|
32
|
+
/// @notice Sui address = blake2b256(flag ‖ pubkey).
|
|
33
|
+
/// @dev crates/sui-types/src/base_types.rs :: impl From<&PublicKey> for SuiAddress
|
|
34
|
+
/// @param flag One of the FLAG_* constants.
|
|
35
|
+
/// @param pubkey Raw public key bytes. Ed25519 is 32; secp256k1/r1 are 33
|
|
36
|
+
/// (SEC1 compressed). Length is checked where it is fixed.
|
|
37
|
+
function addressFromPubkey(uint8 flag, bytes memory pubkey) internal view returns (bytes32) {
|
|
38
|
+
if (flag == FLAG_ED25519) {
|
|
39
|
+
if (pubkey.length != 32) revert LengthMismatch();
|
|
40
|
+
} else if (flag == FLAG_SECP256K1 || flag == FLAG_SECP256R1 || flag == FLAG_PASSKEY) {
|
|
41
|
+
if (pubkey.length != 33) revert LengthMismatch();
|
|
42
|
+
} else if (flag != FLAG_ZKLOGIN) {
|
|
43
|
+
// MultiSig has its own constructor; BLS is not a valid user address.
|
|
44
|
+
revert InvalidFlag();
|
|
45
|
+
}
|
|
46
|
+
return Blake2b256.hash(abi.encodePacked(flag, pubkey));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// @notice MultiSig address.
|
|
50
|
+
/// @dev blake2b256( 0x03 ‖ le16(threshold) ‖ (flag_i ‖ pk_i ‖ weight_i)* )
|
|
51
|
+
/// Threshold is u16 little-endian; each weight is u8. Order of the member
|
|
52
|
+
/// list is significant: it must match the on-chain MultiSigPublicKey.
|
|
53
|
+
/// @param flags Per-member scheme flags.
|
|
54
|
+
/// @param pubkeys Per-member raw public keys, same order as `flags`.
|
|
55
|
+
/// @param weights Per-member weights, same order.
|
|
56
|
+
function addressFromMultisig(uint16 threshold, uint8[] memory flags, bytes[] memory pubkeys, uint8[] memory weights)
|
|
57
|
+
internal
|
|
58
|
+
view
|
|
59
|
+
returns (bytes32)
|
|
60
|
+
{
|
|
61
|
+
uint256 n = flags.length;
|
|
62
|
+
if (pubkeys.length != n || weights.length != n) revert LengthMismatch();
|
|
63
|
+
|
|
64
|
+
bytes memory pre = abi.encodePacked(FLAG_MULTISIG, _le16(threshold));
|
|
65
|
+
for (uint256 i; i < n; ++i) {
|
|
66
|
+
// forge-lint: disable-next-line(encode-packed-collision) Sui's multisig preimage; each key's length is fixed by its flag
|
|
67
|
+
pre = abi.encodePacked(pre, flags[i], pubkeys[i], weights[i]);
|
|
68
|
+
}
|
|
69
|
+
return Blake2b256.hash(pre);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// @notice Object ID created by a transaction.
|
|
73
|
+
/// @dev crates/sui-types/src/base_types.rs :: ObjectID::derive_id
|
|
74
|
+
/// blake2b256( 0xf1 ‖ txDigest ‖ le64(creationNum) ), truncated to 32 bytes
|
|
75
|
+
/// (already 32, so the truncation is a no-op).
|
|
76
|
+
///
|
|
77
|
+
/// This is derivable but not counterfactual: `txDigest` does not exist until
|
|
78
|
+
/// the transaction is built and signed, and `creationNum` depends on how many
|
|
79
|
+
/// IDs that transaction already created. Use it to verify an object ID
|
|
80
|
+
/// reported back by the destination, not to predict one in advance.
|
|
81
|
+
function deriveObjectId(bytes32 txDigest, uint64 creationNum) internal view returns (bytes32) {
|
|
82
|
+
return Blake2b256.hash(abi.encodePacked(SCOPE_REGULAR_OBJECT_ID, txDigest, _le64(creationNum)));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @notice Dynamic-field / child object ID variant.
|
|
86
|
+
function deriveChildObjectId(bytes32 txDigest, uint64 creationNum) internal view returns (bytes32) {
|
|
87
|
+
return Blake2b256.hash(abi.encodePacked(SCOPE_CHILD_OBJECT_ID, txDigest, _le64(creationNum)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function _le16(uint16 x) private pure returns (bytes2) {
|
|
91
|
+
return bytes2(uint16((uint16(x & 0xff) << 8) | (x >> 8)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function _le64(uint64 x) private pure returns (bytes8 r) {
|
|
95
|
+
uint64 v;
|
|
96
|
+
for (uint256 i; i < 8; ++i) {
|
|
97
|
+
v = (v << 8) | (x & 0xff);
|
|
98
|
+
x >>= 8;
|
|
99
|
+
}
|
|
100
|
+
r = bytes8(v);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {ChainType} from "../addressing/ChainType.sol";
|
|
5
|
+
import {AddressDerive} from "./AddressDerive.sol";
|
|
6
|
+
import {BitcoinDerive} from "./BitcoinDerive.sol";
|
|
7
|
+
import {SuiDerive} from "./SuiDerive.sol";
|
|
8
|
+
import {Erc7930} from "../addressing/Erc7930.sol";
|
|
9
|
+
import {IVmDeriver} from "./IVmDeriver.sol";
|
|
10
|
+
|
|
11
|
+
/// @title VmDeriver
|
|
12
|
+
/// @notice One standalone, stateless contract implementing every derivation the protocol
|
|
13
|
+
/// can perform on-chain, dispatched by (ChainType, Scheme).
|
|
14
|
+
///
|
|
15
|
+
/// @dev Why one contract and not one per VM. These are pure functions over calldata with
|
|
16
|
+
/// no storage and no privileges, so there is nothing to isolate between them. A
|
|
17
|
+
/// single deployment means one address to audit and one address to register for
|
|
18
|
+
/// every chain, and the registry's `deriverOf` mapping still allows a per-chain
|
|
19
|
+
/// override later if a VM ever needs its own.
|
|
20
|
+
///
|
|
21
|
+
/// @dev Dispatch is two-dimensional, and it has to be. ChainType alone is not enough:
|
|
22
|
+
/// Ethereum, zkSync Era, and Tron are all `eip155`, with three different CREATE2
|
|
23
|
+
/// formulas. Scheme alone is not enough either, since the same scheme must produce
|
|
24
|
+
/// envelopes on different chains. Every scheme is therefore checked against the
|
|
25
|
+
/// chain type it is legal for, in `supportsScheme`.
|
|
26
|
+
///
|
|
27
|
+
/// @dev Not derivable here, by construction:
|
|
28
|
+
/// - Aptos / Movement: SHA3-256 is not keccak256 (padding domain 0x06 vs 0x01).
|
|
29
|
+
/// A hand-rolled Keccak-f[1600] is ~1e5 gas.
|
|
30
|
+
/// - Starknet: Pedersen/Poseidon over the STARK curve. No precompile, and the
|
|
31
|
+
/// point tables exceed EIP-170 outright.
|
|
32
|
+
/// - Bitcoin P2TR: taproot output keys need secp256k1 point addition; ecrecover
|
|
33
|
+
/// is not a general curve operation.
|
|
34
|
+
/// These revert with `UnsupportedScheme`. Route them through the registry's
|
|
35
|
+
/// committed or `Unique` path: that is exactly what the provenance grades are for.
|
|
36
|
+
contract VmDeriver is IVmDeriver {
|
|
37
|
+
/// @notice Which derivation to run. The chain type says which VM; this says which
|
|
38
|
+
/// formula within it.
|
|
39
|
+
enum Scheme {
|
|
40
|
+
Unset,
|
|
41
|
+
/// EIP-1014. (address deployer, bytes32 salt, bytes32 initCodeHash)
|
|
42
|
+
EvmCreate2,
|
|
43
|
+
/// (address factory, bytes32 salt): bytecode-independent.
|
|
44
|
+
EvmCreate3,
|
|
45
|
+
/// Nonce-based. (address deployer, uint256 nonce)
|
|
46
|
+
EvmCreate,
|
|
47
|
+
/// (address sender, bytes32 salt, bytes32 bytecodeHash, bytes32 ctorInputHash)
|
|
48
|
+
ZkSyncCreate2,
|
|
49
|
+
/// (address sender, uint256 nonce)
|
|
50
|
+
ZkSyncCreate,
|
|
51
|
+
/// (address deployer, bytes32 salt, bytes32 initCodeHash)
|
|
52
|
+
TronCreate2,
|
|
53
|
+
/// (bytes32 checksum, bytes creator, bytes salt, bytes initMsg, uint8 addrLen)
|
|
54
|
+
CosmosInstantiate2,
|
|
55
|
+
/// (bytes[] seeds, uint8 bump, bytes32 programId)
|
|
56
|
+
SolanaPda,
|
|
57
|
+
/// (bytes32 owner, bytes32 mint, bytes32 tokenProgram, bytes32 ataProgram, uint8 bump)
|
|
58
|
+
SolanaAta,
|
|
59
|
+
/// (bytes32 base, bytes seed, bytes32 programId)
|
|
60
|
+
SolanaCreateWithSeed,
|
|
61
|
+
/// (bytes borshStateInit) -> 20 bytes, rendered "0s"+hex off-chain
|
|
62
|
+
NearDeterministic,
|
|
63
|
+
/// (bytes uncompressedPubkey64) -> 20 bytes
|
|
64
|
+
NearEthImplicit,
|
|
65
|
+
/// (bytes32 ed25519Pubkey) -> 32 bytes
|
|
66
|
+
NearImplicit,
|
|
67
|
+
/// (bytes pubkey) -> 20-byte HASH160 witness program
|
|
68
|
+
BitcoinP2wpkh,
|
|
69
|
+
/// (bytes witnessScript) -> 32-byte SHA256 witness program
|
|
70
|
+
BitcoinP2wsh,
|
|
71
|
+
/// (bytes redeemScript) -> 20-byte HASH160
|
|
72
|
+
BitcoinP2sh,
|
|
73
|
+
/// (uint8 flag, bytes pubkey)
|
|
74
|
+
SuiAddress,
|
|
75
|
+
/// (uint16 threshold, uint8[] flags, bytes[] pubkeys, uint8[] weights)
|
|
76
|
+
SuiMultisig,
|
|
77
|
+
/// (bytes32 txDigest, uint64 creationNum): verification only, see note.
|
|
78
|
+
SuiObjectId,
|
|
79
|
+
/// EIP-1167 clone. (address deployer, address implementation, bytes32 salt)
|
|
80
|
+
/// This is how a receiver address is predicted: deployer is the destination
|
|
81
|
+
/// transceiver, salt is `keccak256(abi.encode(transmitter))`.
|
|
82
|
+
EvmClone,
|
|
83
|
+
/// EIP-1167 clone under Tron's 0x41 domain byte. Same params as EvmClone.
|
|
84
|
+
TronClone
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
error UnsupportedScheme(uint16 chainType, uint8 scheme);
|
|
88
|
+
|
|
89
|
+
error NotChainIdentifier();
|
|
90
|
+
error BadCosmosAddressLength();
|
|
91
|
+
|
|
92
|
+
/// @notice Creation-code hash of an EIP-1167 clone. Exposed so the same value can be
|
|
93
|
+
/// computed off-chain and pinned in a signed payload.
|
|
94
|
+
function cloneInitCodeHash(address implementation) external pure returns (bytes32) {
|
|
95
|
+
return AddressDerive.cloneInitCodeHash(implementation);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ================================ dispatch ================================= */
|
|
99
|
+
|
|
100
|
+
/// @inheritdoc IVmDeriver
|
|
101
|
+
function deriveAddress(bytes calldata chainIdentifier, bytes calldata params)
|
|
102
|
+
external
|
|
103
|
+
view
|
|
104
|
+
override
|
|
105
|
+
returns (bytes memory interop)
|
|
106
|
+
{
|
|
107
|
+
Erc7930.Interop memory io = Erc7930.parseStrict(chainIdentifier);
|
|
108
|
+
// A chain identifier carries no conduit. Being handed a populated envelope means
|
|
109
|
+
// the caller is confused about which end of the derivation it is on.
|
|
110
|
+
if (io.addr.length != 0) revert NotChainIdentifier();
|
|
111
|
+
|
|
112
|
+
(Scheme scheme, bytes memory inner) = abi.decode(params, (Scheme, bytes));
|
|
113
|
+
if (!supportsScheme(io.chainType, uint8(scheme))) {
|
|
114
|
+
revert UnsupportedScheme(io.chainType, uint8(scheme));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return Erc7930.encode(io.chainType, io.chainRef, _derive(scheme, inner));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// @inheritdoc IVmDeriver
|
|
121
|
+
/// @dev The legality table. Ethereum/zkSync/Tron all being `eip155` is why this is
|
|
122
|
+
/// keyed on the pair and not on either half alone: the registry pins the exact
|
|
123
|
+
/// scheme per chainKey, so a zkSync chainKey cannot be resolved with Ethereum's
|
|
124
|
+
/// formula just because both are eip155.
|
|
125
|
+
function supportsScheme(uint16 chainType, uint8 scheme) public pure override returns (bool) {
|
|
126
|
+
Scheme s = Scheme(scheme);
|
|
127
|
+
|
|
128
|
+
if (chainType == ChainType.EIP155) {
|
|
129
|
+
return s == Scheme.EvmCreate2 || s == Scheme.EvmCreate3 || s == Scheme.EvmCreate
|
|
130
|
+
|| s == Scheme.ZkSyncCreate2 || s == Scheme.ZkSyncCreate || s == Scheme.TronCreate2
|
|
131
|
+
|| s == Scheme.EvmClone || s == Scheme.TronClone;
|
|
132
|
+
}
|
|
133
|
+
if (chainType == ChainType.SOLANA) {
|
|
134
|
+
return s == Scheme.SolanaPda || s == Scheme.SolanaAta || s == Scheme.SolanaCreateWithSeed;
|
|
135
|
+
}
|
|
136
|
+
if (chainType == ChainType.BIP122) {
|
|
137
|
+
return s == Scheme.BitcoinP2wpkh || s == Scheme.BitcoinP2wsh || s == Scheme.BitcoinP2sh;
|
|
138
|
+
}
|
|
139
|
+
if (chainType == ChainType.COSMOS) {
|
|
140
|
+
return s == Scheme.CosmosInstantiate2;
|
|
141
|
+
}
|
|
142
|
+
if (chainType == ChainType.NEAR) {
|
|
143
|
+
return s == Scheme.NearDeterministic || s == Scheme.NearEthImplicit || s == Scheme.NearImplicit;
|
|
144
|
+
}
|
|
145
|
+
if (chainType == ChainType.SUI) {
|
|
146
|
+
return s == Scheme.SuiAddress || s == Scheme.SuiMultisig || s == Scheme.SuiObjectId;
|
|
147
|
+
}
|
|
148
|
+
// ChainType.Aptos and ChainType.Starknet are deliberately absent.
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* =============================== derivations =============================== */
|
|
153
|
+
|
|
154
|
+
/// @dev Returns the raw address bytes for the scheme, at the width that chain's
|
|
155
|
+
/// ERC-7930 profile expects. The caller wraps them in the envelope.
|
|
156
|
+
function _derive(Scheme s, bytes memory p) private view returns (bytes memory) {
|
|
157
|
+
/* ---------------------------------- EVM --------------------------------- */
|
|
158
|
+
if (s == Scheme.EvmCreate2) {
|
|
159
|
+
(address deployer, bytes32 salt, bytes32 initCodeHash) = abi.decode(p, (address, bytes32, bytes32));
|
|
160
|
+
return abi.encodePacked(AddressDerive.create2(deployer, salt, initCodeHash));
|
|
161
|
+
}
|
|
162
|
+
if (s == Scheme.EvmCreate3) {
|
|
163
|
+
(address factory, bytes32 salt) = abi.decode(p, (address, bytes32));
|
|
164
|
+
return abi.encodePacked(AddressDerive.create3(factory, salt));
|
|
165
|
+
}
|
|
166
|
+
if (s == Scheme.EvmCreate) {
|
|
167
|
+
(address deployer, uint256 nonce) = abi.decode(p, (address, uint256));
|
|
168
|
+
return abi.encodePacked(AddressDerive.create(deployer, nonce));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* -------------------------------- clones -------------------------------- */
|
|
172
|
+
// Note on zkSYNC: EIP-1167 has no zkSync equivalent. EraVM will only deploy
|
|
173
|
+
// bytecode whose hash has been published, and a 55-byte EVM proxy is not valid
|
|
174
|
+
// EraVM bytecode at all. A zkSync "clone" is a real proxy contract compiled with
|
|
175
|
+
// zksolc, so it is derived with `ZkSyncCreate2` using that proxy's versioned
|
|
176
|
+
// bytecode hash: no separate scheme, because it is not a clone in this sense.
|
|
177
|
+
if (s == Scheme.EvmClone) {
|
|
178
|
+
(address deployer, address implementation, bytes32 salt) = abi.decode(p, (address, address, bytes32));
|
|
179
|
+
return abi.encodePacked(AddressDerive.clone2(deployer, implementation, salt));
|
|
180
|
+
}
|
|
181
|
+
if (s == Scheme.TronClone) {
|
|
182
|
+
(address deployer, address implementation, bytes32 salt) = abi.decode(p, (address, address, bytes32));
|
|
183
|
+
return abi.encodePacked(
|
|
184
|
+
AddressDerive.tronCreate2(deployer, salt, AddressDerive.cloneInitCodeHash(implementation))
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* -------------------------------- zkSync -------------------------------- */
|
|
189
|
+
if (s == Scheme.ZkSyncCreate2) {
|
|
190
|
+
(address sender, bytes32 salt, bytes32 bytecodeHash, bytes32 ctorInputHash) =
|
|
191
|
+
abi.decode(p, (address, bytes32, bytes32, bytes32));
|
|
192
|
+
return abi.encodePacked(AddressDerive.zksyncCreate2(sender, salt, bytecodeHash, ctorInputHash));
|
|
193
|
+
}
|
|
194
|
+
if (s == Scheme.ZkSyncCreate) {
|
|
195
|
+
(address sender, uint256 nonce) = abi.decode(p, (address, uint256));
|
|
196
|
+
return abi.encodePacked(AddressDerive.zksyncCreate(sender, nonce));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* --------------------------------- Tron --------------------------------- */
|
|
200
|
+
if (s == Scheme.TronCreate2) {
|
|
201
|
+
(address deployer, bytes32 salt, bytes32 initCodeHash) = abi.decode(p, (address, bytes32, bytes32));
|
|
202
|
+
return abi.encodePacked(AddressDerive.tronCreate2(deployer, salt, initCodeHash));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* -------------------------------- Cosmos -------------------------------- */
|
|
206
|
+
if (s == Scheme.CosmosInstantiate2) {
|
|
207
|
+
(bytes32 checksum, bytes memory creator, bytes memory salt, bytes memory initMsg, uint8 addrLen) =
|
|
208
|
+
abi.decode(p, (bytes32, bytes, bytes, bytes, uint8));
|
|
209
|
+
bytes32 full = AddressDerive.cosmosInstantiate2(checksum, creator, salt, initMsg);
|
|
210
|
+
// Canonical ADR-028 is 32 bytes. Forked wasmd chains that use 20 for
|
|
211
|
+
// Ethereum-ecosystem compatibility (Injective) truncate. Only set addrLen
|
|
212
|
+
// below 32 if you have confirmed the target forked ContractAddrLen:
|
|
213
|
+
// getting it wrong yields a well-formed address nothing will ever occupy.
|
|
214
|
+
if (addrLen == 0 || addrLen > 32) revert BadCosmosAddressLength();
|
|
215
|
+
if (addrLen == 32) return abi.encodePacked(full);
|
|
216
|
+
bytes memory out = new bytes(addrLen);
|
|
217
|
+
for (uint256 i; i < addrLen; ++i) {
|
|
218
|
+
out[i] = full[i];
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* -------------------------------- Solana -------------------------------- */
|
|
224
|
+
if (s == Scheme.SolanaPda) {
|
|
225
|
+
(bytes[] memory seeds, uint8 bump, bytes32 programId) = abi.decode(p, (bytes[], uint8, bytes32));
|
|
226
|
+
return abi.encodePacked(AddressDerive.solanaCreateProgramAddress(seeds, bump, programId));
|
|
227
|
+
}
|
|
228
|
+
if (s == Scheme.SolanaAta) {
|
|
229
|
+
(bytes32 owner, bytes32 mint, bytes32 tokenProgram, bytes32 ataProgram, uint8 bump) =
|
|
230
|
+
abi.decode(p, (bytes32, bytes32, bytes32, bytes32, uint8));
|
|
231
|
+
return
|
|
232
|
+
abi.encodePacked(
|
|
233
|
+
AddressDerive.solanaAssociatedTokenAccount(owner, mint, tokenProgram, ataProgram, bump)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (s == Scheme.SolanaCreateWithSeed) {
|
|
237
|
+
(bytes32 base, bytes memory seed, bytes32 programId) = abi.decode(p, (bytes32, bytes, bytes32));
|
|
238
|
+
return abi.encodePacked(AddressDerive.solanaCreateWithSeed(base, seed, programId));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* --------------------------------- NEAR --------------------------------- */
|
|
242
|
+
if (s == Scheme.NearDeterministic) {
|
|
243
|
+
bytes memory borshStateInit = abi.decode(p, (bytes));
|
|
244
|
+
return abi.encodePacked(AddressDerive.nearDeterministicAccount(borshStateInit));
|
|
245
|
+
}
|
|
246
|
+
if (s == Scheme.NearEthImplicit) {
|
|
247
|
+
bytes memory pubkey64 = abi.decode(p, (bytes));
|
|
248
|
+
return abi.encodePacked(AddressDerive.nearEthImplicitAccount(pubkey64));
|
|
249
|
+
}
|
|
250
|
+
if (s == Scheme.NearImplicit) {
|
|
251
|
+
// The ed25519 pubkey is the conduit id. Identity, kept explicit so the
|
|
252
|
+
// envelope width is unambiguous.
|
|
253
|
+
bytes32 pubkey = abi.decode(p, (bytes32));
|
|
254
|
+
return abi.encodePacked(pubkey);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* -------------------------------- Bitcoin ------------------------------- */
|
|
258
|
+
if (s == Scheme.BitcoinP2wpkh) {
|
|
259
|
+
bytes memory pubkey = abi.decode(p, (bytes));
|
|
260
|
+
return abi.encodePacked(BitcoinDerive.hash160(pubkey));
|
|
261
|
+
}
|
|
262
|
+
if (s == Scheme.BitcoinP2wsh) {
|
|
263
|
+
bytes memory witnessScript = abi.decode(p, (bytes));
|
|
264
|
+
return abi.encodePacked(BitcoinDerive.p2wshProgram(witnessScript));
|
|
265
|
+
}
|
|
266
|
+
if (s == Scheme.BitcoinP2sh) {
|
|
267
|
+
bytes memory redeemScript = abi.decode(p, (bytes));
|
|
268
|
+
return abi.encodePacked(BitcoinDerive.p2shScriptHash(redeemScript));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* ---------------------------------- Sui --------------------------------- */
|
|
272
|
+
if (s == Scheme.SuiAddress) {
|
|
273
|
+
(uint8 flag, bytes memory pubkey) = abi.decode(p, (uint8, bytes));
|
|
274
|
+
return abi.encodePacked(SuiDerive.addressFromPubkey(flag, pubkey));
|
|
275
|
+
}
|
|
276
|
+
if (s == Scheme.SuiMultisig) {
|
|
277
|
+
(uint16 threshold, uint8[] memory flags, bytes[] memory pubkeys, uint8[] memory weights) =
|
|
278
|
+
abi.decode(p, (uint16, uint8[], bytes[], uint8[]));
|
|
279
|
+
return abi.encodePacked(SuiDerive.addressFromMultisig(threshold, flags, pubkeys, weights));
|
|
280
|
+
}
|
|
281
|
+
if (s == Scheme.SuiObjectId) {
|
|
282
|
+
// Derivable but not counterfactual: txDigest does not exist until the
|
|
283
|
+
// transaction is built and signed. Use this to verify an object id the
|
|
284
|
+
// destination reported, never to predict one in advance.
|
|
285
|
+
(bytes32 txDigest, uint64 creationNum) = abi.decode(p, (bytes32, uint64));
|
|
286
|
+
return abi.encodePacked(SuiDerive.deriveObjectId(txDigest, creationNum));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
revert UnsupportedScheme(0, uint8(s));
|
|
290
|
+
}
|
|
291
|
+
}
|