@resurrect-protocol/contracts 0.4.0-dev.30.1 → 0.4.0-dev.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,43 @@
1
1
  # `@resurrect-protocol/contracts`
2
2
 
3
- Canonical, permissionless Resurrect Registry v1 Solidity source and ABI. The contract is stateless and has no owner, administrator, allowlist, pause, upgrade, withdrawal, or peer-storage path. Anyone may publish a bounded signed peer record. Applications consume recent `PeerAnnounced` logs as untrusted discovery hints.
3
+ Canonical Resurrect Solidity sources, ABIs, and Ethereum mainnet deployment records.
4
4
 
5
5
  ```bash
6
6
  npm install @resurrect-protocol/contracts
7
7
  ```
8
8
 
9
+ ## Registry
10
+
9
11
  ```js
10
12
  import registryAbi from '@resurrect-protocol/contracts/abi/ResurrectRegistryV1.json' with { type: 'json' }
11
13
  import ethereumMainnet from '@resurrect-protocol/contracts/deployments/ethereum-mainnet.json' with { type: 'json' }
12
14
  ```
13
15
 
14
- Solidity tools can import `@resurrect-protocol/contracts/src/ResurrectRegistryV1.sol`. The source exposes exactly `VERSION`, `MAX_TTL`, `MAX_RECORD_BYTES`, and `announce`. TTL must be between one second and 90 days; records must be between one and 4096 bytes. Expiry is computed from the block timestamp and emitted with the namespace, codec, and bytes.
16
+ Solidity tools can import `@resurrect-protocol/contracts/src/ResurrectRegistryV1.sol`. The permissionless registry exposes exactly `VERSION`, `MAX_TTL`, `MAX_RECORD_BYTES`, and `announce`. It has no owner, administrator, allowlist, pause, upgrade, withdrawal, or peer-storage path.
17
+
18
+ The reference deployment is `0x6F33c332e8251dcd307D85A27fCcAbd85d578910` on Ethereum mainnet, chain ID `1`, receipt block `25882327`. Its deployment JSON includes the transaction, deployer, compiler settings, runtime hash, source revision, and Etherscan and Sourcify links.
19
+
20
+ Applications may use the reference deployment or deploy the exact source elsewhere, but must pin the chosen chain, address, deployment block, namespace, and constants. A shared stateless registry does not create a shared peer list. RPC providers remain caller-selected, and an event remains an untrusted discovery hint until the signed record, sequence, expiry, endpoints, and application handshake are verified.
21
+
22
+ ## Immutable explorer
23
+
24
+ ```js
25
+ import siteAbi from '@resurrect-protocol/contracts/abi/ResurrectOnchainSite.json' with { type: 'json' }
26
+ import explorerMainnet from '@resurrect-protocol/contracts/deployments/ethereum-mainnet-explorer.json' with { type: 'json' }
27
+ ```
28
+
29
+ Solidity tools can import `@resurrect-protocol/contracts/src/ResurrectOnchainSite.sol`. The contract implements ERC-5219 and the ERC-6944 `5219` resolve mode, reconstructing immutable resources from bytecode-storage contracts. It has no owner, mutable storage, or upgrade path.
30
+
31
+ The production router is `0xb69aF08877a0C417169135D6710Bca4840CCCdE1` on Ethereum mainnet at block `25936611`. Its deployment JSON records the 25 data contracts, all 26 transaction hashes and blocks, every resource hash and payload length, the router runtime and manifest hashes, compiler settings, deployment cost, gateway URLs, and verification results.
32
+
33
+ Open it through:
34
+
35
+ - `web3://0xb69aF08877a0C417169135D6710Bca4840CCCdE1:1/`;
36
+ - `https://0xb69af08877a0c417169135d6710bca4840cccde1.w3eth.io/`; or
37
+ - `https://0xb69af08877a0c417169135d6710bca4840cccde1.1.w3link.io/`.
15
38
 
16
- The reference deployment is `0x6F33c332e8251dcd307D85A27fCcAbd85d578910` on Ethereum mainnet (chain ID `1`, receipt block `25882327`). Its exact transaction, deployer, compiler settings, runtime-bytecode hash, tagged source revision, and Etherscan and Sourcify verification URLs are published in the deployment JSON above. Consumers should independently verify those facts before relying on them.
39
+ Consumers should independently compare deployed code and resources with the pinned source and build artifact before relying on them.
17
40
 
18
- The source is released under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). Applications may use the reference deployment or deploy the exact source elsewhere, but must always pin the chosen chain, address, deployment block, namespace, and constants in their descriptor. A shared stateless registry does not create a shared peer list: namespaces remain application-specific, RPC providers remain caller-selected, and the deployer has no contract authority.
41
+ ## License
19
42
 
20
- An onchain event is an untrusted discovery hint. Consumers must still verify the embedded signed peer record, sequence, expiry, endpoint policy, and application handshake.
43
+ `ResurrectRegistryV1.sol` is CC0-1.0. `ResurrectOnchainSite.sol` is available under MIT or Apache-2.0 at your option. Deployment metadata and dependency licenses remain their own.
@@ -0,0 +1,234 @@
1
+ [
2
+ {
3
+ "type": "constructor",
4
+ "inputs": [
5
+ {
6
+ "name": "pathHashes",
7
+ "type": "bytes32[]",
8
+ "internalType": "bytes32[]"
9
+ },
10
+ {
11
+ "name": "contentTypes",
12
+ "type": "string[]",
13
+ "internalType": "string[]"
14
+ },
15
+ {
16
+ "name": "chunks",
17
+ "type": "address[][]",
18
+ "internalType": "address[][]"
19
+ },
20
+ {
21
+ "name": "lengths",
22
+ "type": "uint256[]",
23
+ "internalType": "uint256[]"
24
+ },
25
+ {
26
+ "name": "expectedManifestHash",
27
+ "type": "bytes32",
28
+ "internalType": "bytes32"
29
+ }
30
+ ],
31
+ "stateMutability": "nonpayable"
32
+ },
33
+ {
34
+ "type": "function",
35
+ "name": "INDEX_PATH_HASH",
36
+ "inputs": [],
37
+ "outputs": [
38
+ {
39
+ "name": "",
40
+ "type": "bytes32",
41
+ "internalType": "bytes32"
42
+ }
43
+ ],
44
+ "stateMutability": "view"
45
+ },
46
+ {
47
+ "type": "function",
48
+ "name": "manifestHash",
49
+ "inputs": [],
50
+ "outputs": [
51
+ {
52
+ "name": "",
53
+ "type": "bytes32",
54
+ "internalType": "bytes32"
55
+ }
56
+ ],
57
+ "stateMutability": "view"
58
+ },
59
+ {
60
+ "type": "function",
61
+ "name": "request",
62
+ "inputs": [
63
+ {
64
+ "name": "resource",
65
+ "type": "string[]",
66
+ "internalType": "string[]"
67
+ },
68
+ {
69
+ "name": "",
70
+ "type": "tuple[]",
71
+ "internalType": "struct KeyValue[]",
72
+ "components": [
73
+ {
74
+ "name": "key",
75
+ "type": "string",
76
+ "internalType": "string"
77
+ },
78
+ {
79
+ "name": "value",
80
+ "type": "string",
81
+ "internalType": "string"
82
+ }
83
+ ]
84
+ }
85
+ ],
86
+ "outputs": [
87
+ {
88
+ "name": "statusCode",
89
+ "type": "uint16",
90
+ "internalType": "uint16"
91
+ },
92
+ {
93
+ "name": "body",
94
+ "type": "string",
95
+ "internalType": "string"
96
+ },
97
+ {
98
+ "name": "headers",
99
+ "type": "tuple[]",
100
+ "internalType": "struct KeyValue[]",
101
+ "components": [
102
+ {
103
+ "name": "key",
104
+ "type": "string",
105
+ "internalType": "string"
106
+ },
107
+ {
108
+ "name": "value",
109
+ "type": "string",
110
+ "internalType": "string"
111
+ }
112
+ ]
113
+ }
114
+ ],
115
+ "stateMutability": "view"
116
+ },
117
+ {
118
+ "type": "function",
119
+ "name": "resolveMode",
120
+ "inputs": [],
121
+ "outputs": [
122
+ {
123
+ "name": "mode",
124
+ "type": "bytes32",
125
+ "internalType": "bytes32"
126
+ }
127
+ ],
128
+ "stateMutability": "pure"
129
+ },
130
+ {
131
+ "type": "function",
132
+ "name": "resourceInfo",
133
+ "inputs": [
134
+ {
135
+ "name": "path",
136
+ "type": "string",
137
+ "internalType": "string"
138
+ }
139
+ ],
140
+ "outputs": [
141
+ {
142
+ "name": "exists",
143
+ "type": "bool",
144
+ "internalType": "bool"
145
+ },
146
+ {
147
+ "name": "contentType",
148
+ "type": "string",
149
+ "internalType": "string"
150
+ },
151
+ {
152
+ "name": "length",
153
+ "type": "uint256",
154
+ "internalType": "uint256"
155
+ },
156
+ {
157
+ "name": "chunks",
158
+ "type": "address[]",
159
+ "internalType": "address[]"
160
+ }
161
+ ],
162
+ "stateMutability": "view"
163
+ },
164
+ {
165
+ "type": "error",
166
+ "name": "ArrayLengthMismatch",
167
+ "inputs": []
168
+ },
169
+ {
170
+ "type": "error",
171
+ "name": "DuplicatePath",
172
+ "inputs": [
173
+ {
174
+ "name": "pathHash",
175
+ "type": "bytes32",
176
+ "internalType": "bytes32"
177
+ }
178
+ ]
179
+ },
180
+ {
181
+ "type": "error",
182
+ "name": "EmptyResource",
183
+ "inputs": [
184
+ {
185
+ "name": "pathHash",
186
+ "type": "bytes32",
187
+ "internalType": "bytes32"
188
+ }
189
+ ]
190
+ },
191
+ {
192
+ "type": "error",
193
+ "name": "InvalidChunk",
194
+ "inputs": [
195
+ {
196
+ "name": "pathHash",
197
+ "type": "bytes32",
198
+ "internalType": "bytes32"
199
+ },
200
+ {
201
+ "name": "chunk",
202
+ "type": "address",
203
+ "internalType": "address"
204
+ }
205
+ ]
206
+ },
207
+ {
208
+ "type": "error",
209
+ "name": "MissingIndex",
210
+ "inputs": []
211
+ },
212
+ {
213
+ "type": "error",
214
+ "name": "ResourceLengthMismatch",
215
+ "inputs": [
216
+ {
217
+ "name": "pathHash",
218
+ "type": "bytes32",
219
+ "internalType": "bytes32"
220
+ },
221
+ {
222
+ "name": "expected",
223
+ "type": "uint256",
224
+ "internalType": "uint256"
225
+ },
226
+ {
227
+ "name": "actual",
228
+ "type": "uint256",
229
+ "internalType": "uint256"
230
+ }
231
+ ]
232
+ }
233
+ ]
234
+
@@ -0,0 +1,288 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "deploymentKind": "erc-5219-static-site",
4
+ "application": "Resurrect Explorer",
5
+ "network": "ethereum-mainnet",
6
+ "chainId": 1,
7
+ "address": "0xb69aF08877a0C417169135D6710Bca4840CCCdE1",
8
+ "deploymentBlock": 25936611,
9
+ "firstDeploymentBlock": 25936585,
10
+ "deploymentTimestamp": "2026-09-09T01:44:35Z",
11
+ "transactionHash": "0x79f6fe3d1871d574d2e6e3201a08e7c1a36878648401af6f9ba597729ef88b78",
12
+ "deployer": "0x318027A00a3A3eB6A7d6F45C832e47c126B4F2C2",
13
+ "immutable": true,
14
+ "resolveMode": "5219",
15
+ "manifestHash": "0xd23db1c25fef981cc403b978336a1795c0f891ac5f1f942c4ef4b33c15c33c02",
16
+ "manifestEncoding": "keccak256(abi.encode(string[6] paths, string[6] contentTypes, bytes32[] resourceHashes))",
17
+ "runtimeBytecodeHash": "0x4e8268e3d86eea86e5b2a4b376d240273a25bee5b1d76cade546375ad6a181bf",
18
+ "source": {
19
+ "repository": "https://github.com/cazala/resurrect",
20
+ "commit": "5f167abf8a4d5cac38b8f56dd22f9ab236cd4c62",
21
+ "contractPath": "contracts/src/ResurrectOnchainSite.sol",
22
+ "deploymentScriptPath": "contracts/script/DeployResurrectExplorer.s.sol",
23
+ "explorerArtifactCommit": "dff3cc44087a4c3b1931aef9d5beb9f6c3bed73c"
24
+ },
25
+ "compiler": {
26
+ "language": "Solidity",
27
+ "version": "0.8.24",
28
+ "optimizer": true,
29
+ "optimizerRuns": 20000,
30
+ "bytecodeHash": "none",
31
+ "cborMetadata": false
32
+ },
33
+ "storage": {
34
+ "scheme": "immutable-contract-runtime-bytecode",
35
+ "sentinelBytes": 1,
36
+ "maxPayloadBytesPerChunk": 24000,
37
+ "resourceBytes": 494226,
38
+ "chunkContracts": 25,
39
+ "transactions": 26
40
+ },
41
+ "deploymentCost": {
42
+ "gasUsed": 110247127,
43
+ "weiPaid": "6929689171978036",
44
+ "ethPaid": "0.006929689171978036"
45
+ },
46
+ "gateways": {
47
+ "web3": "web3://0xb69aF08877a0C417169135D6710Bca4840CCCdE1:1/",
48
+ "w3eth": "https://0xb69af08877a0c417169135d6710bca4840cccde1.w3eth.io/",
49
+ "w3link": "https://0xb69af08877a0c417169135d6710bca4840cccde1.1.w3link.io/"
50
+ },
51
+ "ens": {
52
+ "name": "resurrect.cazala.eth",
53
+ "status": "pending-address-record"
54
+ },
55
+ "verification": {
56
+ "ethereumRpc": {
57
+ "status": "pass",
58
+ "resolveMode": true,
59
+ "manifestHash": true,
60
+ "allResourceBodiesByteForByte": true,
61
+ "resourceMetadata": true,
62
+ "notFoundResponse": true
63
+ },
64
+ "w3eth": {
65
+ "status": "pass",
66
+ "allAssetsByteForByte": true,
67
+ "htmlCompatibilityShimInjectedByGateway": true
68
+ },
69
+ "w3link": {
70
+ "status": "pass",
71
+ "allAssetsByteForByte": true,
72
+ "htmlCompatibilityShimInjectedByGateway": true
73
+ },
74
+ "browser": {
75
+ "status": "pass",
76
+ "gateway": "w3eth",
77
+ "registryScan": true,
78
+ "signedPeerRecord": true,
79
+ "noisePeerIdentity": true,
80
+ "identify": true,
81
+ "ping": true,
82
+ "peerId": "12D3KooWRFAprLu4b2RQzq9PWJ2sTYSYuCYA9yDJNEF5kFPYh7B6",
83
+ "endpoint": "/dns4/resurrect-ws.caza.la/tcp/443/wss",
84
+ "observedPingMilliseconds": 306,
85
+ "observedConnectMilliseconds": 1950
86
+ }
87
+ },
88
+ "resources": [
89
+ {
90
+ "path": "index.html",
91
+ "contentType": "text/html; charset=utf-8",
92
+ "bytes": 661,
93
+ "keccak256": "0xb11742d9a343d4d8ca148ef1592a8cdbb470752fd60e596b8b3a329c7cd3e213",
94
+ "chunks": [
95
+ {
96
+ "address": "0xa3018a41347f3220366aa467a2b99eed7bdea1c8",
97
+ "transactionHash": "0xd8f3df539a89875c3e5132c79cb6ed04a07bdebecf7c748bd38d87766f175b9f",
98
+ "block": 25936585,
99
+ "payloadBytes": 661
100
+ }
101
+ ]
102
+ },
103
+ {
104
+ "path": "index.css",
105
+ "contentType": "text/css; charset=utf-8",
106
+ "bytes": 5521,
107
+ "keccak256": "0x46890c55fc010d0cbc6fe7851cb1ff53c14b1abaa1ddd3a81c97c1d6dc019719",
108
+ "chunks": [
109
+ {
110
+ "address": "0xaba2c11fc2a3d30bd9cdf76253844c57c139506e",
111
+ "transactionHash": "0x5e0a834cadbd05ccb3e2047dc2db98857a95e977f4650560c1f857d1d4cf7646",
112
+ "block": 25936586,
113
+ "payloadBytes": 5521
114
+ }
115
+ ]
116
+ },
117
+ {
118
+ "path": "app.js",
119
+ "contentType": "text/javascript; charset=utf-8",
120
+ "bytes": 63525,
121
+ "keccak256": "0x04d1f263ea798d7df89dd06b74b8da6d0a10a67a96f953b94da410e9309dccf5",
122
+ "chunks": [
123
+ {
124
+ "address": "0x70566f16b7ac1c0cd94eafb057169d6caf5ded2b",
125
+ "transactionHash": "0x08601adc0b4a6877d6350159ac8cfde6bd43a58903d1f438be6e667d93139cfc",
126
+ "block": 25936587,
127
+ "payloadBytes": 24000
128
+ },
129
+ {
130
+ "address": "0xc6495227365ec56cac967b280e42e062883b630c",
131
+ "transactionHash": "0x59cc4c60ba42133b715aabe49908d3dc3e2ffe9d7b230500583209992c10b1d5",
132
+ "block": 25936588,
133
+ "payloadBytes": 24000
134
+ },
135
+ {
136
+ "address": "0xa00157a681ea3b87cc0a7be67b87f7c90ea62db4",
137
+ "transactionHash": "0x0b786e15e5b00f60d8b86479bdf89cfab8f9ce53f52d6ace67b9b9dac22793b3",
138
+ "block": 25936589,
139
+ "payloadBytes": 15525
140
+ }
141
+ ]
142
+ },
143
+ {
144
+ "path": "rolldown-runtime.js",
145
+ "contentType": "text/javascript; charset=utf-8",
146
+ "bytes": 716,
147
+ "keccak256": "0x7de63044a95dcac21b8884ab40985883e687488e6e2abfd5770266e2c537cfb2",
148
+ "chunks": [
149
+ {
150
+ "address": "0x8e340d94abbc1f38745b3ffbf0568a2c011208cf",
151
+ "transactionHash": "0x1f4f411e9cfcc2448d8cdd65951deec56806989fc9ab64b9576611a458ef783e",
152
+ "block": 25936590,
153
+ "payloadBytes": 716
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "path": "peer-record.js",
159
+ "contentType": "text/javascript; charset=utf-8",
160
+ "bytes": 132713,
161
+ "keccak256": "0xe69fcbeb747bf15edf524ec758d6340157a4460ac14ff0a7fcf77b84e1eb2529",
162
+ "chunks": [
163
+ {
164
+ "address": "0x059635cc5c16083d2b6c2d6379e40a3780e589d6",
165
+ "transactionHash": "0xdfc885c38d5d3f0746470ea48eecf8c4b3ea172bacfbf10a006f7ec2da2bfd06",
166
+ "block": 25936591,
167
+ "payloadBytes": 24000
168
+ },
169
+ {
170
+ "address": "0xceb09dba1872a07e5d82cdf4df252df4420ad10a",
171
+ "transactionHash": "0xc4467840db732cd6765fe59251efd73857d71d6b2ad5861048a481c04df58893",
172
+ "block": 25936592,
173
+ "payloadBytes": 24000
174
+ },
175
+ {
176
+ "address": "0x2cfd0ff617b0d3ceedab78ef783122e773c8edc1",
177
+ "transactionHash": "0x94f800283966c037e3c1a359c5b11bddfc17e94464cd61c4f3c9c0702aa2c149",
178
+ "block": 25936593,
179
+ "payloadBytes": 24000
180
+ },
181
+ {
182
+ "address": "0x0ffe481a6d04a0cbf6fb5891f17979e1e2a8ae51",
183
+ "transactionHash": "0xd5cb645736b9b9c3a1f257e7d376fcb25f1f6e80aad792715734d9f63c37a456",
184
+ "block": 25936594,
185
+ "payloadBytes": 24000
186
+ },
187
+ {
188
+ "address": "0x9e932760c948d09d4c02a386887b37377cb85460",
189
+ "transactionHash": "0x61e56065fcc91dee9aaae4d53acf65574a1b6e74df65130172439980393c09df",
190
+ "block": 25936595,
191
+ "payloadBytes": 24000
192
+ },
193
+ {
194
+ "address": "0x38ae771c6153ac41e3829aa795d0bbf75bb60adc",
195
+ "transactionHash": "0xce90bc457a2fd9b3197971839d15e6b3e83077bac5c6942829ea805976d6345b",
196
+ "block": 25936596,
197
+ "payloadBytes": 12713
198
+ }
199
+ ]
200
+ },
201
+ {
202
+ "path": "peer-probe.js",
203
+ "contentType": "text/javascript; charset=utf-8",
204
+ "bytes": 291090,
205
+ "keccak256": "0xd52f2b97fec863c83fdffce1ad05e338e95a762e81cab8bacd99437c421ada7a",
206
+ "chunks": [
207
+ {
208
+ "address": "0xdb490a00fd1de273e82a12fbbaa9eac328d7c8ec",
209
+ "transactionHash": "0xd122f2ec17379e282da8d2c4d2b9430a1b01a96bb8975dad2030fe9f3fb6e9af",
210
+ "block": 25936597,
211
+ "payloadBytes": 24000
212
+ },
213
+ {
214
+ "address": "0x9862e3fca8654922b96f772c7ea1f0a8fa5853af",
215
+ "transactionHash": "0x2d14008803c816cffdca5f03f802de4ce6ac533439f466c2acd5a5e8692a1524",
216
+ "block": 25936598,
217
+ "payloadBytes": 24000
218
+ },
219
+ {
220
+ "address": "0x816095efc8d7ea80dcb5fd48d3639ab1a8a73ea3",
221
+ "transactionHash": "0xd8401f8ef0346415ae4352f30786fd7e0f3354dd7dda4c57e56f3ab769125674",
222
+ "block": 25936599,
223
+ "payloadBytes": 24000
224
+ },
225
+ {
226
+ "address": "0x39caa55da0d24de9a4c3675ae1c696d258d47854",
227
+ "transactionHash": "0x2ddae2a0883b385b4b96ee582a0870c886ba46f1f9670cb5e1c10c6ca7a49909",
228
+ "block": 25936600,
229
+ "payloadBytes": 24000
230
+ },
231
+ {
232
+ "address": "0x5bf2786b16aab97a25bf36024e2a302caf78f45f",
233
+ "transactionHash": "0x6e91abff36aa778213303e529192a9365dbefd2f92af8c743fa68a4fa12a9619",
234
+ "block": 25936601,
235
+ "payloadBytes": 24000
236
+ },
237
+ {
238
+ "address": "0xdfde55779da28f3657d88143113d77b444cbd32d",
239
+ "transactionHash": "0x2f481c10c4d08a9e2d46435b61250287367f58d881822a3f927a81b09523a2c8",
240
+ "block": 25936602,
241
+ "payloadBytes": 24000
242
+ },
243
+ {
244
+ "address": "0x7cda636334d36a7f42fda1bf701f29eab9101a69",
245
+ "transactionHash": "0x70048693d2a81263bf0ac349bb05ae17b28dbda77784c76449488b50d68345b5",
246
+ "block": 25936603,
247
+ "payloadBytes": 24000
248
+ },
249
+ {
250
+ "address": "0x0f7180c0955c541483b97028885ad100afdfcdda",
251
+ "transactionHash": "0x77b455f18d98e866db03e3fe2e75eae26f0dda2c8b1788fe42740a15898ff27b",
252
+ "block": 25936604,
253
+ "payloadBytes": 24000
254
+ },
255
+ {
256
+ "address": "0xad1107664c170688cb5226141f64272191d6e056",
257
+ "transactionHash": "0x2ccf0e15fb0e594fd532ab4f7671b4bbbdcdd264f2c8de06f595810b5415c4f3",
258
+ "block": 25936605,
259
+ "payloadBytes": 24000
260
+ },
261
+ {
262
+ "address": "0x11b1df01f23000894ba3d9ce8c9866da5a516755",
263
+ "transactionHash": "0xd012cc7f620909c9b193c935e4bf6b859cf4379c1a9b999be9672f4eb7146cf3",
264
+ "block": 25936606,
265
+ "payloadBytes": 24000
266
+ },
267
+ {
268
+ "address": "0xe2e0cda7353a7806ef644ec04c778613a955fa95",
269
+ "transactionHash": "0xf428eda0c11a578b7c626aa28921780ec996b700ad0d7e65ad1ba094090bc98b",
270
+ "block": 25936607,
271
+ "payloadBytes": 24000
272
+ },
273
+ {
274
+ "address": "0x42dbff87f19d84f237e1aba0242b88548b8afa8b",
275
+ "transactionHash": "0xd1d503fea960cfdf528518c14ae3683320e243c9fe0262daebf56d2482123f2f",
276
+ "block": 25936608,
277
+ "payloadBytes": 24000
278
+ },
279
+ {
280
+ "address": "0xcea7a193e998620e2406fe9e94526b7fba17d1ba",
281
+ "transactionHash": "0x461cc7fa1573cdb5653e6beb45b85ee2804fd58001efdbf7f90c39bfdd39f2ea",
282
+ "block": 25936609,
283
+ "payloadBytes": 3090
284
+ }
285
+ ]
286
+ }
287
+ ]
288
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@resurrect-protocol/contracts",
3
- "version": "0.4.0-dev.30.1",
4
- "description": "Canonical immutable Solidity registry source and ABI for Resurrect v1",
5
- "license": "CC0-1.0",
3
+ "version": "0.4.0-dev.31.1",
4
+ "description": "Canonical immutable Solidity contracts, ABIs, and Ethereum deployments for Resurrect",
5
+ "license": "CC0-1.0 AND (MIT OR Apache-2.0)",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/cazala/resurrect.git",
@@ -17,13 +17,16 @@
17
17
  ],
18
18
  "exports": {
19
19
  "./abi/ResurrectRegistryV1.json": "./abi/ResurrectRegistryV1.json",
20
+ "./abi/ResurrectOnchainSite.json": "./abi/ResurrectOnchainSite.json",
21
+ "./deployments/ethereum-mainnet-explorer.json": "./deployments/ethereum-mainnet-explorer.json",
20
22
  "./deployments/ethereum-mainnet.json": "./deployments/ethereum-mainnet.json",
23
+ "./src/ResurrectOnchainSite.sol": "./src/ResurrectOnchainSite.sol",
21
24
  "./src/ResurrectRegistryV1.sol": "./src/ResurrectRegistryV1.sol"
22
25
  },
23
26
  "scripts": {
24
- "build": "node -e \"const fs=require('node:fs'); JSON.parse(fs.readFileSync('abi/ResurrectRegistryV1.json')); JSON.parse(fs.readFileSync('deployments/ethereum-mainnet.json'))\"",
25
- "check": "node -e \"const fs=require('node:fs'); JSON.parse(fs.readFileSync('abi/ResurrectRegistryV1.json')); JSON.parse(fs.readFileSync('deployments/ethereum-mainnet.json'))\"",
26
- "test": "node -e \"const fs=require('node:fs'); JSON.parse(fs.readFileSync('abi/ResurrectRegistryV1.json')); JSON.parse(fs.readFileSync('deployments/ethereum-mainnet.json'))\""
27
+ "build": "node -e \"const fs=require('node:fs'); for (const file of ['abi/ResurrectRegistryV1.json','abi/ResurrectOnchainSite.json','deployments/ethereum-mainnet.json','deployments/ethereum-mainnet-explorer.json']) JSON.parse(fs.readFileSync(file))\"",
28
+ "check": "node -e \"const fs=require('node:fs'); for (const file of ['abi/ResurrectRegistryV1.json','abi/ResurrectOnchainSite.json','deployments/ethereum-mainnet.json','deployments/ethereum-mainnet-explorer.json']) JSON.parse(fs.readFileSync(file))\"",
29
+ "test": "node -e \"const fs=require('node:fs'); for (const file of ['abi/ResurrectRegistryV1.json','abi/ResurrectOnchainSite.json','deployments/ethereum-mainnet.json','deployments/ethereum-mainnet-explorer.json']) JSON.parse(fs.readFileSync(file))\""
27
30
  },
28
31
  "publishConfig": {
29
32
  "access": "public",
@@ -0,0 +1,182 @@
1
+ // SPDX-License-Identifier: MIT OR Apache-2.0
2
+ pragma solidity 0.8.24;
3
+
4
+ struct KeyValue {
5
+ string key;
6
+ string value;
7
+ }
8
+
9
+ interface IDecentralizedApp {
10
+ function request(string[] memory resource, KeyValue[] memory params)
11
+ external
12
+ view
13
+ returns (uint16 statusCode, string memory body, KeyValue[] memory headers);
14
+ }
15
+
16
+ /// @notice Writes immutable bytes into contract runtime code and reads them with EXTCODECOPY.
17
+ /// @dev Runtime byte zero is a STOP sentinel; the remaining bytes are the payload.
18
+ library ResurrectBytecodeStorage {
19
+ uint256 internal constant MAX_CHUNK_BYTES = 24_000;
20
+
21
+ error EmptyChunk();
22
+ error ChunkTooLarge(uint256 length);
23
+ error ChunkDeploymentFailed();
24
+
25
+ function write(bytes memory data) internal returns (address pointer) {
26
+ if (data.length == 0) revert EmptyChunk();
27
+ if (data.length > MAX_CHUNK_BYTES) revert ChunkTooLarge(data.length);
28
+
29
+ uint16 runtimeLength = uint16(data.length + 1);
30
+ bytes memory initCode =
31
+ abi.encodePacked(hex"61", bytes2(runtimeLength), hex"80600a5f395ff300", data);
32
+ assembly ("memory-safe") {
33
+ pointer := create(0, add(initCode, 0x20), mload(initCode))
34
+ }
35
+ if (pointer == address(0)) revert ChunkDeploymentFailed();
36
+ }
37
+
38
+ function payloadSize(address pointer) internal view returns (uint256 length) {
39
+ assembly ("memory-safe") {
40
+ length := extcodesize(pointer)
41
+ }
42
+ if (length == 0) return 0;
43
+ unchecked {
44
+ --length;
45
+ }
46
+ }
47
+
48
+ function copy(address pointer, bytes memory output, uint256 outputOffset) internal view {
49
+ uint256 length = payloadSize(pointer);
50
+ assembly ("memory-safe") {
51
+ extcodecopy(pointer, add(add(output, 0x20), outputOffset), 1, length)
52
+ }
53
+ }
54
+ }
55
+
56
+ /// @notice Immutable ERC-5219 resource router for one Resurrect explorer build.
57
+ /// @dev No resource can be added, changed, or removed after construction.
58
+ contract ResurrectOnchainSite is IDecentralizedApp {
59
+ using ResurrectBytecodeStorage for address;
60
+
61
+ bytes32 public constant INDEX_PATH_HASH = keccak256("index.html");
62
+ bytes32 public immutable manifestHash;
63
+
64
+ struct Resource {
65
+ string contentType;
66
+ address[] chunks;
67
+ uint256 length;
68
+ bool exists;
69
+ }
70
+
71
+ mapping(bytes32 pathHash => Resource resource) private resources;
72
+
73
+ error ArrayLengthMismatch();
74
+ error DuplicatePath(bytes32 pathHash);
75
+ error EmptyResource(bytes32 pathHash);
76
+ error InvalidChunk(bytes32 pathHash, address chunk);
77
+ error ResourceLengthMismatch(bytes32 pathHash, uint256 expected, uint256 actual);
78
+ error MissingIndex();
79
+
80
+ constructor(
81
+ bytes32[] memory pathHashes,
82
+ string[] memory contentTypes,
83
+ address[][] memory chunks,
84
+ uint256[] memory lengths,
85
+ bytes32 expectedManifestHash
86
+ ) {
87
+ uint256 count = pathHashes.length;
88
+ if (
89
+ count == 0 || count != contentTypes.length || count != chunks.length
90
+ || count != lengths.length
91
+ ) revert ArrayLengthMismatch();
92
+
93
+ manifestHash = expectedManifestHash;
94
+ for (uint256 i; i < count; ++i) {
95
+ bytes32 pathHash = pathHashes[i];
96
+ Resource storage target = resources[pathHash];
97
+ if (target.exists) revert DuplicatePath(pathHash);
98
+ if (lengths[i] == 0 || chunks[i].length == 0) revert EmptyResource(pathHash);
99
+
100
+ target.contentType = contentTypes[i];
101
+ target.length = lengths[i];
102
+ target.exists = true;
103
+ uint256 actualLength;
104
+ for (uint256 j; j < chunks[i].length; ++j) {
105
+ address chunk = chunks[i][j];
106
+ uint256 chunkLength = chunk.payloadSize();
107
+ if (chunkLength == 0 || chunkLength > ResurrectBytecodeStorage.MAX_CHUNK_BYTES) {
108
+ revert InvalidChunk(pathHash, chunk);
109
+ }
110
+ actualLength += chunkLength;
111
+ target.chunks.push(chunk);
112
+ }
113
+ if (actualLength != lengths[i]) {
114
+ revert ResourceLengthMismatch(pathHash, lengths[i], actualLength);
115
+ }
116
+ }
117
+ if (!resources[INDEX_PATH_HASH].exists) revert MissingIndex();
118
+ }
119
+
120
+ function resolveMode() external pure returns (bytes32 mode) {
121
+ return "5219";
122
+ }
123
+
124
+ function request(string[] memory resource, KeyValue[] memory)
125
+ external
126
+ view
127
+ override
128
+ returns (uint16 statusCode, string memory body, KeyValue[] memory headers)
129
+ {
130
+ bytes32 pathHash;
131
+ if (resource.length == 0) {
132
+ pathHash = INDEX_PATH_HASH;
133
+ } else if (resource.length == 1) {
134
+ pathHash = keccak256(bytes(resource[0]));
135
+ } else {
136
+ return _notFound();
137
+ }
138
+
139
+ Resource storage selected = resources[pathHash];
140
+ if (!selected.exists) return _notFound();
141
+
142
+ bytes memory data = new bytes(selected.length);
143
+ uint256 offset;
144
+ for (uint256 i; i < selected.chunks.length; ++i) {
145
+ address chunk = selected.chunks[i];
146
+ chunk.copy(data, offset);
147
+ offset += chunk.payloadSize();
148
+ }
149
+
150
+ headers = _headers(selected.contentType, true);
151
+ return (200, string(data), headers);
152
+ }
153
+
154
+ function resourceInfo(string calldata path)
155
+ external
156
+ view
157
+ returns (bool exists, string memory contentType, uint256 length, address[] memory chunks)
158
+ {
159
+ Resource storage selected = resources[keccak256(bytes(path))];
160
+ return (selected.exists, selected.contentType, selected.length, selected.chunks);
161
+ }
162
+
163
+ function _notFound()
164
+ private
165
+ pure
166
+ returns (uint16 statusCode, string memory body, KeyValue[] memory headers)
167
+ {
168
+ return (404, "not found\n", _headers("text/plain; charset=utf-8", false));
169
+ }
170
+
171
+ function _headers(string memory contentType, bool immutableResource)
172
+ private
173
+ pure
174
+ returns (KeyValue[] memory headers)
175
+ {
176
+ headers = new KeyValue[](2);
177
+ headers[0] = KeyValue("Content-Type", contentType);
178
+ headers[1] = KeyValue(
179
+ "Cache-Control", immutableResource ? "public, max-age=31536000, immutable" : "no-store"
180
+ );
181
+ }
182
+ }