@towns-protocol/contracts 0.0.455 → 1.0.2
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/package.json +4 -4
- package/scripts/deployments/diamonds/DeployL1Resolver.s.sol +155 -0
- package/scripts/deployments/diamonds/DeployL2Registrar.s.sol +171 -0
- package/scripts/deployments/diamonds/DeployL2Resolver.s.sol +196 -0
- package/scripts/deployments/diamonds/DeploySpaceFactory.s.sol +0 -12
- package/scripts/deployments/facets/DeployAddrResolverFacet.s.sol +36 -0
- package/scripts/deployments/facets/DeployArchitect.s.sol +1 -3
- package/scripts/deployments/facets/DeployContentHashResolverFacet.s.sol +34 -0
- package/scripts/deployments/facets/DeployCreateSpace.s.sol +2 -1
- package/scripts/deployments/facets/{DeploySpaceFactoryInit.s.sol → DeployExtendedResolverFacet.s.sol} +10 -10
- package/scripts/deployments/facets/DeployL1ResolverFacet.s.sol +61 -0
- package/scripts/deployments/facets/DeployL2RegistrarFacet.s.sol +56 -0
- package/scripts/deployments/facets/DeployL2RegistryFacet.s.sol +71 -0
- package/scripts/deployments/facets/DeployNodeRegistry.s.sol +4 -1
- package/scripts/deployments/facets/DeployTextResolverFacet.s.sol +34 -0
- package/scripts/deployments/utils/DeployDomainFeeHook.s.sol +78 -0
- package/scripts/interactions/InteractAlphaPost.s.sol +0 -7
- package/scripts/interactions/InteractBaseAlpha.s.sol +1 -14
- package/scripts/interactions/InteractDomainFee.s.sol +62 -0
- package/scripts/interactions/InteractSetStreamDistributionBalancingAdvantage.s.sol +42 -0
- package/scripts/interactions/InteractSetStreamDistributionRequiredOperators.s.sol +32 -0
- package/scripts/interactions/helpers/RiverConfigValues.sol +6 -0
- package/src/domains/facets/l1/IL1ResolverService.sol +20 -0
- package/src/domains/facets/l1/L1ResolverFacet.sol +100 -0
- package/src/domains/facets/l1/L1ResolverMod.sol +245 -0
- package/src/domains/facets/l2/AddrResolverFacet.sol +59 -0
- package/src/domains/facets/l2/ContentHashResolverFacet.sol +41 -0
- package/src/domains/facets/l2/ExtendedResolverFacet.sol +38 -0
- package/src/domains/facets/l2/IL2Registry.sol +79 -0
- package/src/domains/facets/l2/L2RegistryFacet.sol +203 -0
- package/src/domains/facets/l2/TextResolverFacet.sol +43 -0
- package/src/domains/facets/l2/modules/AddrResolverMod.sol +110 -0
- package/src/domains/facets/l2/modules/ContentHashResolverMod.sol +60 -0
- package/src/domains/facets/l2/modules/L2RegistryMod.sol +286 -0
- package/src/domains/facets/l2/modules/TextResolverMod.sol +62 -0
- package/src/domains/facets/l2/modules/VersionRecordMod.sol +42 -0
- package/src/domains/facets/registrar/IL2Registrar.sol +57 -0
- package/src/domains/facets/registrar/L2RegistrarFacet.sol +127 -0
- package/src/domains/facets/registrar/L2RegistrarMod.sol +224 -0
- package/src/domains/hooks/DomainFeeHook.sol +212 -0
- package/src/factory/facets/architect/Architect.sol +48 -29
- package/src/factory/facets/architect/IArchitect.sol +2 -21
- package/src/factory/facets/architect/ImplementationStorage.sol +12 -20
- package/src/factory/facets/create/CreateSpace.sol +5 -0
- package/src/factory/facets/create/CreateSpaceBase.sol +32 -8
- package/src/factory/facets/create/ICreateSpace.sol +5 -0
- package/src/factory/facets/fee/FeeManagerFacet.sol +10 -0
- package/src/factory/facets/fee/FeeTypesLib.sol +3 -0
- package/src/river/registry/facets/node/INodeRegistry.sol +25 -0
- package/src/river/registry/facets/node/NodeRegistry.sol +53 -2
- package/src/river/registry/libraries/RegistryStorage.sol +5 -0
- package/src/spaces/facets/ProtocolFeeLib.sol +56 -1
- package/src/spaces/facets/proxy/SpaceProxyInitializer.sol +6 -8
- package/src/spaces/facets/xchain/SpaceEntitlementGated.sol +9 -0
- package/LICENSE.txt +0 -21
- package/scripts/bytecode-diff/README.md +0 -182
- package/scripts/deployments/utils/DeploySpaceProxyInitializer.s.sol +0 -28
- package/scripts/readme.md +0 -289
- package/src/diamond/readme.md +0 -50
- package/src/factory/SpaceFactoryInit.sol +0 -17
- package/src/factory/facets/architect/ArchitectBase.sol +0 -95
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IExtendedResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IExtendedResolver.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {L1ResolverMod} from "./L1ResolverMod.sol";
|
|
9
|
+
|
|
10
|
+
// contracts
|
|
11
|
+
import {OwnableBase} from "@towns-protocol/diamond/src/facets/ownable/OwnableBase.sol";
|
|
12
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
13
|
+
|
|
14
|
+
contract L1ResolverFacet is IExtendedResolver, OwnableBase, Facet {
|
|
15
|
+
using L1ResolverMod for L1ResolverMod.Layout;
|
|
16
|
+
|
|
17
|
+
/// @notice Initializes the resolver with a gateway URL and signer
|
|
18
|
+
/// @param gatewayUrl The URL of the CCIP gateway
|
|
19
|
+
/// @param gatewaySigner The address of the gateway signer
|
|
20
|
+
function __L1Resolver_init(
|
|
21
|
+
string calldata gatewayUrl,
|
|
22
|
+
address gatewaySigner
|
|
23
|
+
) external onlyInitializing {
|
|
24
|
+
_addInterface(type(IExtendedResolver).interfaceId);
|
|
25
|
+
__L1Resolver_init_unchained(gatewayUrl, gatewaySigner);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function __L1Resolver_init_unchained(
|
|
29
|
+
string calldata gatewayUrl,
|
|
30
|
+
address gatewaySigner
|
|
31
|
+
) internal {
|
|
32
|
+
L1ResolverMod.Layout storage $ = L1ResolverMod.getStorage();
|
|
33
|
+
$.setGatewayURL(gatewayUrl);
|
|
34
|
+
$.setGatewaySigner(gatewaySigner);
|
|
35
|
+
$.setNameWrapper();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// @notice Sets the L2 registry for a given node
|
|
39
|
+
/// @param node The node to set the L2 registry for
|
|
40
|
+
/// @param chainId The chain ID of the L2 registry
|
|
41
|
+
/// @param registryAddress The address of the L2 registry
|
|
42
|
+
function setL2Registry(bytes32 node, uint64 chainId, address registryAddress) external {
|
|
43
|
+
L1ResolverMod.getStorage().setL2Registry(node, chainId, registryAddress);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @notice Sets the gateway URL
|
|
47
|
+
/// @param gatewayUrl The URL of the CCIP gateway
|
|
48
|
+
function setGatewayURL(string calldata gatewayUrl) external onlyOwner {
|
|
49
|
+
L1ResolverMod.getStorage().setGatewayURL(gatewayUrl);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// @notice Sets the gateway signer
|
|
53
|
+
/// @param gatewaySigner The address of the gateway signer
|
|
54
|
+
function setGatewaySigner(address gatewaySigner) external onlyOwner {
|
|
55
|
+
L1ResolverMod.getStorage().setGatewaySigner(gatewaySigner);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @inheritdoc IExtendedResolver
|
|
59
|
+
/// @dev Always reverts with OffchainLookup to trigger CCIP-Read
|
|
60
|
+
function resolve(
|
|
61
|
+
bytes calldata name,
|
|
62
|
+
bytes calldata data
|
|
63
|
+
) external view returns (bytes memory) {
|
|
64
|
+
return L1ResolverMod.getStorage().resolve(name, data, this.resolveWithProof.selector);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// @notice Callback for CCIP-Read to verify and return the resolved data
|
|
68
|
+
/// @param response The response from the gateway (result, expires, sig)
|
|
69
|
+
/// @param extraData The original request data for signature verification
|
|
70
|
+
/// @return The verified resolved data
|
|
71
|
+
function resolveWithProof(
|
|
72
|
+
bytes calldata response,
|
|
73
|
+
bytes calldata extraData
|
|
74
|
+
) external view returns (bytes memory) {
|
|
75
|
+
return L1ResolverMod.getStorage().resolveWithProof(response, extraData);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// @notice Returns the L2 registry for a given node
|
|
79
|
+
/// @param node The node to get the L2 registry for
|
|
80
|
+
/// @return chainId The chain ID of the L2 registry
|
|
81
|
+
/// @return registryAddress The address of the L2 registry
|
|
82
|
+
function getL2Registry(
|
|
83
|
+
bytes32 node
|
|
84
|
+
) external view returns (uint64 chainId, address registryAddress) {
|
|
85
|
+
L1ResolverMod.L2Registry memory registry = L1ResolverMod.getStorage().registryByNode[node];
|
|
86
|
+
return (registry.chainId, registry.registryAddress);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @notice Returns the gateway signer address
|
|
90
|
+
/// @return The address of the gateway signer
|
|
91
|
+
function getGatewaySigner() external view returns (address) {
|
|
92
|
+
return L1ResolverMod.getStorage().gatewaySigner;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// @notice Returns the gateway URL
|
|
96
|
+
/// @return The URL of the CCIP gateway
|
|
97
|
+
function getGatewayURL() external view returns (string memory) {
|
|
98
|
+
return L1ResolverMod.getStorage().gatewayUrl;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {INameWrapper} from "@ensdomains/ens-contracts/wrapper/INameWrapper.sol";
|
|
6
|
+
import {IAddrResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IAddrResolver.sol";
|
|
7
|
+
import {IL1ResolverService} from "./IL1ResolverService.sol";
|
|
8
|
+
|
|
9
|
+
// libraries
|
|
10
|
+
import {CustomRevert} from "../../../utils/libraries/CustomRevert.sol";
|
|
11
|
+
import {NameCoder} from "@ensdomains/ens-contracts/utils/NameCoder.sol";
|
|
12
|
+
import {LibString} from "solady/utils/LibString.sol";
|
|
13
|
+
import {OffchainLookup} from "@ensdomains/ens-contracts/ccipRead/EIP3668.sol";
|
|
14
|
+
import {SignatureCheckerLib} from "solady/utils/SignatureCheckerLib.sol";
|
|
15
|
+
|
|
16
|
+
// contracts
|
|
17
|
+
import {ENS} from "@ensdomains/ens-contracts/registry/ENS.sol";
|
|
18
|
+
|
|
19
|
+
library L1ResolverMod {
|
|
20
|
+
using CustomRevert for bytes4;
|
|
21
|
+
using LibString for string;
|
|
22
|
+
|
|
23
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
24
|
+
/* STORAGE */
|
|
25
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
26
|
+
|
|
27
|
+
// keccak256(abi.encode(uint256(keccak256("towns.domains.resolver.storage")) - 1)) & ~bytes32(uint256(0xff))
|
|
28
|
+
bytes32 internal constant STORAGE_SLOT =
|
|
29
|
+
0xad5af01a9aec1f0fbc422f62e21406a58de498f1c0ee36ca202bc49bd857fd00;
|
|
30
|
+
|
|
31
|
+
// ENS protocol address
|
|
32
|
+
ENS internal constant ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
|
|
33
|
+
|
|
34
|
+
// ENS name wrapper node
|
|
35
|
+
bytes32 internal constant NAME_WRAPPER_NODE =
|
|
36
|
+
0xdee478ba2734e34d81c6adc77a32d75b29007895efa2fe60921f1c315e1ec7d9; // namewrapper.eth
|
|
37
|
+
|
|
38
|
+
/// @notice L2 registry information
|
|
39
|
+
/// @param chainId The chain ID of the L2 registry
|
|
40
|
+
/// @param registryAddress The address of the L2 registry
|
|
41
|
+
struct L2Registry {
|
|
42
|
+
uint64 chainId;
|
|
43
|
+
address registryAddress;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @notice Storage layout for the L1Resolver
|
|
47
|
+
/// @param gatewayUrl The URL of the CCIP gateway
|
|
48
|
+
/// @param gatewaySigner The address of the gateway signer
|
|
49
|
+
/// @param nameWrapper The name wrapper contract
|
|
50
|
+
/// @param registryByNode Mapping of node to L2 registry information
|
|
51
|
+
struct Layout {
|
|
52
|
+
string gatewayUrl;
|
|
53
|
+
address gatewaySigner;
|
|
54
|
+
INameWrapper nameWrapper;
|
|
55
|
+
mapping(bytes32 node => L2Registry l2Registry) registryByNode;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @notice Returns the storage layout for the L1Resolver
|
|
59
|
+
/// @return $ The storage layout
|
|
60
|
+
/// @custom:storage-location erc7201:towns.domains.resolver.storage
|
|
61
|
+
function getStorage() internal pure returns (Layout storage $) {
|
|
62
|
+
assembly {
|
|
63
|
+
$.slot := STORAGE_SLOT
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
68
|
+
/* EVENTS */
|
|
69
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
70
|
+
|
|
71
|
+
event GatewayURLSet(string gatewayUrl);
|
|
72
|
+
event GatewaySignerSet(address gatewaySigner);
|
|
73
|
+
event L2RegistrySet(
|
|
74
|
+
bytes32 indexed node,
|
|
75
|
+
uint64 indexed chainId,
|
|
76
|
+
address indexed registryAddress
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
80
|
+
/* ERRORS */
|
|
81
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
82
|
+
|
|
83
|
+
error L1Resolver__InvalidGatewayURL();
|
|
84
|
+
error L1Resolver__InvalidGatewaySigner();
|
|
85
|
+
error L1Resolver__InvalidL2Registry();
|
|
86
|
+
error L1Resolver__InvalidName();
|
|
87
|
+
error L1Resolver__InvalidNameWrapper();
|
|
88
|
+
error L1Resolver__InvalidNode();
|
|
89
|
+
error L1Resolver__InvalidChainId();
|
|
90
|
+
error L1Resolver__InvalidOwner();
|
|
91
|
+
error L1Resolver__SignatureExpired();
|
|
92
|
+
error L1Resolver__InvalidSignature();
|
|
93
|
+
|
|
94
|
+
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
|
|
95
|
+
/* FUNCTIONS */
|
|
96
|
+
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
|
|
97
|
+
function setGatewayURL(Layout storage $, string calldata gatewayUrl) internal {
|
|
98
|
+
if (bytes(gatewayUrl).length == 0) L1Resolver__InvalidGatewayURL.selector.revertWith();
|
|
99
|
+
$.gatewayUrl = gatewayUrl;
|
|
100
|
+
emit GatewayURLSet(gatewayUrl);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function setGatewaySigner(Layout storage $, address gatewaySigner) internal {
|
|
104
|
+
if (gatewaySigner == address(0)) L1Resolver__InvalidGatewaySigner.selector.revertWith();
|
|
105
|
+
$.gatewaySigner = gatewaySigner;
|
|
106
|
+
emit GatewaySignerSet(gatewaySigner);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function setNameWrapper(Layout storage $) internal {
|
|
110
|
+
address wrapperResolver = ens.resolver(NAME_WRAPPER_NODE);
|
|
111
|
+
if (wrapperResolver == address(0)) L1Resolver__InvalidNameWrapper.selector.revertWith();
|
|
112
|
+
address wrapperAddress = IAddrResolver(wrapperResolver).addr(NAME_WRAPPER_NODE);
|
|
113
|
+
if (wrapperAddress == address(0)) L1Resolver__InvalidNameWrapper.selector.revertWith();
|
|
114
|
+
$.nameWrapper = INameWrapper(wrapperAddress);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setL2Registry(
|
|
118
|
+
Layout storage $,
|
|
119
|
+
bytes32 node,
|
|
120
|
+
uint64 chainId,
|
|
121
|
+
address registryAddress
|
|
122
|
+
) internal {
|
|
123
|
+
if (node == bytes32(0)) L1Resolver__InvalidNode.selector.revertWith();
|
|
124
|
+
if (chainId == 0) L1Resolver__InvalidChainId.selector.revertWith();
|
|
125
|
+
if (registryAddress == address(0)) L1Resolver__InvalidL2Registry.selector.revertWith();
|
|
126
|
+
|
|
127
|
+
address owner = ens.owner(node);
|
|
128
|
+
|
|
129
|
+
if (owner == address($.nameWrapper)) {
|
|
130
|
+
owner = $.nameWrapper.ownerOf(uint256(node));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (owner != msg.sender) L1Resolver__InvalidOwner.selector.revertWith();
|
|
134
|
+
|
|
135
|
+
$.registryByNode[node] = L2Registry(chainId, registryAddress);
|
|
136
|
+
emit L2RegistrySet(node, chainId, registryAddress);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// @notice Resolve a name via CCIP-Read
|
|
140
|
+
/// @dev Always reverts with OffchainLookup to trigger CCIP-Read flow
|
|
141
|
+
/// @param $ The storage layout
|
|
142
|
+
/// @param name The DNS-encoded name to resolve
|
|
143
|
+
/// @param data The resolution data (e.g., addr(bytes32) calldata)
|
|
144
|
+
/// @param callbackSelector The selector for the callback function (resolveWithProof)
|
|
145
|
+
/// @return This function always reverts, return type is for interface compatibility
|
|
146
|
+
function resolve(
|
|
147
|
+
Layout storage $,
|
|
148
|
+
bytes calldata name,
|
|
149
|
+
bytes calldata data,
|
|
150
|
+
bytes4 callbackSelector
|
|
151
|
+
) internal view returns (bytes memory) {
|
|
152
|
+
string memory decodedName = NameCoder.decode(name); // 'sub.name.eth'
|
|
153
|
+
string[] memory parts = LibString.split(decodedName, ".");
|
|
154
|
+
|
|
155
|
+
// Require at least 2 parts (2LD + TLD)
|
|
156
|
+
if (parts.length < 2) L1Resolver__InvalidName.selector.revertWith();
|
|
157
|
+
|
|
158
|
+
// get the 2LD + TLD (final 2 parts), regardless of how many labels the name has
|
|
159
|
+
string memory parentName = string.concat(
|
|
160
|
+
parts[parts.length - 2],
|
|
161
|
+
".",
|
|
162
|
+
parts[parts.length - 1]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
bytes memory parentDnsName = NameCoder.encode(parentName);
|
|
166
|
+
bytes32 parentNode = NameCoder.namehash(parentDnsName, 0);
|
|
167
|
+
|
|
168
|
+
L2Registry memory registry = $.registryByNode[parentNode];
|
|
169
|
+
if (registry.registryAddress == address(0)) {
|
|
170
|
+
L1Resolver__InvalidL2Registry.selector.revertWith();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Build callData for the gateway
|
|
174
|
+
bytes memory callData = abi.encodeCall(
|
|
175
|
+
IL1ResolverService.stuffedResolveCall,
|
|
176
|
+
(name, data, registry.chainId, registry.registryAddress)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Build the gateway URL array
|
|
180
|
+
string[] memory urls = new string[](1);
|
|
181
|
+
urls[0] = $.gatewayUrl;
|
|
182
|
+
|
|
183
|
+
// Revert with OffchainLookup to trigger CCIP-Read
|
|
184
|
+
revert OffchainLookup(
|
|
185
|
+
address(this),
|
|
186
|
+
urls,
|
|
187
|
+
callData,
|
|
188
|
+
callbackSelector,
|
|
189
|
+
callData // extraData same as callData for verification
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// @notice Verifies the gateway response and returns the resolved data
|
|
194
|
+
/// @param $ The storage layout
|
|
195
|
+
/// @param response The gateway response (result, expires, sig)
|
|
196
|
+
/// @param extraData The original request data for signature verification
|
|
197
|
+
/// @return result The verified result data
|
|
198
|
+
function resolveWithProof(
|
|
199
|
+
Layout storage $,
|
|
200
|
+
bytes calldata response,
|
|
201
|
+
bytes calldata extraData
|
|
202
|
+
) internal view returns (bytes memory result) {
|
|
203
|
+
address signer = $.gatewaySigner;
|
|
204
|
+
if (signer == address(0)) L1Resolver__InvalidGatewaySigner.selector.revertWith();
|
|
205
|
+
|
|
206
|
+
// Decode the gateway response
|
|
207
|
+
uint64 expires;
|
|
208
|
+
bytes memory sig;
|
|
209
|
+
(result, expires, sig) = abi.decode(response, (bytes, uint64, bytes));
|
|
210
|
+
|
|
211
|
+
// Check expiration
|
|
212
|
+
if (block.timestamp > expires) {
|
|
213
|
+
L1Resolver__SignatureExpired.selector.revertWith();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Create the signature hash (matching the gateway's signing format)
|
|
217
|
+
bytes32 hash = _makeSignatureHash(
|
|
218
|
+
address(this),
|
|
219
|
+
expires,
|
|
220
|
+
keccak256(extraData),
|
|
221
|
+
keccak256(result)
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Verify signature using SignatureCheckerLib
|
|
225
|
+
// Supports both EOA (ECDSA) and smart contract wallets (ERC-1271)
|
|
226
|
+
if (!SignatureCheckerLib.isValidSignatureNow(signer, hash, sig)) {
|
|
227
|
+
L1Resolver__InvalidSignature.selector.revertWith();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/// @dev Generates a hash for signing/verifying gateway responses
|
|
232
|
+
/// @param target The address the signature is for (this contract)
|
|
233
|
+
/// @param expires The expiration timestamp
|
|
234
|
+
/// @param request The original request data
|
|
235
|
+
/// @param result The result data from the gateway
|
|
236
|
+
/// @return The hash to be signed/verified
|
|
237
|
+
function _makeSignatureHash(
|
|
238
|
+
address target,
|
|
239
|
+
uint64 expires,
|
|
240
|
+
bytes32 request,
|
|
241
|
+
bytes32 result
|
|
242
|
+
) private pure returns (bytes32) {
|
|
243
|
+
return keccak256(abi.encodePacked(hex"1900", target, expires, request, result));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IAddrResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IAddrResolver.sol";
|
|
6
|
+
import {IAddressResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IAddressResolver.sol";
|
|
7
|
+
|
|
8
|
+
// libraries
|
|
9
|
+
import {AddrResolverMod} from "./modules/AddrResolverMod.sol";
|
|
10
|
+
import {L2RegistryMod} from "./modules/L2RegistryMod.sol";
|
|
11
|
+
import {VersionRecordMod} from "./modules/VersionRecordMod.sol";
|
|
12
|
+
|
|
13
|
+
// contracts
|
|
14
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
15
|
+
|
|
16
|
+
/// @title AddrResolverFacet
|
|
17
|
+
/// @notice ENS address resolver facet for storing and retrieving blockchain addresses on L2
|
|
18
|
+
/// @dev Implements IAddrResolver (ETH only) and IAddressResolver (multi-coin) with versioned records
|
|
19
|
+
contract AddrResolverFacet is IAddrResolver, IAddressResolver, Facet {
|
|
20
|
+
using AddrResolverMod for AddrResolverMod.Layout;
|
|
21
|
+
using VersionRecordMod for VersionRecordMod.Layout;
|
|
22
|
+
|
|
23
|
+
/// @notice Initializes the facet by registering resolver interfaces
|
|
24
|
+
function __AddrResolverFacet_init() external onlyInitializing {
|
|
25
|
+
_addInterface(type(IAddrResolver).interfaceId);
|
|
26
|
+
_addInterface(type(IAddressResolver).interfaceId);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// @notice Sets the address for a node and coin type (SLIP-44 standard)
|
|
30
|
+
/// @param node The ENS node to update
|
|
31
|
+
/// @param coinType The SLIP-44 coin type (e.g., 60 for ETH, 0 for BTC)
|
|
32
|
+
/// @param a The address bytes to set
|
|
33
|
+
function setAddr(bytes32 node, uint256 coinType, bytes memory a) external {
|
|
34
|
+
L2RegistryMod.onlyAuthorized(node);
|
|
35
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
36
|
+
AddrResolverMod.getStorage().setAddr(version, node, coinType, a);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// @notice Sets the Ethereum address (coinType 60) for a node
|
|
40
|
+
/// @param node The ENS node to update
|
|
41
|
+
/// @param a The Ethereum address to set
|
|
42
|
+
function setAddr(bytes32 node, address a) external {
|
|
43
|
+
L2RegistryMod.onlyAuthorized(node);
|
|
44
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
45
|
+
AddrResolverMod.getStorage().setAddr(version, node, a);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @inheritdoc IAddressResolver
|
|
49
|
+
function addr(bytes32 node, uint256 coinType) external view returns (bytes memory) {
|
|
50
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
51
|
+
return AddrResolverMod.getStorage().addr(version, node, coinType);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// @inheritdoc IAddrResolver
|
|
55
|
+
function addr(bytes32 node) external view returns (address payable) {
|
|
56
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
57
|
+
return AddrResolverMod.getStorage().addr(version, node);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IContentHashResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IContentHashResolver.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
import {ContentHashResolverMod} from "./modules/ContentHashResolverMod.sol";
|
|
9
|
+
import {L2RegistryMod} from "./modules/L2RegistryMod.sol";
|
|
10
|
+
import {VersionRecordMod} from "./modules/VersionRecordMod.sol";
|
|
11
|
+
|
|
12
|
+
// contracts
|
|
13
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
14
|
+
|
|
15
|
+
/// @title ContentHashResolverFacet
|
|
16
|
+
/// @notice ENS content hash resolver facet for storing IPFS/IPNS/Swarm hashes on L2
|
|
17
|
+
/// @dev Implements IContentHashResolver with versioned records
|
|
18
|
+
contract ContentHashResolverFacet is IContentHashResolver, Facet {
|
|
19
|
+
using ContentHashResolverMod for ContentHashResolverMod.Layout;
|
|
20
|
+
using VersionRecordMod for VersionRecordMod.Layout;
|
|
21
|
+
|
|
22
|
+
/// @notice Initializes the facet by registering resolver interface
|
|
23
|
+
function __ContentHashResolverFacet_init() external onlyInitializing {
|
|
24
|
+
_addInterface(type(IContentHashResolver).interfaceId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// @notice Sets the content hash for a node
|
|
28
|
+
/// @param node The ENS node to update
|
|
29
|
+
/// @param hash The content hash bytes (IPFS, IPNS, Swarm, etc.)
|
|
30
|
+
function setContenthash(bytes32 node, bytes calldata hash) external {
|
|
31
|
+
L2RegistryMod.onlyAuthorized(node);
|
|
32
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
33
|
+
ContentHashResolverMod.getStorage().setContenthash(version, node, hash);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// @inheritdoc IContentHashResolver
|
|
37
|
+
function contenthash(bytes32 node) external view returns (bytes memory) {
|
|
38
|
+
uint64 version = VersionRecordMod.getStorage().recordVersions[node];
|
|
39
|
+
return ContentHashResolverMod.getStorage().contenthash(version, node);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IExtendedResolver} from "@ensdomains/ens-contracts/resolvers/profiles/IExtendedResolver.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
|
|
9
|
+
// contracts
|
|
10
|
+
import {Facet} from "@towns-protocol/diamond/src/facets/Facet.sol";
|
|
11
|
+
|
|
12
|
+
/// @title ExtendedResolverFacet
|
|
13
|
+
/// @notice Implements EIP-3668 (CCIP Read) resolve function for on-chain resolution without offchain lookup
|
|
14
|
+
/// @dev Executes the encoded resolver call directly via staticcall and returns the result or propagates revert
|
|
15
|
+
contract ExtendedResolverFacet is IExtendedResolver, Facet {
|
|
16
|
+
/// @notice Initializes the facet by registering the IExtendedResolver interface
|
|
17
|
+
function __ExtendedResolverFacet_init() external onlyInitializing {
|
|
18
|
+
_addInterface(type(IExtendedResolver).interfaceId);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @notice Resolves ENS data by executing the encoded call directly on this contract
|
|
22
|
+
/// @param data The encoded resolver function call (e.g., addr(bytes32), text(bytes32,string))
|
|
23
|
+
/// @return The result of the resolver call
|
|
24
|
+
function resolve(
|
|
25
|
+
bytes memory /* name */,
|
|
26
|
+
bytes memory data
|
|
27
|
+
) external view returns (bytes memory) {
|
|
28
|
+
(bool success, bytes memory result) = address(this).staticcall(data);
|
|
29
|
+
if (success) {
|
|
30
|
+
return result;
|
|
31
|
+
} else {
|
|
32
|
+
// Revert with the reason provided by the call
|
|
33
|
+
assembly {
|
|
34
|
+
revert(add(result, 0x20), mload(result))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.29;
|
|
3
|
+
|
|
4
|
+
// interfaces
|
|
5
|
+
import {IERC721A} from "../../../diamond/facets/token/ERC721A/IERC721A.sol";
|
|
6
|
+
|
|
7
|
+
// libraries
|
|
8
|
+
|
|
9
|
+
// contracts
|
|
10
|
+
|
|
11
|
+
/// @title IL2Registry
|
|
12
|
+
/// @notice Interface for L2 ENS-compatible domain registry that mints subdomains as NFTs
|
|
13
|
+
/// @dev Extends IERC721A to provide ERC721 functionality and adds domain-specific operations
|
|
14
|
+
interface IL2Registry is IERC721A {
|
|
15
|
+
/// @notice Creates a subdomain under an existing domain and optionally sets resolver records
|
|
16
|
+
/// @param domainHash The parent domain's namehash
|
|
17
|
+
/// @param subdomain The subdomain label (e.g., "alice" for "alice.towns.eth")
|
|
18
|
+
/// @param owner The address that will own the subdomain NFT
|
|
19
|
+
/// @param records Encoded resolver calls to set initial records (addr, text, etc.)
|
|
20
|
+
/// @param metadata Arbitrary bytes for registrar use (e.g., expiration, tier, etc.)
|
|
21
|
+
function createSubdomain(
|
|
22
|
+
bytes32 domainHash,
|
|
23
|
+
string calldata subdomain,
|
|
24
|
+
address owner,
|
|
25
|
+
bytes[] calldata records,
|
|
26
|
+
bytes calldata metadata
|
|
27
|
+
) external;
|
|
28
|
+
|
|
29
|
+
/// @notice Adds an address as an approved registrar that can mint subdomains
|
|
30
|
+
/// @param registrar The address to approve as a registrar
|
|
31
|
+
function addRegistrar(address registrar) external;
|
|
32
|
+
|
|
33
|
+
/// @notice Removes an address from the approved registrars list
|
|
34
|
+
/// @param registrar The address to remove
|
|
35
|
+
function removeRegistrar(address registrar) external;
|
|
36
|
+
|
|
37
|
+
/// @notice Sets or updates the metadata for a subdomain (registrar only)
|
|
38
|
+
/// @dev Metadata is arbitrary bytes that the registrar can interpret (e.g., expiration, tier, etc.)
|
|
39
|
+
/// @param node The namehash of the subdomain
|
|
40
|
+
/// @param data The metadata bytes to store
|
|
41
|
+
function setMetadata(bytes32 node, bytes calldata data) external;
|
|
42
|
+
|
|
43
|
+
/// @notice Returns the metadata bytes for a subdomain
|
|
44
|
+
/// @param node The namehash of the subdomain
|
|
45
|
+
/// @return The metadata bytes (empty if not set)
|
|
46
|
+
function getMetadata(bytes32 node) external view returns (bytes memory);
|
|
47
|
+
|
|
48
|
+
/// @notice Returns the owner of the root domain
|
|
49
|
+
/// @return The address that owns the root domain NFT
|
|
50
|
+
function domainOwner() external view returns (address);
|
|
51
|
+
|
|
52
|
+
/// @notice Returns the owner of a subdomain by its namehash
|
|
53
|
+
/// @param nameHash The namehash of the subdomain
|
|
54
|
+
/// @return The address that owns the subdomain NFT
|
|
55
|
+
function subdomainOwner(bytes32 nameHash) external view returns (address);
|
|
56
|
+
|
|
57
|
+
/// @notice Returns the namehash of the base domain
|
|
58
|
+
/// @return The namehash of the base domain
|
|
59
|
+
function baseDomainHash() external view returns (bytes32);
|
|
60
|
+
|
|
61
|
+
/// @notice Computes the namehash of a domain name
|
|
62
|
+
/// @param node The domain name (e.g., "alice.towns.eth")
|
|
63
|
+
/// @return The namehash (bytes32)
|
|
64
|
+
function namehash(string calldata node) external pure returns (bytes32);
|
|
65
|
+
|
|
66
|
+
/// @notice Decodes a DNS-encoded name to a human-readable string
|
|
67
|
+
/// @param node The DNS-encoded name bytes
|
|
68
|
+
/// @return The decoded domain name
|
|
69
|
+
function decodeName(bytes calldata node) external pure returns (string memory);
|
|
70
|
+
|
|
71
|
+
/// @notice Helper to derive a node from a parent node and label
|
|
72
|
+
/// @param domainHash The namehash of the domain, e.g. `namehash("name.eth")` for "name.eth"
|
|
73
|
+
/// @param subdomain The label of the subnode, e.g. "x" for "x.name.eth"
|
|
74
|
+
/// @return The resulting subnode, e.g. `namehash("x.name.eth")` for "x.name.eth"
|
|
75
|
+
function encodeSubdomain(
|
|
76
|
+
bytes32 domainHash,
|
|
77
|
+
string calldata subdomain
|
|
78
|
+
) external pure returns (bytes32);
|
|
79
|
+
}
|