@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.
- package/README.md +26 -2
- 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 +36 -4
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @notice The one call a `Proxy` accepts from its deployer. Declared separately so
|
|
5
|
+
/// callers have a selector without the proxy shadowing the implementation's ABI.
|
|
6
|
+
interface IProxy {
|
|
7
|
+
function upgradeInitializeAndLock(address implementation, bytes calldata data) external;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/// @title Proxy
|
|
11
|
+
/// @notice The proxy every crossecute conduit is deployed as, transmitter and receiver alike,
|
|
12
|
+
/// and every transceiver, through `ProxyDeployer`. Its implementation is locked
|
|
13
|
+
/// once armed; its owner's authority is unchanged.
|
|
14
|
+
///
|
|
15
|
+
/// @dev No constructor arguments, so its initcode is one constant and a transmitter (from its
|
|
16
|
+
/// home's transceiver) and a receiver (from another chain's) at the same deployer address
|
|
17
|
+
/// and salt land on one address. An EIP-1167 clone embeds its implementation in the initcode and could not.
|
|
18
|
+
///
|
|
19
|
+
/// @dev The deployer can arm the proxy only while no implementation is set. The single admin
|
|
20
|
+
/// operation installs the implementation, runs its initializer, and by installing it ends
|
|
21
|
+
/// the deployer's ability to arm, so no conduit or transceiver ever has a live upgrade key
|
|
22
|
+
/// and real logic at once. An implementation must not write the ERC-1967 implementation
|
|
23
|
+
/// slot: clearing it would let the deployer arm again.
|
|
24
|
+
///
|
|
25
|
+
/// @dev The deployer is an immutable, so it sits in the runtime code and not the initcode: the
|
|
26
|
+
/// address is unchanged, but the code hash differs per deployer, and a proxy is verified by
|
|
27
|
+
/// its CREATE2 address. A call on an armed proxy reads the implementation slot and nothing else.
|
|
28
|
+
///
|
|
29
|
+
/// @dev The admin operation is routed in `fallback`, not declared, so it shadows no selector.
|
|
30
|
+
/// Once armed every call delegates, as in a plain ERC-1967 proxy.
|
|
31
|
+
contract Proxy {
|
|
32
|
+
// forge-lint: disable-next-line(unused-error) reverted in assembly; declared for the ABI
|
|
33
|
+
error UnknownAdminCall(bytes4 selector);
|
|
34
|
+
|
|
35
|
+
event Locked(address implementation);
|
|
36
|
+
|
|
37
|
+
// ERC1967Utils.IMPLEMENTATION_SLOT and the errors and events the arming call reports, as
|
|
38
|
+
// literals so assembly can read them; `ProxyDifferential` compares every one against
|
|
39
|
+
// the library-built reference.
|
|
40
|
+
//
|
|
41
|
+
// bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
|
|
42
|
+
bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
|
|
43
|
+
// keccak256("Upgraded(address)"), IERC1967
|
|
44
|
+
bytes32 private constant UPGRADED = 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b;
|
|
45
|
+
// keccak256("Locked(address)")
|
|
46
|
+
bytes32 private constant LOCKED = 0x44427e3003a08f22cf803894075ac0297524e09e521fc1c15bc91741ce3dc159;
|
|
47
|
+
// bytes4(keccak256("upgradeInitializeAndLock(address,bytes)"))
|
|
48
|
+
uint256 private constant ARM = 0x885088b2;
|
|
49
|
+
// bytes4(keccak256("UnknownAdminCall(bytes4)")), declared above
|
|
50
|
+
uint256 private constant UNKNOWN_ADMIN_CALL = 0xf3e81dfa;
|
|
51
|
+
// bytes4(keccak256("ERC1967InvalidImplementation(address)")), as in ERC1967Utils
|
|
52
|
+
uint256 private constant INVALID_IMPLEMENTATION = 0x4c9c8ce3;
|
|
53
|
+
// bytes4(keccak256("ERC1967NonPayable()")), as in ERC1967Utils
|
|
54
|
+
uint256 private constant NON_PAYABLE = 0xb398979f;
|
|
55
|
+
// bytes4(keccak256("FailedCall()")), as in OpenZeppelin's Errors
|
|
56
|
+
uint256 private constant FAILED_CALL = 0xd6bda275;
|
|
57
|
+
|
|
58
|
+
address private immutable ADMIN;
|
|
59
|
+
|
|
60
|
+
constructor() {
|
|
61
|
+
ADMIN = msg.sender;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fallback() external payable {
|
|
65
|
+
address current;
|
|
66
|
+
assembly {
|
|
67
|
+
current := sload(IMPLEMENTATION_SLOT) // implementation; zero while the proxy is blank
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Armed, or not the deployer: an ordinary call. A blank proxy delegates to no code and
|
|
71
|
+
// succeeds. `ADMIN` is read only while blank.
|
|
72
|
+
if (current != address(0) || msg.sender != ADMIN) {
|
|
73
|
+
assembly {
|
|
74
|
+
calldatacopy(0, 0, calldatasize()) // copy the whole call to memory at 0
|
|
75
|
+
let ok := delegatecall(gas(), current, 0, calldatasize(), 0, 0) // run it as this proxy
|
|
76
|
+
returndatacopy(0, 0, returndatasize()) // copy its output, or its revert data, to memory at 0
|
|
77
|
+
if iszero(ok) { revert(0, returndatasize()) } // pass a revert through unchanged
|
|
78
|
+
return(0, returndatasize()) // return its output
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
assembly {
|
|
83
|
+
let size := calldatasize() // total bytes of calldata
|
|
84
|
+
let sig := and(calldataload(0), shl(224, 0xffffffff)) // selector, left-aligned; zero-padded if short
|
|
85
|
+
if iszero(eq(sig, shl(224, ARM))) {
|
|
86
|
+
// anything but the arming selector is refused
|
|
87
|
+
mstore(0, shl(224, UNKNOWN_ADMIN_CALL)) // UnknownAdminCall(bytes4), selector left-aligned at 0
|
|
88
|
+
mstore(4, sig) // its argument, the selector received, at 4
|
|
89
|
+
revert(0, 36) // selector and one 32-byte argument
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// abi.decode(msg.data[4:], (address, bytes)), refusing what it refuses. A length over
|
|
93
|
+
// 2^64 reverts with no data here and with Panic(0x41) there.
|
|
94
|
+
if lt(size, 68) { revert(0, 0) } // needs the selector and both head words: 4 + 64
|
|
95
|
+
let implementation := calldataload(4) // first argument
|
|
96
|
+
if shr(160, implementation) { revert(0, 0) } // an address with high bits set is malformed
|
|
97
|
+
let offset := calldataload(36) // second argument: where the bytes sit, counted after the selector
|
|
98
|
+
if gt(offset, 0xffffffffffffffff) { revert(0, 0) } // keeps the additions below from wrapping
|
|
99
|
+
let start := add(offset, 4) // position of the length word in calldata
|
|
100
|
+
let length := calldataload(start) // number of initializer bytes; zero if read past the end
|
|
101
|
+
if gt(length, 0xffffffffffffffff) { revert(0, 0) } // keeps `data + length` from wrapping
|
|
102
|
+
let data := add(start, 32) // where the initializer bytes begin
|
|
103
|
+
if gt(add(data, length), size) { revert(0, 0) } // must end in the calldata; refuses a far start
|
|
104
|
+
|
|
105
|
+
if iszero(extcodesize(implementation)) {
|
|
106
|
+
// an implementation with no code, or the zero address, is refused
|
|
107
|
+
mstore(0, shl(224, INVALID_IMPLEMENTATION)) // ERC1967InvalidImplementation(address)
|
|
108
|
+
mstore(4, implementation) // its argument at 4
|
|
109
|
+
revert(0, 36) // selector and one 32-byte argument
|
|
110
|
+
}
|
|
111
|
+
sstore(IMPLEMENTATION_SLOT, implementation) // install it; the slot is now non-zero, which closes arming
|
|
112
|
+
log2(0, 0, UPGRADED, implementation) // Upgraded(address indexed implementation), as ERC-1967
|
|
113
|
+
|
|
114
|
+
switch length // with or without an initializer
|
|
115
|
+
case 0 {
|
|
116
|
+
// no initializer
|
|
117
|
+
if callvalue() {
|
|
118
|
+
// ether would have nowhere to go: refused, as ERC1967Utils does
|
|
119
|
+
mstore(0, shl(224, NON_PAYABLE)) // ERC1967NonPayable()
|
|
120
|
+
revert(0, 4) // selector only
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
default {
|
|
124
|
+
// run the initializer
|
|
125
|
+
let ptr := mload(0x40) // free memory, so the copy touches nothing in use
|
|
126
|
+
calldatacopy(ptr, data, length) // copy the initializer bytes
|
|
127
|
+
if iszero(delegatecall(gas(), implementation, ptr, length, 0, 0)) {
|
|
128
|
+
// run it as this proxy; a failure reverts the whole arming
|
|
129
|
+
if iszero(returndatasize()) {
|
|
130
|
+
// it reverted with no data
|
|
131
|
+
mstore(0, shl(224, FAILED_CALL)) // FailedCall(), so the revert is not silent
|
|
132
|
+
revert(0, 4) // selector only
|
|
133
|
+
}
|
|
134
|
+
returndatacopy(ptr, 0, returndatasize()) // copy its revert data
|
|
135
|
+
revert(ptr, returndatasize()) // pass it through unchanged
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
mstore(0, implementation) // the one data word of Locked
|
|
140
|
+
log1(0, 32, LOCKED) // Locked(address implementation)
|
|
141
|
+
return(0, 0) // the arming call returns nothing
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
|
|
5
|
+
import {Proxy, IProxy} from "./Proxy.sol";
|
|
6
|
+
|
|
7
|
+
// Where `ProxyDeployer` sits: Arachnid's factory at salt zero, so the same address on every
|
|
8
|
+
// standard EVM chain. A `Predetermined` chain is one whose transceivers were deployed through it
|
|
9
|
+
// (#33). A literal, since a zkSync or Tron registry's compiler would derive another;
|
|
10
|
+
// `ProxyDeployer.t.sol` pins it to solc's build.
|
|
11
|
+
address constant PROXY_DEPLOYER = 0x415DaAF39EB13593a0Fe5E140bc97C4C9a63e116;
|
|
12
|
+
|
|
13
|
+
/// @notice The CREATE2 salt `ProxyDeployer` deploys under for `deployedBy`'s `salt`, where
|
|
14
|
+
/// `deployedBy` is the account that calls `deploy`.
|
|
15
|
+
/// @dev The caller is hashed in so that nobody else can deploy at a caller's address first
|
|
16
|
+
/// with their own implementation. `ChainRegistry.predictTransceiver` derives with it.
|
|
17
|
+
function proxySalt(address deployedBy, bytes32 salt) pure returns (bytes32) {
|
|
18
|
+
return keccak256(abi.encode(deployedBy, salt));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @title ProxyDeployer
|
|
22
|
+
/// @notice Deploys a `Proxy` and arms it in one call, so a transceiver has no window in
|
|
23
|
+
/// which another caller could install its logic.
|
|
24
|
+
///
|
|
25
|
+
/// @dev Deployed through Arachnid's factory with no constructor arguments, so it has one address
|
|
26
|
+
/// on every standard EVM chain, and every transceiver it deploys for one caller and salt
|
|
27
|
+
/// shares one address too. A `Proxy` makes its deployer its admin, which is why the
|
|
28
|
+
/// proxy cannot be deployed through Arachnid's factory directly: that factory makes no
|
|
29
|
+
/// calls and could never arm it.
|
|
30
|
+
contract ProxyDeployer {
|
|
31
|
+
event Deployed(address indexed deployedBy, bytes32 salt, address proxy, address implementation);
|
|
32
|
+
|
|
33
|
+
function deploy(bytes32 salt, address implementation, bytes calldata data) external returns (address proxy) {
|
|
34
|
+
proxy = Create2.deploy(0, proxySalt(msg.sender, salt), type(Proxy).creationCode);
|
|
35
|
+
IProxy(proxy).upgradeInitializeAndLock(implementation, data);
|
|
36
|
+
emit Deployed(msg.sender, salt, proxy, implementation);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function predict(address deployedBy, bytes32 salt) external view returns (address) {
|
|
40
|
+
return Create2.computeAddress(proxySalt(deployedBy, salt), keccak256(type(Proxy).creationCode));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import {Proxy} from "./Proxy.sol";
|
|
5
|
+
|
|
6
|
+
/// @notice `Proxy`'s initcode held by another contract, which returns it to any call, so a
|
|
7
|
+
/// contract that deploys conduits reads it rather than carrying 0.6 KB of it in its own
|
|
8
|
+
/// runtime (#70).
|
|
9
|
+
///
|
|
10
|
+
/// @dev CREATE2 hashes the initcode bytes, not where they were read from, so a conduit deployed
|
|
11
|
+
/// from the loaded copy lands where `PROXY_INIT_CODE_HASH` predicts.
|
|
12
|
+
///
|
|
13
|
+
/// @dev Read by `STATICCALL` rather than `EXTCODECOPY`, which zksolc refuses to compile: every
|
|
14
|
+
/// contract a zkSync variant's imports reach is compiled for EraVM, the plain transceivers
|
|
15
|
+
/// among them.
|
|
16
|
+
library ProxyInitCode {
|
|
17
|
+
error StoreFailed();
|
|
18
|
+
|
|
19
|
+
/// @notice What every CREATE2 prediction of a conduit hashes.
|
|
20
|
+
function hash() internal pure returns (bytes32) {
|
|
21
|
+
return keccak256(type(Proxy).creationCode);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// @notice Deploy the code-only contract. `Proxy`'s initcode appears only in the caller's
|
|
25
|
+
/// initcode when this runs from a constructor.
|
|
26
|
+
function store() internal returns (address at) {
|
|
27
|
+
bytes memory initCode = type(Proxy).creationCode;
|
|
28
|
+
// PUSH2 len, DUP1, PUSH1 12, PUSH1 0, CODECOPY, PUSH1 0, RETURN: on any call, returns what
|
|
29
|
+
// follows this 12-byte prefix, the initcode.
|
|
30
|
+
// forge-lint: disable-next-line(unsafe-typecast) a constant 0.6 KB
|
|
31
|
+
bytes memory runtime = abi.encodePacked(hex"61", uint16(initCode.length), hex"80600c6000396000f3", initCode);
|
|
32
|
+
// The same prefix shape, 14 bytes with PUSH4: returns `runtime` as the new contract's code.
|
|
33
|
+
// forge-lint: disable-next-line(unsafe-typecast) a constant 0.6 KB
|
|
34
|
+
bytes memory creation = abi.encodePacked(hex"63", uint32(runtime.length), hex"80600e6000396000f3", runtime);
|
|
35
|
+
assembly {
|
|
36
|
+
at := create(0, add(creation, 0x20), mload(creation))
|
|
37
|
+
}
|
|
38
|
+
if (at == address(0)) revert StoreFailed();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @notice The initcode `store` kept at `at`.
|
|
42
|
+
function load(address at) internal view returns (bytes memory code) {
|
|
43
|
+
bool ok;
|
|
44
|
+
(ok, code) = at.staticcall("");
|
|
45
|
+
if (!ok || code.length == 0) revert StoreFailed();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Erc7930} from "../addressing/Erc7930.sol";
|
|
5
|
+
|
|
6
|
+
/// @title AddressDerive
|
|
7
|
+
/// @notice Deterministic address derivation across EVM and non-EVM chains, computed
|
|
8
|
+
/// on-chain inside an Ethereum transaction. Pure where possible, no chain state.
|
|
9
|
+
///
|
|
10
|
+
/// @dev Formerly ForeignCreate2. EVM derivations are now a real path rather than a
|
|
11
|
+
/// test-only comparison, because on an EVM destination the registry can resolve
|
|
12
|
+
/// locally and skip the round trip entirely.
|
|
13
|
+
///
|
|
14
|
+
/// @dev Scope. Everything here bottoms out in `keccak256` (native) or `sha256` (0x02).
|
|
15
|
+
/// Bitcoin's `ripemd160` lives in BitcoinDerive.sol, because
|
|
16
|
+
/// EraVM does not support that precompile and rejects any unit importing it, which a
|
|
17
|
+
/// zkSync transceiver needs this file for. Sui lives in SuiDerive.sol because BLAKE2b needs the
|
|
18
|
+
/// 0x09 precompile, which forces `view` rather than `pure`.
|
|
19
|
+
/// Not derivable on the EVM at any price: Aptos/Movement (SHA3-256, ~1e5 gas for
|
|
20
|
+
/// a hand-rolled Keccak-f), Starknet (Pedersen/Poseidon over the STARK curve),
|
|
21
|
+
/// Bitcoin P2TR (secp256k1 point addition). Route those through the `Unique` path.
|
|
22
|
+
library AddressDerive {
|
|
23
|
+
/* ====================================================================== */
|
|
24
|
+
/* EVM */
|
|
25
|
+
/* ====================================================================== */
|
|
26
|
+
|
|
27
|
+
/// @notice EIP-1014 CREATE2. The default resolution path.
|
|
28
|
+
function create2(address deployer, bytes32 salt, bytes32 initCodeHash) internal pure returns (address) {
|
|
29
|
+
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, initCodeHash)))));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// @notice Nonce-based CREATE, address = keccak256(rlp([deployer, nonce]))[12:].
|
|
33
|
+
/// @dev RLP has three cases for the nonce and getting them wrong is a classic bug:
|
|
34
|
+
/// 0 encodes as 0x80 (empty string, not 0x00), 1..127 encode as themselves,
|
|
35
|
+
/// and anything larger takes a 0x80+len prefix over minimal big-endian bytes.
|
|
36
|
+
///
|
|
37
|
+
/// @dev The list prefix is `0xc0 + payload`, and the payload grows with the nonce.
|
|
38
|
+
/// The address item is always 21 bytes (0x94 plus twenty), and the nonce item is
|
|
39
|
+
/// 1 byte in the first two arms but `1 + len` in the third, so the prefix is
|
|
40
|
+
/// 0xd6 in the first two and `0xd6 + len` in the third. It read `0xd5 + len`
|
|
41
|
+
/// until `test/CreateDerivation.t.sol` was written. Every nonce at or above 128
|
|
42
|
+
/// produced a well-formed address that was not the one Ethereum would use. Only
|
|
43
|
+
/// `create3` called this, always with a hardcoded nonce of 1, so nothing
|
|
44
|
+
/// exercised the arm. `VmDeriver.Scheme.EvmCreate` takes the nonce from
|
|
45
|
+
/// registry params and could have reached it with any value.
|
|
46
|
+
function create(address deployer, uint256 nonce) internal pure returns (address) {
|
|
47
|
+
bytes memory rlp;
|
|
48
|
+
if (nonce == 0) {
|
|
49
|
+
rlp = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80));
|
|
50
|
+
} else if (nonce <= 0x7f) {
|
|
51
|
+
// forge-lint: disable-next-line(unsafe-typecast) nonce <= 0x7f in this branch
|
|
52
|
+
rlp = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce));
|
|
53
|
+
} else {
|
|
54
|
+
// Minimal big-endian, the same rule the eip155 chain reference uses.
|
|
55
|
+
// Borrowed from `Erc7930` rather than restated: `derivation` imports
|
|
56
|
+
// `addressing` and never the reverse, so this is the direction the
|
|
57
|
+
// dependency graph already runs, and one encoder cannot drift from itself.
|
|
58
|
+
bytes memory n = Erc7930.minimalBigEndian(nonce);
|
|
59
|
+
// forge-lint: disable-next-line(unsafe-typecast) n is at most 32 bytes
|
|
60
|
+
uint8 nLen = uint8(n.length);
|
|
61
|
+
rlp = abi.encodePacked(bytes1(0xd6 + nLen), bytes1(0x94), deployer, bytes1(0x80 + nLen), n);
|
|
62
|
+
}
|
|
63
|
+
return address(uint160(uint256(keccak256(rlp))));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @notice Creation-code hash of an EIP-1167 minimal proxy for `implementation`.
|
|
67
|
+
/// @dev For a destination whose contracts are EIP-1167 clones, reached through
|
|
68
|
+
/// `VmDeriver`'s clone schemes: the address is CREATE2 over the deployer, the salt, and
|
|
69
|
+
/// this hash. This protocol's own conduits are `Proxy`, not clones.
|
|
70
|
+
///
|
|
71
|
+
/// The 55-byte layout is EIP-1167 verbatim: 10 bytes of creation code, then the
|
|
72
|
+
/// 45-byte runtime with the implementation address spliced in at byte 20:
|
|
73
|
+
/// 3d602d80600a3d3981f3 363d3d373d3d3d363d73 <impl> 5af43d82803e903d91602b57fd5bf3
|
|
74
|
+
/// Verified against OpenZeppelin's `Clones.predictDeterministicAddress` in tests
|
|
75
|
+
/// rather than trusted from memory. If OZ ever changes its proxy bytecode, this
|
|
76
|
+
/// constant is wrong and the test fails.
|
|
77
|
+
function cloneInitCodeHash(address implementation) internal pure returns (bytes32) {
|
|
78
|
+
return keccak256(
|
|
79
|
+
abi.encodePacked(
|
|
80
|
+
hex"3d602d80600a3d3981f3363d3d373d3d3d363d73", implementation, hex"5af43d82803e903d91602b57fd5bf3"
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// @notice Address of an EIP-1167 clone deployed with CREATE2.
|
|
86
|
+
function clone2(address deployer, address implementation, bytes32 salt) internal pure returns (address) {
|
|
87
|
+
return create2(deployer, salt, cloneInitCodeHash(implementation));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// @dev Canonical CREATE3 proxy initcode: PUSH8 363d3d37363d34f0; 3d5260086018f3.
|
|
91
|
+
/// Constant across the 0xsequence, Solady, and LI.FI variants. Verified by
|
|
92
|
+
/// hashing the literal rather than copied from a header file.
|
|
93
|
+
bytes32 internal constant CREATE3_PROXY_INITCODE_HASH =
|
|
94
|
+
0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f;
|
|
95
|
+
|
|
96
|
+
/// @notice CREATE3: address depends only on (factory, salt), never on bytecode.
|
|
97
|
+
/// @dev Two stages. A fixed-initcode proxy is CREATE2-deployed, then that proxy
|
|
98
|
+
/// does a plain CREATE at nonce 1. The nonce is a hardcoded 1 from a fresh
|
|
99
|
+
/// contract, not the deployer's transaction history.
|
|
100
|
+
///
|
|
101
|
+
/// Does not port to zkSync (both stages use different formulas) or to Tron
|
|
102
|
+
/// (CREATE derives from root tx id, so stage 2 is not a function of the proxy
|
|
103
|
+
/// address). Use only for chains sharing Ethereum's derivation.
|
|
104
|
+
function create3(address factory, bytes32 salt) internal pure returns (address) {
|
|
105
|
+
address proxy = create2(factory, salt, CREATE3_PROXY_INITCODE_HASH);
|
|
106
|
+
return create(proxy, 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ====================================================================== */
|
|
110
|
+
/* L1 -> L2 sender aliasing */
|
|
111
|
+
/* ====================================================================== */
|
|
112
|
+
|
|
113
|
+
/// @dev Shared by three stacks, which is why it is not under any of them. Arbitrum,
|
|
114
|
+
/// zkSync Era, and the OP Stack's `OptimismPortal` all rewrite the sender of an
|
|
115
|
+
/// L1-originated message by the same constant, and Arbitrum's own
|
|
116
|
+
/// `AddressAliasHelper.OFFSET` is this value. Filing it under one of them invites
|
|
117
|
+
/// the next binding to write a second copy.
|
|
118
|
+
uint160 internal constant L1_TO_L2_ALIAS_OFFSET = uint160(0x1111000000000000000000000000000000001111);
|
|
119
|
+
|
|
120
|
+
/// @notice The address an L1 contract appears as when its message arrives on L2.
|
|
121
|
+
///
|
|
122
|
+
/// @dev Contracts only. An EOA is not aliased, because the collision this prevents is a
|
|
123
|
+
/// contract at some address on L1 impersonating a different contract that happens
|
|
124
|
+
/// to sit at the same address on L2.
|
|
125
|
+
function applyL1ToL2Alias(address l1) internal pure returns (address) {
|
|
126
|
+
unchecked {
|
|
127
|
+
return address(uint160(l1) + L1_TO_L2_ALIAS_OFFSET);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// @notice Recover the L1 address from the sender an L2 actually observed.
|
|
132
|
+
///
|
|
133
|
+
/// @dev This is the direction a binding needs, and the one whose absence is a silent bug
|
|
134
|
+
/// rather than a revert. `ReceiverBase` compares an inbound sender against
|
|
135
|
+
/// `sourceTransmitter`, which holds the transmitter's address on the home chain; an
|
|
136
|
+
/// L2 receiver sees that address plus the offset, so every inbound message is
|
|
137
|
+
/// refused until the binding subtracts it back out.
|
|
138
|
+
///
|
|
139
|
+
/// @dev It is L1 -> L2 only, and applying it symmetrically corrupts the other direction.
|
|
140
|
+
/// A withdrawal carries the raw L2 sender: Arbitrum's `Outbox` hashes `l2Sender`
|
|
141
|
+
/// unaliased into the leaf it proves against. So a binding un-aliases on the
|
|
142
|
+
/// inbound-to-L2 path and nowhere else.
|
|
143
|
+
///
|
|
144
|
+
/// @dev It cannot tell whether it was needed. The arithmetic wraps, so undoing an alias
|
|
145
|
+
/// that was never applied returns a perfectly well-formed address belonging to
|
|
146
|
+
/// nobody rather than failing. There is no "was this aliased" predicate and there
|
|
147
|
+
/// cannot be one: the caller decides from the direction of the message, never from
|
|
148
|
+
/// the value. That is why this is a binding's job and not `ReceiverBase`'s, which
|
|
149
|
+
/// cannot know which transport delivered.
|
|
150
|
+
///
|
|
151
|
+
/// @dev Not every binding needs it. The OP Stack's `CrossDomainMessenger` un-aliases
|
|
152
|
+
/// internally and reports the original through `xDomainMessageSender()`, so only a
|
|
153
|
+
/// binding built directly on `OptimismPortal` has to. Arbitrum and zkSync have no
|
|
154
|
+
/// equivalent.
|
|
155
|
+
function undoL1ToL2Alias(address l2) internal pure returns (address) {
|
|
156
|
+
unchecked {
|
|
157
|
+
return address(uint160(l2) - L1_TO_L2_ALIAS_OFFSET);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* ====================================================================== */
|
|
162
|
+
/* zkSync Era */
|
|
163
|
+
/* ====================================================================== */
|
|
164
|
+
|
|
165
|
+
bytes32 internal constant ZKSYNC_CREATE2_PREFIX = keccak256("zksyncCreate2");
|
|
166
|
+
bytes32 internal constant ZKSYNC_CREATE_PREFIX = keccak256("zksyncCreate");
|
|
167
|
+
|
|
168
|
+
/// @param sender If an L1 *contract* triggers the deploy via Bridgehub/Mailbox, pass
|
|
169
|
+
/// the aliased address. EOAs are not aliased.
|
|
170
|
+
/// @param bytecodeHash EraVM versioned hash: see `hashL2Bytecode`, not keccak(initcode).
|
|
171
|
+
function zksyncCreate2(address sender, bytes32 salt, bytes32 bytecodeHash, bytes32 constructorInputHash)
|
|
172
|
+
internal
|
|
173
|
+
pure
|
|
174
|
+
returns (address)
|
|
175
|
+
{
|
|
176
|
+
return address(
|
|
177
|
+
uint160(
|
|
178
|
+
uint256(
|
|
179
|
+
keccak256(
|
|
180
|
+
abi.encodePacked(
|
|
181
|
+
ZKSYNC_CREATE2_PREFIX,
|
|
182
|
+
bytes32(uint256(uint160(sender))),
|
|
183
|
+
salt,
|
|
184
|
+
bytecodeHash,
|
|
185
|
+
constructorInputHash
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function zksyncCreate(address sender, uint256 nonce) internal pure returns (address) {
|
|
194
|
+
return address(
|
|
195
|
+
uint160(
|
|
196
|
+
uint256(
|
|
197
|
+
keccak256(abi.encodePacked(ZKSYNC_CREATE_PREFIX, bytes32(uint256(uint160(sender))), bytes32(nonce)))
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/// @dev [0]=0x01 version, [1]=0x00, [2:4]=length in words, [4:32]=trailing 28 bytes
|
|
204
|
+
/// of sha256(bytecode). Invariants mirror EraVM. Requires zksolc output.
|
|
205
|
+
function hashL2Bytecode(bytes memory bytecode) internal pure returns (bytes32 h) {
|
|
206
|
+
require(bytecode.length % 32 == 0, "C2L: not word-aligned");
|
|
207
|
+
uint256 words = bytecode.length / 32;
|
|
208
|
+
require(words < 2 ** 16, "C2L: bytecode too long");
|
|
209
|
+
require(words % 2 == 1, "C2L: even word count");
|
|
210
|
+
h = sha256(bytecode);
|
|
211
|
+
h = h & bytes32(uint256(type(uint224).max));
|
|
212
|
+
h = h | bytes32(uint256(1) << 248);
|
|
213
|
+
h = h | bytes32(words << 224);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ====================================================================== */
|
|
217
|
+
/* Tron */
|
|
218
|
+
/* ====================================================================== */
|
|
219
|
+
|
|
220
|
+
/// @notice Same 85-byte preimage as EIP-1014; only the domain byte differs (0x41).
|
|
221
|
+
/// @dev Returns the bare 20-byte in-VM form. Prepend 0x41 + Base58Check off-chain.
|
|
222
|
+
/// initCodeHash must come from Tron-solc, not solc.
|
|
223
|
+
/// java-tron's CREATE2 hashes the sender in its 21-byte form, `0x41 ‖ deployer`
|
|
224
|
+
/// (`WalletUtil.generateContractAddress2`), which is where the 0x41 comes from.
|
|
225
|
+
function tronCreate2(address deployer, bytes32 salt, bytes32 initCodeHash) internal pure returns (address) {
|
|
226
|
+
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0x41), deployer, salt, initCodeHash)))));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* ====================================================================== */
|
|
230
|
+
/* Cosmos / CosmWasm */
|
|
231
|
+
/* ====================================================================== */
|
|
232
|
+
|
|
233
|
+
/// @dev sha256("module"), the ADR-028 domain tag.
|
|
234
|
+
bytes32 internal constant COSMOS_MODULE_TAG = 0x120970d812836f19888625587a4606a5ad23cef31c8684e601771552548fc6b9;
|
|
235
|
+
|
|
236
|
+
/// @notice CosmWasm Instantiate2: the CREATE2 analogue on Cosmos.
|
|
237
|
+
/// @dev sha256( sha256("module") ++ key ), key = "wasm\0" ++ be64/value pairs.
|
|
238
|
+
/// The "wasm\0" prefix exists in cosmwasm-std but is absent from the published
|
|
239
|
+
/// spec page. Verified against cosmwasm-std's own test vectors.
|
|
240
|
+
/// @param creator Canonical decoded address bytes, not the bech32 string.
|
|
241
|
+
function cosmosInstantiate2(bytes32 checksum, bytes memory creator, bytes memory salt, bytes memory initMsg)
|
|
242
|
+
internal
|
|
243
|
+
pure
|
|
244
|
+
returns (bytes32)
|
|
245
|
+
{
|
|
246
|
+
require(salt.length >= 1 && salt.length <= 64, "C2L: bad salt len");
|
|
247
|
+
// Each variable field is length-prefixed, and a memory length fits in 64 bits.
|
|
248
|
+
// forge-lint: disable-start(encode-packed-collision,unsafe-typecast)
|
|
249
|
+
bytes memory key = abi.encodePacked(
|
|
250
|
+
hex"7761736d00", // "wasm\0"
|
|
251
|
+
uint64(32),
|
|
252
|
+
checksum,
|
|
253
|
+
uint64(creator.length),
|
|
254
|
+
creator,
|
|
255
|
+
uint64(salt.length),
|
|
256
|
+
salt,
|
|
257
|
+
uint64(initMsg.length),
|
|
258
|
+
initMsg
|
|
259
|
+
);
|
|
260
|
+
// forge-lint: disable-end(encode-packed-collision,unsafe-typecast)
|
|
261
|
+
return sha256(abi.encodePacked(COSMOS_MODULE_TAG, key));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* ====================================================================== */
|
|
265
|
+
/* Solana */
|
|
266
|
+
/* ====================================================================== */
|
|
267
|
+
|
|
268
|
+
/// @notice PDA for a known bump.
|
|
269
|
+
/// @dev This is not `find_program_address`. The canonical PDA is the first bump
|
|
270
|
+
/// counting down from 255 whose output is off the ed25519 curve; that check
|
|
271
|
+
/// needs Edwards decompression over GF(2^255-19) and is not implemented.
|
|
272
|
+
/// A too-high bump yields an on-curve point this function will happily return.
|
|
273
|
+
/// Bind the bump into committed inputs so signers approve it explicitly.
|
|
274
|
+
function solanaCreateProgramAddress(bytes[] memory seeds, uint8 bump, bytes32 programId)
|
|
275
|
+
internal
|
|
276
|
+
pure
|
|
277
|
+
returns (bytes32)
|
|
278
|
+
{
|
|
279
|
+
require(seeds.length <= 16, "C2L: too many seeds");
|
|
280
|
+
bytes memory pre;
|
|
281
|
+
for (uint256 i; i < seeds.length; ++i) {
|
|
282
|
+
require(seeds[i].length <= 32, "C2L: seed too long");
|
|
283
|
+
// forge-lint: disable-next-line(encode-packed-collision) Solana's PDA preimage is the bare concatenation
|
|
284
|
+
pre = abi.encodePacked(pre, seeds[i]);
|
|
285
|
+
}
|
|
286
|
+
return sha256(abi.encodePacked(pre, bump, programId, "ProgramDerivedAddress"));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/// @notice SPL Associated Token Account.
|
|
290
|
+
/// @dev Seed order is [owner, tokenProgram, mint]: the token program sits in the
|
|
291
|
+
/// middle, which is easy to get backwards. Verified against solders.
|
|
292
|
+
function solanaAssociatedTokenAccount(
|
|
293
|
+
bytes32 owner,
|
|
294
|
+
bytes32 mint,
|
|
295
|
+
bytes32 tokenProgram,
|
|
296
|
+
bytes32 ataProgram,
|
|
297
|
+
uint8 bump
|
|
298
|
+
) internal pure returns (bytes32) {
|
|
299
|
+
return sha256(abi.encodePacked(owner, tokenProgram, mint, bump, ataProgram, "ProgramDerivedAddress"));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/// @notice SystemProgram::createWithSeed. No bump, no off-curve requirement.
|
|
303
|
+
function solanaCreateWithSeed(bytes32 base, bytes memory seed, bytes32 programId) internal pure returns (bytes32) {
|
|
304
|
+
return sha256(abi.encodePacked(base, seed, programId));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ====================================================================== */
|
|
308
|
+
/* NEAR */
|
|
309
|
+
/* ====================================================================== */
|
|
310
|
+
|
|
311
|
+
/// @notice NEAR deterministic account: NEAR's CREATE2, and free here since it
|
|
312
|
+
/// uses keccak256. Render as "0s" + lowercase hex, not "0x".
|
|
313
|
+
/// @param borshStateInit Borsh-serialized DeterministicAccountStateInit. Serialize
|
|
314
|
+
/// off-chain; do not build Borsh in Solidity.
|
|
315
|
+
function nearDeterministicAccount(bytes memory borshStateInit) internal pure returns (bytes20) {
|
|
316
|
+
// forge-lint: disable-next-line(unsafe-typecast) the address is the hash's low 20 bytes
|
|
317
|
+
return bytes20(uint160(uint256(keccak256(borshStateInit))));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/// @param uncompressedPubkey64 64 bytes, without the 0x04 SEC1 prefix.
|
|
321
|
+
function nearEthImplicitAccount(bytes memory uncompressedPubkey64) internal pure returns (bytes20) {
|
|
322
|
+
require(uncompressedPubkey64.length == 64, "C2L: bad pubkey len");
|
|
323
|
+
// forge-lint: disable-next-line(unsafe-typecast) the address is the hash's low 20 bytes
|
|
324
|
+
return bytes20(uint160(uint256(keccak256(uncompressedPubkey64))));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// @title BitcoinDerive
|
|
5
|
+
/// @notice Bitcoin script and witness-program hashing, for `bip122` references.
|
|
6
|
+
///
|
|
7
|
+
/// @dev Split out of `AddressDerive` to keep `ripemd160` off EraVM, and that is a compile
|
|
8
|
+
/// constraint rather than a taste one. zkSync's compiler rejects the `ripemd160`
|
|
9
|
+
/// precompile outright, and it rejects the whole compilation unit rather than the
|
|
10
|
+
/// unreachable function: a zkSync transceiver needs `AddressDerive.zksyncCreate2` and would
|
|
11
|
+
/// otherwise drag `hash160` in with it and fail to build. Nothing else separated these,
|
|
12
|
+
/// since both halves are "hash bytes with a precompile".
|
|
13
|
+
///
|
|
14
|
+
/// @dev Nothing on a destination calls this. Bitcoin has no executor and no receiver: `bip122`
|
|
15
|
+
/// appears in the registry as a reference that can be validated and addressed, never
|
|
16
|
+
/// as a destination a payload runs on. Its only caller is `VmDeriver`, which nothing
|
|
17
|
+
/// deployed on EraVM imports, so `VmDeriver` cannot be built for EraVM either.
|
|
18
|
+
library BitcoinDerive {
|
|
19
|
+
/// @notice HASH160 = ripemd160(sha256(x)). P2PKH and P2WPKH witness programs.
|
|
20
|
+
function hash160(bytes memory data) internal pure returns (bytes20) {
|
|
21
|
+
return ripemd160(abi.encodePacked(sha256(data)));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// @notice P2WSH witness program = sha256(witnessScript).
|
|
25
|
+
function p2wshProgram(bytes memory witnessScript) internal pure returns (bytes32) {
|
|
26
|
+
return sha256(witnessScript);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @notice P2SH script hash = hash160(redeemScript).
|
|
30
|
+
function p2shScriptHash(bytes memory redeemScript) internal pure returns (bytes20) {
|
|
31
|
+
return hash160(redeemScript);
|
|
32
|
+
}
|
|
33
|
+
}
|