📄 AddressRegistry.sol
D-OPEN SOVEREIGN

Documentation & Insights

@title Address Registry
@dev An immutable registry that stores the historical and active deployment addresses for
Ꭰ-Library Core Contracts (Factory, BouncerStorage, BackupStorage, etc.).
Designed to replace build-time static configuration.
@dev Transfers administration of the registry to a new address.
@dev Appends a new deployed address to the history of a given contract name.
The last appended address is considered the "active" or "latest" version.
@dev Appends a new string value to the history of a given data key.
The last appended value is considered the "active" or "latest" version.
@dev Returns the most recently deployed (active) address for a given contract name.
Reverts if the contract name has no history.
@dev Returns the most recently added value for a given data key.
Reverts if the data key has no history.
@dev Returns the entire chronological history of records for a given contract name.
The last record in the array is the current active version.
@dev Returns the entire chronological history of strings for a given data key.
@dev Returns all recognized contract names currently tracked in the registry.
@dev Returns all recognized data keys currently tracked in the registry.
@dev Anchors a corpus pointer blob on-chain by storing its Arweave TxID and SHA-256 hash.
The blob is the per-environment corpus sub-block uploaded to Arweave by seed-registry-v2.js.
Key convention: "CORPUS_V2_PROD" (on chain 1116) / "CORPUS_V2_STAGING" (on chain 1114).
@dev Returns the most recently anchored blob ref for a given key.
@dev Returns a paginated slice of the blob ref history for a given key.
Use offset=0, limit=1 to get the oldest; getLatestBlobRef for the newest.
Historical audit should use indexed BlobRefAdded events instead of this function.
@dev Returns all blob ref keys currently tracked in the registry.
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15
16// SPDX-License-Identifier: Apache-2.0
17pragma solidity ^0.8.24;
18
19/**
20 * @title Address Registry
21 * @dev An immutable registry that stores the historical and active deployment addresses for
22 * Ꭰ-Library Core Contracts (Factory, BouncerStorage, BackupStorage, etc.).
23 * Designed to replace build-time static configuration.
24 */
25contract AddressRegistry {
26    address public admin;
27
28    struct ContractRecord {
29        address contractAddress;
30        string abiUrl;
31    }
32
33    // Mapping from Contract Name (e.g., "Factory") to a historical array of deployed records
34    mapping(string => ContractRecord[]) private _contractHistory;
35
36    // Array to keep track of all registered contract names for easy enumeration
37    string[] private _contractNames;
38    // Mapping to prevent duplicates in _contractNames
39    mapping(string => bool) private _nameExists;
40
41    // Mapping from a data key (e.g., "EN_TOPICS") to a historical array of string values
42    mapping(string => string[]) private _dataHistory;
43
44    // Array to keep track of all registered data keys
45    string[] private _dataKeys;
46    // Mapping to prevent duplicates in _dataKeys
47    mapping(string => bool) private _dataKeyExists;
48
49    // Blob references: key → (Arweave TxID, SHA-256 hash) pairs
50    // Used to anchor corpus pointer blobs (e.g., CORPUS_V2_PROD) on-chain for verifiability.
51    struct BlobRef {
52        string arweaveTxId;
53        bytes32 sha256Hash;
54    }
55
56    mapping(string => BlobRef[]) private _blobRefs;
57    string[] private _blobRefKeys;
58    mapping(string => bool) private _blobRefKeyExists;
59
60    event ContractAdded(
61        string indexed name,
62        address indexed contractAddress,
63        string abiUrl,
64        uint256 version
65    );
66
67    event DataAdded(
68        string indexed key,
69        string value,
70        uint256 version
71    );
72
73    event BlobRefAdded(
74        string indexed key,
75        string arweaveTxId,
76        bytes32 sha256Hash,
77        uint256 version
78    );
79
80    event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
81
82    modifier onlyAdmin() {
83        require(
84            msg.sender == admin,
85            "AddressRegistry: caller is not the admin"
86        );
87        _;
88    }
89
90    constructor() {
91        admin = msg.sender;
92    }
93
94    /**
95     * @dev Transfers administration of the registry to a new address.
96     */
97    function changeAdmin(address newAdmin) external onlyAdmin {
98        require(
99            newAdmin != address(0),
100            "AddressRegistry: new admin is the zero address"
101        );
102        emit AdminChanged(admin, newAdmin);
103        admin = newAdmin;
104    }
105
106    /**
107     * @dev Appends a new deployed address to the history of a given contract name.
108     * The last appended address is considered the "active" or "latest" version.
109     */
110    function addContract(
111        string memory name,
112        address contractAddress,
113        string memory abiUrl
114    ) external onlyAdmin {
115        require(
116            contractAddress != address(0),
117            "AddressRegistry: contract address cannot be zero"
118        );
119        require(
120            bytes(name).length > 0,
121            "AddressRegistry: contract name cannot be empty"
122        );
123
124        if (!_nameExists[name]) {
125            _nameExists[name] = true;
126            _contractNames.push(name);
127        }
128
129        _contractHistory[name].push(
130            ContractRecord({contractAddress: contractAddress, abiUrl: abiUrl})
131        );
132
133        uint256 versionIndex = _contractHistory[name].length; // 1-indexed version
134        emit ContractAdded(name, contractAddress, abiUrl, versionIndex);
135    }
136
137    /**
138     * @dev Appends a new string value to the history of a given data key.
139     * The last appended value is considered the "active" or "latest" version.
140     */
141    function addData(
142        string memory key,
143        string memory value
144    ) external onlyAdmin {
145        require(
146            bytes(key).length > 0,
147            "AddressRegistry: data key cannot be empty"
148        );
149        require(
150            bytes(value).length > 0,
151            "AddressRegistry: data value cannot be empty"
152        );
153
154        if (!_dataKeyExists[key]) {
155            _dataKeyExists[key] = true;
156            _dataKeys.push(key);
157        }
158
159        _dataHistory[key].push(value);
160
161        uint256 versionIndex = _dataHistory[key].length; // 1-indexed version
162        emit DataAdded(key, value, versionIndex);
163    }
164
165    /**
166     * @dev Returns the most recently deployed (active) address for a given contract name.
167     * Reverts if the contract name has no history.
168     */
169    function getLatestContract(
170        string memory name
171    ) external view returns (address, string memory) {
172        require(
173            _contractHistory[name].length > 0,
174            "AddressRegistry: contract not found"
175        );
176        ContractRecord memory record = _contractHistory[name][
177            _contractHistory[name].length - 1
178        ];
179        return (record.contractAddress, record.abiUrl);
180    }
181
182    /**
183     * @dev Returns the most recently added value for a given data key.
184     * Reverts if the data key has no history.
185     */
186    function getLatestData(
187        string memory key
188    ) external view returns (string memory) {
189        require(
190            _dataHistory[key].length > 0,
191            "AddressRegistry: data not found"
192        );
193        return _dataHistory[key][_dataHistory[key].length - 1];
194    }
195
196    /**
197     * @dev Returns the entire chronological history of records for a given contract name.
198     * The last record in the array is the current active version.
199     */
200    function getContractHistory(
201        string memory name
202    ) external view returns (ContractRecord[] memory) {
203        return _contractHistory[name];
204    }
205
206    /**
207     * @dev Returns the entire chronological history of strings for a given data key.
208     */
209    function getDataHistory(
210        string memory key
211    ) external view returns (string[] memory) {
212        return _dataHistory[key];
213    }
214
215    /**
216     * @dev Returns all recognized contract names currently tracked in the registry.
217     */
218    function getAllContractNames() external view returns (string[] memory) {
219        return _contractNames;
220    }
221
222    /**
223     * @dev Returns all recognized data keys currently tracked in the registry.
224     */
225    function getAllDataKeys() external view returns (string[] memory) {
226        return _dataKeys;
227    }
228
229    /**
230     * @dev Anchors a corpus pointer blob on-chain by storing its Arweave TxID and SHA-256 hash.
231     * The blob is the per-environment corpus sub-block uploaded to Arweave by seed-registry-v2.js.
232     * Key convention: "CORPUS_V2_PROD" (on chain 1116) / "CORPUS_V2_STAGING" (on chain 1114).
233     */
234    function addBlobRef(
235        string memory key,
236        string memory arweaveTxId,
237        bytes32 sha256Hash
238    ) external onlyAdmin {
239        require(bytes(key).length > 0, "AddressRegistry: key cannot be empty");
240        require(bytes(arweaveTxId).length > 0, "AddressRegistry: txId cannot be empty");
241        require(sha256Hash != bytes32(0), "AddressRegistry: hash cannot be zero");
242
243        if (!_blobRefKeyExists[key]) {
244            _blobRefKeyExists[key] = true;
245            _blobRefKeys.push(key);
246        }
247
248        _blobRefs[key].push(BlobRef({ arweaveTxId: arweaveTxId, sha256Hash: sha256Hash }));
249        uint256 version = _blobRefs[key].length;
250        emit BlobRefAdded(key, arweaveTxId, sha256Hash, version);
251    }
252
253    /**
254     * @dev Returns the most recently anchored blob ref for a given key.
255     */
256    function getLatestBlobRef(
257        string memory key
258    ) external view returns (string memory, bytes32) {
259        require(_blobRefs[key].length > 0, "AddressRegistry: blob ref not found");
260        BlobRef memory ref = _blobRefs[key][_blobRefs[key].length - 1];
261        return (ref.arweaveTxId, ref.sha256Hash);
262    }
263
264    /**
265     * @dev Returns a paginated slice of the blob ref history for a given key.
266     * Use offset=0, limit=1 to get the oldest; getLatestBlobRef for the newest.
267     * Historical audit should use indexed BlobRefAdded events instead of this function.
268     */
269    function getBlobRefHistory(
270        string memory key,
271        uint256 offset,
272        uint256 limit
273    ) external view returns (BlobRef[] memory) {
274        BlobRef[] storage history = _blobRefs[key];
275        uint256 end = offset + limit > history.length ? history.length : offset + limit;
276        if (offset >= end) return new BlobRef[](0);
277        BlobRef[] memory page = new BlobRef[](end - offset);
278        for (uint256 i = offset; i < end; i++) {
279            page[i - offset] = history[i];
280        }
281        return page;
282    }
283
284    /**
285     * @dev Returns all blob ref keys currently tracked in the registry.
286     */
287    function getAllBlobRefKeys() external view returns (string[] memory) {
288        return _blobRefKeys;
289    }
290}
291