ccip-router 0.5.4 → 0.5.5

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.
@@ -0,0 +1,171 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ /**
5
+ * OffchainResolver v2 — EIP-3668 (CCIP Read) resolver for ENS.
6
+ *
7
+ * v2 changes from v1:
8
+ * - Multi-signer: replaces single `signerAddress` with
9
+ * `mapping(address => bool) authorizedSigners`
10
+ * - `addSigner` / `removeSigner` admin functions
11
+ * - `setSigner` kept for compatibility (adds to mapping, doesn't remove others)
12
+ *
13
+ * Flow:
14
+ * 1. ENS client calls resolve(name, data) on this contract.
15
+ * 2. Contract reverts with OffchainLookup, pointing to gateway URLs.
16
+ * 3. Client queries gateway, gets signed response.
17
+ * 4. Client calls resolveWithProof(response, extraData) to verify.
18
+ * Any authorized signer's signature is accepted.
19
+ */
20
+ contract OffchainResolver {
21
+ // EIP-3668 error — triggers CCIP Read in compliant clients
22
+ error OffchainLookup(
23
+ address sender,
24
+ string[] urls,
25
+ bytes callData,
26
+ bytes4 callbackFunction,
27
+ bytes extraData
28
+ );
29
+
30
+ address public owner;
31
+ string[] public gatewayURLs;
32
+
33
+ // v2: multiple authorized signers (any gateway node may sign)
34
+ mapping(address => bool) public authorizedSigners;
35
+
36
+ // On-chain contenthash per node — read directly by browsers (Brave, etc.)
37
+ // that don't support CCIP Read. Set via setContenthash(), costs gas once.
38
+ mapping(bytes32 => bytes) public contenthashes;
39
+
40
+ event GatewayURLsUpdated(string[] urls);
41
+ event SignerAdded(address indexed signer);
42
+ event SignerRemoved(address indexed signer);
43
+ event ContenthashUpdated(bytes32 indexed node, bytes contenthash);
44
+
45
+ modifier onlyOwner() {
46
+ require(msg.sender == owner, "not owner");
47
+ _;
48
+ }
49
+
50
+ constructor(string[] memory _urls, address _initialSigner) {
51
+ owner = msg.sender;
52
+ gatewayURLs = _urls;
53
+ authorizedSigners[_initialSigner] = true;
54
+ emit SignerAdded(_initialSigner);
55
+ }
56
+
57
+ // ─── ENS Resolver Interface ──────────────────────────────────────────────
58
+
59
+ function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
60
+ return
61
+ interfaceId == 0x9061b923 || // IExtendedResolver (resolve/ENSIP-10)
62
+ interfaceId == 0x3b3b57de || // IAddrResolver (addr)
63
+ interfaceId == 0x59d1d43c || // ITextResolver (text)
64
+ interfaceId == 0xbc1c58d1 || // IContentHashResolver (contenthash)
65
+ interfaceId == 0x01ffc9a7; // IERC165
66
+ }
67
+
68
+ /**
69
+ * Returns the on-chain contenthash for a node.
70
+ */
71
+ function contenthash(bytes32 node) external view returns (bytes memory) {
72
+ return contenthashes[node];
73
+ }
74
+
75
+ function setContenthash(bytes32 node, bytes calldata _contenthash) external onlyOwner {
76
+ contenthashes[node] = _contenthash;
77
+ emit ContenthashUpdated(node, _contenthash);
78
+ }
79
+
80
+ /**
81
+ * Wildcard resolution entry point (ENSIP-10).
82
+ */
83
+ function resolve(bytes calldata name, bytes calldata data)
84
+ external
85
+ view
86
+ returns (bytes memory)
87
+ {
88
+ string[] memory urls = new string[](gatewayURLs.length);
89
+ for (uint i = 0; i < gatewayURLs.length; i++) {
90
+ urls[i] = gatewayURLs[i];
91
+ }
92
+
93
+ revert OffchainLookup(
94
+ address(this),
95
+ urls,
96
+ abi.encode(name, data),
97
+ OffchainResolver.resolveWithProof.selector,
98
+ abi.encode(name, data)
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Callback — verifies the signed gateway response.
104
+ * Accepts any authorized signer's signature.
105
+ */
106
+ function resolveWithProof(bytes calldata response, bytes calldata extraData)
107
+ external
108
+ view
109
+ returns (bytes memory)
110
+ {
111
+ (bytes memory result, uint64 expires, bytes memory sig) =
112
+ abi.decode(response, (bytes, uint64, bytes));
113
+
114
+ require(block.timestamp <= expires, "response expired");
115
+
116
+ bytes32 hash = keccak256(
117
+ abi.encodePacked(
118
+ hex"1900",
119
+ address(this),
120
+ expires,
121
+ keccak256(extraData),
122
+ keccak256(result)
123
+ )
124
+ );
125
+
126
+ bytes32 r;
127
+ bytes32 s;
128
+ uint8 v;
129
+ assembly {
130
+ r := mload(add(sig, 32))
131
+ s := mload(add(sig, 64))
132
+ v := byte(0, mload(add(sig, 96)))
133
+ }
134
+ if (v < 27) v += 27;
135
+
136
+ address recovered = ecrecover(
137
+ keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)),
138
+ v, r, s
139
+ );
140
+
141
+ require(authorizedSigners[recovered], "invalid signature");
142
+ return result;
143
+ }
144
+
145
+ // ─── Admin ───────────────────────────────────────────────────────────────
146
+
147
+ function setGatewayURLs(string[] calldata _urls) external onlyOwner {
148
+ gatewayURLs = _urls;
149
+ emit GatewayURLsUpdated(_urls);
150
+ }
151
+
152
+ function addSigner(address signer) external onlyOwner {
153
+ authorizedSigners[signer] = true;
154
+ emit SignerAdded(signer);
155
+ }
156
+
157
+ function removeSigner(address signer) external onlyOwner {
158
+ authorizedSigners[signer] = false;
159
+ emit SignerRemoved(signer);
160
+ }
161
+
162
+ // kept for v1 compatibility — adds to mapping without removing others
163
+ function setSigner(address signer) external onlyOwner {
164
+ authorizedSigners[signer] = true;
165
+ emit SignerAdded(signer);
166
+ }
167
+
168
+ function transferOwnership(address newOwner) external onlyOwner {
169
+ owner = newOwner;
170
+ }
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccip-router",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "The coordination layer CCIP-Read was missing. Peer sync, deduplication, and cryptographic attestation for any CCIP-Read gateway.",
5
5
  "type": "module",
6
6
  "main": "./dist/lib.js",
@@ -27,7 +27,8 @@
27
27
  "contracts/IAttestationVerifier.sol",
28
28
  "contracts/AttestationIndex.sol",
29
29
  "contracts/NodeRegistry.sol",
30
- "contracts/WyriweAttestationVerifier.sol"
30
+ "contracts/WyriweAttestationVerifier.sol",
31
+ "contracts/OffchainResolver.sol"
31
32
  ],
32
33
  "keywords": [
33
34
  "ccip-read",