@rootzero/contracts 1.19.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +73 -0
- package/Codec.sol +1 -1
- package/Commands.sol +1 -0
- package/Endpoints.sol +5 -4
- package/Events.sol +3 -2
- package/README.md +18 -12
- package/Utils.sol +0 -1
- package/codec/Decoders.sol +9 -1
- package/codec/Descriptors.sol +28 -28
- package/codec/Schema.sol +5 -5
- package/codec/Specs.sol +9 -27
- package/commands/Allocate.sol +1 -1
- package/commands/Base.sol +4 -10
- package/commands/Burn.sol +1 -1
- package/commands/Credit.sol +3 -3
- package/commands/Debit.sol +3 -3
- package/commands/Deposit.sol +3 -10
- package/commands/Payout.sol +1 -1
- package/commands/Provision.sol +3 -3
- package/commands/Recover.sol +3 -3
- package/commands/Relay.sol +20 -15
- package/commands/Repay.sol +3 -3
- package/commands/Settle.sol +5 -5
- package/commands/Withdraw.sol +1 -1
- package/commands/admin/AllowAssets.sol +2 -2
- package/commands/admin/Allowance.sol +2 -2
- package/commands/admin/Annotate.sol +2 -2
- package/commands/admin/Appoint.sol +2 -2
- package/commands/admin/Authorize.sol +2 -2
- package/commands/admin/Base.sol +1 -1
- package/commands/admin/DenyAssets.sol +2 -8
- package/commands/admin/Dismiss.sol +2 -2
- package/commands/admin/Execute.sol +2 -2
- package/commands/admin/Unauthorize.sol +2 -2
- package/core/Portal.sol +11 -8
- package/docs/Schema.md +53 -29
- package/events/Relay.sol +20 -0
- package/events/Resolved.sol +17 -0
- package/events/Unresolved.sol +18 -0
- package/execution/Execution.sol +10 -1
- package/guards/Base.sol +1 -2
- package/package.json +1 -1
- package/ports/AllowAssets.sol +1 -1
- package/ports/Allowance.sol +1 -1
- package/ports/Base.sol +4 -5
- package/ports/Credit.sol +1 -1
- package/ports/Debit.sol +1 -1
- package/ports/DenyAssets.sol +1 -1
- package/ports/Dispatch.sol +19 -7
- package/ports/Pipe.sol +2 -1
- package/ports/Post.sol +1 -1
- package/ports/Redeem.sol +1 -1
- package/queries/Base.sol +1 -2
- package/utils/Nodes.sol +21 -16
- package/events/Recovered.sol +0 -17
- package/events/Undelivered.sol +0 -18
- package/utils/Selectors.sol +0 -49
package/guards/Base.sol
CHANGED
|
@@ -5,7 +5,6 @@ import {InputEndpointBase} from "../core/Endpoint.sol";
|
|
|
5
5
|
import {GuardianAccess} from "../core/Access.sol";
|
|
6
6
|
import {Specs} from "../codec/Specs.sol";
|
|
7
7
|
import {Nodes} from "../utils/Nodes.sol";
|
|
8
|
-
import {Selectors} from "../utils/Selectors.sol";
|
|
9
8
|
|
|
10
9
|
/// @title GuardBase
|
|
11
10
|
/// @notice Abstract base for guardian-only direct host actions.
|
|
@@ -27,7 +26,7 @@ abstract contract GuardBase is GuardianAccess, InputEndpointBase {
|
|
|
27
26
|
string memory name,
|
|
28
27
|
uint input
|
|
29
28
|
) internal returns (uint id, uint descriptor) {
|
|
30
|
-
id = Nodes.toGuard(
|
|
29
|
+
id = Nodes.toGuard(name, address(this));
|
|
31
30
|
descriptor = endpoint(id, name, Specs.Empty, input, Specs.Empty, 0, 0);
|
|
32
31
|
}
|
|
33
32
|
}
|
package/package.json
CHANGED
package/ports/AllowAssets.sol
CHANGED
|
@@ -15,7 +15,7 @@ abstract contract AllowAssetsPort is PortBase, AllowAssetsHook {
|
|
|
15
15
|
uint private immutable descriptor;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
(, descriptor) = port("portAllowAssets", Specs.Asset, Specs.Empty,
|
|
18
|
+
(, descriptor) = port("portAllowAssets", Specs.Asset, Specs.Empty, 0);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the allow-assets peer call.
|
package/ports/Allowance.sol
CHANGED
|
@@ -16,7 +16,7 @@ abstract contract AllowancePort is PortBase, AllowanceHook {
|
|
|
16
16
|
uint private immutable descriptor;
|
|
17
17
|
|
|
18
18
|
constructor() {
|
|
19
|
-
(, descriptor) = port("portAllowance", Specs.Amount, Specs.Empty,
|
|
19
|
+
(, descriptor) = port("portAllowance", Specs.Amount, Specs.Empty, 0);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/// @notice Execute the allowance port call.
|
package/ports/Base.sol
CHANGED
|
@@ -6,7 +6,6 @@ import { NodeAccess } from "../core/Access.sol";
|
|
|
6
6
|
import { Specs } from "../codec/Specs.sol";
|
|
7
7
|
import { InputEndpointBase } from "../core/Endpoint.sol";
|
|
8
8
|
import { Nodes } from "../utils/Nodes.sol";
|
|
9
|
-
import { Selectors } from "../utils/Selectors.sol";
|
|
10
9
|
import { Descriptors } from "../codec/Descriptors.sol";
|
|
11
10
|
|
|
12
11
|
/// @title PortBase
|
|
@@ -33,16 +32,16 @@ abstract contract PortBase is NodeCalls, NodeAccess, InputEndpointBase {
|
|
|
33
32
|
/// match the Solidity port function name used by the canonical ABI.
|
|
34
33
|
/// @param input Input block specification.
|
|
35
34
|
/// @param output Output block specification.
|
|
36
|
-
/// @param
|
|
35
|
+
/// @param flags Packed port behavior flags.
|
|
37
36
|
/// @return id Port node ID.
|
|
38
37
|
/// @return descriptor Packed endpoint lane metadata and flags.
|
|
39
38
|
function port(
|
|
40
39
|
string memory name,
|
|
41
40
|
uint input,
|
|
42
41
|
uint output,
|
|
43
|
-
|
|
42
|
+
uint8 flags
|
|
44
43
|
) internal returns (uint id, uint descriptor) {
|
|
45
|
-
descriptor = Descriptors.create(Specs.Empty, input, output, 0,
|
|
44
|
+
descriptor = Descriptors.create(Specs.Empty, input, output, 0, flags);
|
|
46
45
|
return port(name, descriptor);
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -56,7 +55,7 @@ abstract contract PortBase is NodeCalls, NodeAccess, InputEndpointBase {
|
|
|
56
55
|
string memory name,
|
|
57
56
|
uint descriptor
|
|
58
57
|
) internal returns (uint id, uint published) {
|
|
59
|
-
id = Nodes.toPort(
|
|
58
|
+
id = Nodes.toPort(name, address(this));
|
|
60
59
|
published = endpoint(id, name, descriptor);
|
|
61
60
|
}
|
|
62
61
|
}
|
package/ports/Credit.sol
CHANGED
|
@@ -15,7 +15,7 @@ abstract contract CreditAccountPort is PortBase, CreditAccountHook {
|
|
|
15
15
|
uint private immutable descriptor;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
(, descriptor) = port("portCreditAccount", Specs.AccountAmount, Specs.Empty,
|
|
18
|
+
(, descriptor) = port("portCreditAccount", Specs.AccountAmount, Specs.Empty, 0);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the port-credit call.
|
package/ports/Debit.sol
CHANGED
|
@@ -15,7 +15,7 @@ abstract contract DebitAccountPort is PortBase, DebitAccountHook {
|
|
|
15
15
|
uint private immutable descriptor;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
(, descriptor) = port("portDebitAccount", Specs.AccountAmount, Specs.Empty,
|
|
18
|
+
(, descriptor) = port("portDebitAccount", Specs.AccountAmount, Specs.Empty, 0);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the port-debit call.
|
package/ports/DenyAssets.sol
CHANGED
|
@@ -15,7 +15,7 @@ abstract contract DenyAssetsPort is PortBase, DenyAssetsHook {
|
|
|
15
15
|
uint private immutable descriptor;
|
|
16
16
|
|
|
17
17
|
constructor() {
|
|
18
|
-
(, descriptor) = port("portDenyAssets", Specs.Asset, Specs.Empty,
|
|
18
|
+
(, descriptor) = port("portDenyAssets", Specs.Asset, Specs.Empty, 0);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// @notice Execute the deny-assets peer call.
|
package/ports/Dispatch.sol
CHANGED
|
@@ -2,23 +2,35 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
-
import {
|
|
5
|
+
import {Flags} from "../codec/Descriptors.sol";
|
|
6
6
|
import {Specs} from "../Codec.sol";
|
|
7
7
|
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
8
8
|
|
|
9
9
|
using Executions for Execution;
|
|
10
10
|
|
|
11
|
+
/// @notice Hook implemented by hosts that forward funded dispatch payloads.
|
|
12
|
+
abstract contract DispatchPayableHook {
|
|
13
|
+
/// @notice Override to dispatch an encoded payload to `portal`.
|
|
14
|
+
/// @param portal Destination portal identifier, often the destination host ID.
|
|
15
|
+
/// @param resources Chain-specific destination resources. EVM adapters
|
|
16
|
+
/// may interpret this as packed execution gas and destination value.
|
|
17
|
+
/// @param payload Encoded payload ready for the transport layer.
|
|
18
|
+
/// @param funds Execution used for source value available for transport fees
|
|
19
|
+
/// and destination resource funding.
|
|
20
|
+
function dispatchTo(uint portal, uint resources, bytes memory payload, Execution memory funds) internal virtual;
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
/// @title DispatchPayablePort
|
|
12
|
-
/// @notice Port endpoint that forwards DISPATCH blocks to a host-defined
|
|
13
|
-
abstract contract DispatchPayablePort is PortBase,
|
|
24
|
+
/// @notice Port endpoint that forwards DISPATCH blocks to a host-defined dispatch hook.
|
|
25
|
+
abstract contract DispatchPayablePort is PortBase, DispatchPayableHook {
|
|
14
26
|
uint private immutable descriptor;
|
|
15
27
|
|
|
16
28
|
constructor() {
|
|
17
|
-
(, descriptor) = port("portDispatchPayable", Specs.Dispatch, Specs.Empty,
|
|
29
|
+
(, descriptor) = port("portDispatchPayable", Specs.Dispatch, Specs.Empty, Flags.Funded);
|
|
18
30
|
}
|
|
19
31
|
|
|
20
|
-
/// @notice Forward peer-supplied dispatches to the host-defined
|
|
21
|
-
/// @dev
|
|
32
|
+
/// @notice Forward peer-supplied dispatches to the host-defined dispatch hook.
|
|
33
|
+
/// @dev Dispatch hooks receive the shared top-level source value
|
|
22
34
|
/// budget. Any `msg.value` not spent by the hook remains on this host.
|
|
23
35
|
/// @param data DISPATCH block stream supplied by the trusted peer.
|
|
24
36
|
/// @return Empty response bytes.
|
|
@@ -27,7 +39,7 @@ abstract contract DispatchPayablePort is PortBase, RelayPayableHook {
|
|
|
27
39
|
|
|
28
40
|
while (exec.more()) {
|
|
29
41
|
(uint portal, uint resources, bytes calldata payload) = exec.unpackDispatch(Lanes.Input);
|
|
30
|
-
|
|
42
|
+
dispatchTo(portal, resources, payload, exec);
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
return "";
|
package/ports/Pipe.sol
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
pragma solidity ^0.8.33;
|
|
3
3
|
|
|
4
4
|
import {PortBase} from "./Base.sol";
|
|
5
|
+
import {Flags} from "../codec/Descriptors.sol";
|
|
5
6
|
import {Pipeline} from "../core/Pipeline.sol";
|
|
6
7
|
import {Specs} from "../Codec.sol";
|
|
7
8
|
import {Execution, Executions, Lanes} from "../execution/Execution.sol";
|
|
@@ -16,7 +17,7 @@ abstract contract PipePayablePort is PortBase, Pipeline {
|
|
|
16
17
|
uint private immutable descriptor;
|
|
17
18
|
|
|
18
19
|
constructor() {
|
|
19
|
-
(, descriptor) = port("portPipePayable", Specs.Context, Specs.Empty,
|
|
20
|
+
(, descriptor) = port("portPipePayable", Specs.Context, Specs.Empty, Flags.Funded);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/// @notice Execute peer-supplied contexts through the shared payable pipe.
|
package/ports/Post.sol
CHANGED
|
@@ -18,7 +18,7 @@ abstract contract PostPort is PortBase, PostHook, Action {
|
|
|
18
18
|
|
|
19
19
|
constructor() {
|
|
20
20
|
uint id;
|
|
21
|
-
(id, descriptor) = port("portPost", Specs.Transaction, Specs.Empty,
|
|
21
|
+
(id, descriptor) = port("portPost", Specs.Transaction, Specs.Empty, 0);
|
|
22
22
|
action(id, Actions.Post);
|
|
23
23
|
}
|
|
24
24
|
|
package/ports/Redeem.sol
CHANGED
|
@@ -24,7 +24,7 @@ abstract contract RedeemBalancePort is PortBase, RedeemBalanceHook {
|
|
|
24
24
|
uint private immutable descriptor;
|
|
25
25
|
|
|
26
26
|
constructor() {
|
|
27
|
-
(, descriptor) = port("portRedeemBalance", Specs.Balance, Specs.Empty,
|
|
27
|
+
(, descriptor) = port("portRedeemBalance", Specs.Balance, Specs.Empty, 0);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/// @notice Execute the balance redemption port call.
|
package/queries/Base.sol
CHANGED
|
@@ -5,7 +5,6 @@ import { InputEndpointBase } from "../core/Endpoint.sol";
|
|
|
5
5
|
import { Descriptors } from "../codec/Descriptors.sol";
|
|
6
6
|
import { Specs } from "../codec/Specs.sol";
|
|
7
7
|
import { Nodes } from "../utils/Nodes.sol";
|
|
8
|
-
import { Selectors } from "../utils/Selectors.sol";
|
|
9
8
|
|
|
10
9
|
/// @title QueryBase
|
|
11
10
|
/// @notice Abstract base for rootzero query contracts.
|
|
@@ -39,7 +38,7 @@ abstract contract QueryBase is InputEndpointBase {
|
|
|
39
38
|
string memory name,
|
|
40
39
|
uint descriptor
|
|
41
40
|
) internal returns (uint id, uint published) {
|
|
42
|
-
id = Nodes.toQuery(
|
|
41
|
+
id = Nodes.toQuery(name, address(this));
|
|
43
42
|
published = endpoint(id, name, descriptor);
|
|
44
43
|
}
|
|
45
44
|
}
|
package/utils/Nodes.sol
CHANGED
|
@@ -192,40 +192,45 @@ library Nodes {
|
|
|
192
192
|
return toLocalBase(Host) | uint(uint160(target));
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
/// @notice Build a chain-local command ID for the given
|
|
196
|
-
/// @param
|
|
195
|
+
/// @notice Build a chain-local command ID for the given endpoint name and contract.
|
|
196
|
+
/// @param name Command function name.
|
|
197
197
|
/// @param target Command contract address.
|
|
198
198
|
/// @return node Command node ID embedding both the selector and address.
|
|
199
|
-
function toCommand(
|
|
199
|
+
function toCommand(string memory name, address target) internal view returns (uint node) {
|
|
200
200
|
node = toLocalBase(Command) | uint(uint160(target));
|
|
201
|
-
node |= uint(uint32(
|
|
201
|
+
node |= uint(uint32(toSelector(name, "(bytes32,bytes,bytes)"))) << 160;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
/// @notice Build a chain-local port ID for the given
|
|
205
|
-
/// @param
|
|
204
|
+
/// @notice Build a chain-local port ID for the given endpoint name and contract.
|
|
205
|
+
/// @param name Port function name.
|
|
206
206
|
/// @param target Port contract address.
|
|
207
207
|
/// @return node Port node ID embedding both the selector and address.
|
|
208
|
-
function toPort(
|
|
208
|
+
function toPort(string memory name, address target) internal view returns (uint node) {
|
|
209
209
|
node = toLocalBase(Port) | uint(uint160(target));
|
|
210
|
-
node |= uint(uint32(
|
|
210
|
+
node |= uint(uint32(toSelector(name, "(bytes)"))) << 160;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
/// @notice Build a chain-local query ID for the given
|
|
214
|
-
/// @param
|
|
213
|
+
/// @notice Build a chain-local query ID for the given endpoint name and contract.
|
|
214
|
+
/// @param name Query function name.
|
|
215
215
|
/// @param target Query contract address.
|
|
216
216
|
/// @return node Query node ID embedding both the selector and address.
|
|
217
|
-
function toQuery(
|
|
217
|
+
function toQuery(string memory name, address target) internal view returns (uint node) {
|
|
218
218
|
node = toLocalBase(Query) | uint(uint160(target));
|
|
219
|
-
node |= uint(uint32(
|
|
219
|
+
node |= uint(uint32(toSelector(name, "(bytes)"))) << 160;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
/// @notice Build a chain-local guard action ID for the given
|
|
223
|
-
/// @param
|
|
222
|
+
/// @notice Build a chain-local guard action ID for the given endpoint name and contract.
|
|
223
|
+
/// @param name Guard action function name.
|
|
224
224
|
/// @param target Guard action contract address.
|
|
225
225
|
/// @return node Guard action node ID embedding both the selector and address.
|
|
226
|
-
function toGuard(
|
|
226
|
+
function toGuard(string memory name, address target) internal view returns (uint node) {
|
|
227
227
|
node = toLocalBase(Guard) | uint(uint160(target));
|
|
228
|
-
node |= uint(uint32(
|
|
228
|
+
node |= uint(uint32(toSelector(name, "(bytes)"))) << 160;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// @dev Derive an ABI selector from an endpoint name and argument signature.
|
|
232
|
+
function toSelector(string memory name, string memory args) private pure returns (bytes4) {
|
|
233
|
+
return bytes4(keccak256(bytes(string.concat(name, args))));
|
|
229
234
|
}
|
|
230
235
|
|
|
231
236
|
/// @notice Derive an opaque node ID from a keccak preimage.
|
package/events/Recovered.sol
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
-
|
|
6
|
-
/// @notice Emitted when a host recovers a previously recorded key.
|
|
7
|
-
abstract contract RecoveredEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Recovered(uint indexed host, bytes32 key)";
|
|
9
|
-
|
|
10
|
-
/// @param host Host node ID that owns the recovered key.
|
|
11
|
-
/// @param key Recovery lookup key.
|
|
12
|
-
event Recovered(uint indexed host, bytes32 key);
|
|
13
|
-
|
|
14
|
-
constructor() {
|
|
15
|
-
emit EventAbi(ABI);
|
|
16
|
-
}
|
|
17
|
-
}
|
package/events/Undelivered.sol
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
import {EventEmitter} from "./Emitter.sol";
|
|
5
|
-
|
|
6
|
-
/// @notice Emitted when a host records an undelivered portal message.
|
|
7
|
-
abstract contract UndeliveredEvent is EventEmitter {
|
|
8
|
-
string private constant ABI = "event Undelivered(uint indexed host, bytes32 key, bytes32 digest)";
|
|
9
|
-
|
|
10
|
-
/// @param host Host node ID that owns the undelivered message.
|
|
11
|
-
/// @param key Delivery lookup key.
|
|
12
|
-
/// @param digest Digest of the undelivered message.
|
|
13
|
-
event Undelivered(uint indexed host, bytes32 key, bytes32 digest);
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
emit EventAbi(ABI);
|
|
17
|
-
}
|
|
18
|
-
}
|
package/utils/Selectors.sol
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-only
|
|
2
|
-
pragma solidity ^0.8.33;
|
|
3
|
-
|
|
4
|
-
/// @title Selectors
|
|
5
|
-
/// @notice Canonical ABI selector derivation for rootzero endpoint types.
|
|
6
|
-
library Selectors {
|
|
7
|
-
/// @notice Derive the ABI selector for a command entrypoint.
|
|
8
|
-
/// @param name Command function name.
|
|
9
|
-
/// @return Selector for `name(bytes32,bytes,bytes)`.
|
|
10
|
-
function command(string memory name) internal pure returns (bytes4) {
|
|
11
|
-
return derive(name, "(bytes32,bytes,bytes)");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/// @notice Derive the ABI selector for a port entrypoint.
|
|
15
|
-
/// @param name Port function name.
|
|
16
|
-
/// @return Selector for `name(bytes)`.
|
|
17
|
-
function port(string memory name) internal pure returns (bytes4) {
|
|
18
|
-
return direct(name);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/// @notice Derive the ABI selector for a query entrypoint.
|
|
22
|
-
/// @param name Query function name.
|
|
23
|
-
/// @return Selector for `name(bytes)`.
|
|
24
|
-
function query(string memory name) internal pure returns (bytes4) {
|
|
25
|
-
return direct(name);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/// @notice Derive the ABI selector for a guard action entrypoint.
|
|
29
|
-
/// @param name Guard action function name.
|
|
30
|
-
/// @return Selector for `name(bytes)`.
|
|
31
|
-
function guard(string memory name) internal pure returns (bytes4) {
|
|
32
|
-
return direct(name);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/// @dev Derive a direct endpoint selector with a single `bytes` argument.
|
|
36
|
-
/// @param name Endpoint function name.
|
|
37
|
-
/// @return Selector for `name(bytes)`.
|
|
38
|
-
function direct(string memory name) private pure returns (bytes4) {
|
|
39
|
-
return derive(name, "(bytes)");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/// @dev Derive an ABI selector from a function name and argument signature suffix.
|
|
43
|
-
/// @param name Function name.
|
|
44
|
-
/// @param args Parenthesized ABI argument signature.
|
|
45
|
-
/// @return First four bytes of the function signature hash.
|
|
46
|
-
function derive(string memory name, string memory args) private pure returns (bytes4) {
|
|
47
|
-
return bytes4(keccak256(bytes(string.concat(name, args))));
|
|
48
|
-
}
|
|
49
|
-
}
|