📄 ProjectManager.sol
D-OPEN SOVEREIGN

Documentation & Insights

This is the friendly wrapper API that is public inside the generated ABI.

addressDonationsPerProject(address addr) public view returns (uint256[] memory)
setProjectStorage(address storageAddr)
getProjectAllocatedBudget(uint16 projectId) public view returns (uint256[] memory) {
getProject(uint16 projectId) public view returns
nbProjects() public view returns (uint16)
getProjectVersion(uint16 projectId, bytes4 lang, uint16 versionNumber) public view returns (IProjectManagerStorage.Version memory) {
addProjectVersion() public onlyOwner returns (uint16)
fundAddress(address addr, uint256 amount) internal {
balance() public view returns (uint256, uint256, uint256) {
getBalanceOfAddress(address addr) public view returns (uint256 donated, uint256 allocated, uint256 remaining) {
getProjectSupportedLanguages(uint16 projectId) public view returns (bytes4[] memory) {
setProjectManagerStorage(address storageAddr) external onlyOwner
createProject() public onlyOwner returns (uint16)
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
19import "./IProjectManagerStorage.sol";
20import {Ownable} from "./Ownable.sol";
21import {ErrorLibrary} from "./ErrorLibrary.sol";
22
23/**
24 * This is the friendly wrapper API that is public inside the generated ABI.
25 *
26 * addressDonationsPerProject(address addr) public view returns (uint256[] memory)
27 * setProjectStorage(address storageAddr)
28 * getProjectAllocatedBudget(uint16 projectId) public view returns (uint256[] memory) {
29 * getProject(uint16 projectId) public view returns
30 * nbProjects() public view returns (uint16)
31 * getProjectVersion(uint16 projectId, bytes4 lang, uint16 versionNumber) public view returns (IProjectManagerStorage.Version memory) {
32 * addProjectVersion() public onlyOwner returns (uint16)
33 * fundAddress(address addr, uint256 amount) internal {
34 * balance() public view returns (uint256, uint256, uint256) {
35 * getBalanceOfAddress(address addr) public view returns (uint256 donated, uint256 allocated, uint256 remaining) {
36 * getProjectSupportedLanguages(uint16 projectId) public view returns (bytes4[] memory) {
37 * setProjectManagerStorage(address storageAddr) external onlyOwner
38 * createProject() public onlyOwner returns (uint16)
39 */
40abstract contract ProjectManager is Ownable {
41    IProjectManagerStorage public projectStorage;
42    string public constant DOMAIN_AUTHORITY = "https://datapond.earth";
43
44    constructor() {}
45
46    function setProjectStorage(address storageAddr) external onlyOwner {
47        if (storageAddr == address(0)) revert ErrorLibrary.EmptyAddress();
48        projectStorage = IProjectManagerStorage(storageAddr);
49    }
50
51    function nbProjects() public view returns (uint16) {
52        return projectStorage.nbProjects();
53    }
54
55    function voteForProject(
56        uint16 projectId,
57        bytes4 lang,
58        uint256 amount
59    ) public {
60        uint16 totalProjects = projectStorage.nbProjects();
61        if (projectId == 0 || projectId > totalProjects)
62            revert ErrorLibrary.InvalidProjectId();
63        if (amount == 0) revert ErrorLibrary.NotEnoughOnBalance();
64
65        uint256[3] memory balances = projectStorage.getUserBalances(msg.sender);
66        if (amount > balances[2]) revert ErrorLibrary.NotEnoughOnBalance();
67        if (projectStorage.getLanguageIndex(projectId, lang) == 0)
68            revert ErrorLibrary.InvalidLangVote();
69
70        balances[2] -= amount; // balance
71        balances[1] += amount; // debit total
72        projectStorage.updateUserBalances(msg.sender, balances);
73
74        projectStorage.updateUserProjectDonation(
75            projectId,
76            msg.sender,
77            amount,
78            true
79        );
80        projectStorage.updateUserTotalDonationForProject(
81            msg.sender,
82            projectId,
83            amount,
84            true
85        );
86        projectStorage.updateVotingBalance(projectId, lang, amount, true);
87    }
88
89    function addressDonationsPerProject(
90        address addr
91    ) public view returns (uint256[] memory) {
92        uint16 totalProjects = projectStorage.nbProjects();
93        if (totalProjects == 0) return new uint256[](0);
94        uint256[] memory donationsPerProject = new uint256[](totalProjects);
95        for (uint16 i = 1; i <= totalProjects; i++) {
96            donationsPerProject[i - 1] = projectStorage
97                .getUserTotalDonationForProject(addr, i);
98        }
99        return donationsPerProject;
100    }
101
102    function createProject(
103        bytes4 lang,
104        string memory uri,
105        uint32 publicationDate,
106        string memory name,
107        string memory headline,
108        string memory arweaveAddressLogo,
109        string memory arweaveAddressBanner,
110        string memory descriptionMarkdown,
111        uint256 minimumFundingBeforeStartWork,
112        uint256 maximumFunding,
113        uint8[] memory tags,
114        uint8 complexity
115    ) public onlyOwner returns (uint16) {
116        uint16 projectId = projectStorage.incrementNbProjects();
117        projectStorage.setProject(projectId);
118        projectStorage.setNbLanguages(projectId, 1);
119        projectStorage.setLanguageMapping(projectId, lang, 1);
120        projectStorage.setNbVersions(projectId, lang, 1);
121
122        projectStorage.setVersion(
123            projectId,
124            lang,
125            1,
126            IProjectManagerStorage.Version({
127                publicationDate: publicationDate,
128                uri: uri,
129                name: name,
130                headline: headline,
131                arweaveAddressLogo: arweaveAddressLogo,
132                arweaveAddressBanner: arweaveAddressBanner,
133                descriptionMarkdown: descriptionMarkdown,
134                minimumFundingBeforeStartWork: minimumFundingBeforeStartWork,
135                maximumFunding: maximumFunding,
136                tags: tags,
137                status: 9,
138                complexity: complexity
139            })
140        );
141
142        return projectId;
143    }
144
145    function getProjectSupportedLanguages(
146        uint16 projectId
147    ) public view returns (bytes4[] memory) {
148        uint16 count = projectStorage.getNbLanguages(projectId);
149        bytes4[] memory langs = new bytes4[](count);
150        for (uint16 i = 0; i < count; i++) {
151            langs[i] = projectStorage.getLanguageAtIndex(projectId, i + 1);
152        }
153        return langs;
154    }
155
156    function getProjectAllocatedBudget(
157        uint16 projectId
158    ) public view returns (uint256[] memory) {
159        uint16 count = projectStorage.getNbLanguages(projectId);
160        uint256[] memory balances = new uint256[](count);
161        for (uint16 i = 1; i <= count; i++) {
162            bytes4 lang = projectStorage.getLanguageAtIndex(projectId, i);
163            balances[i - 1] = projectStorage.getVotingBalance(projectId, lang);
164        }
165        return balances;
166    }
167
168    function getProject(
169        uint16 projectId
170    )
171        public
172        view
173        returns (
174            uint16 id,
175            uint256[] memory projectBalance,
176            IProjectManagerStorage.Version[] memory projectVersions,
177            bytes4[] memory availableLanguages
178        )
179    {
180        uint16 totalProjects = projectStorage.nbProjects();
181        if (projectId == 0 || projectId > totalProjects)
182            revert ErrorLibrary.InvalidProjectId();
183
184        uint16 langCount = projectStorage.getNbLanguages(projectId);
185        IProjectManagerStorage.Version[]
186            memory results = new IProjectManagerStorage.Version[](langCount);
187        uint256[] memory balances = new uint256[](langCount);
188        bytes4[] memory langs = new bytes4[](langCount);
189
190        for (uint16 i = 0; i < langCount; i++) {
191            bytes4 lang = projectStorage.getLanguageAtIndex(projectId, i + 1);
192            langs[i] = lang;
193            uint16 latestVersion = projectStorage.getNbVersions(
194                projectId,
195                lang
196            );
197            IProjectManagerStorage.Version memory v = projectStorage.getVersion(
198                projectId,
199                lang,
200                latestVersion
201            );
202            v.uri = string.concat(DOMAIN_AUTHORITY, v.uri);
203            results[i] = v;
204            balances[i] = projectStorage.getVotingBalance(projectId, lang);
205        }
206
207        return (projectId, balances, results, langs);
208    }
209
210    function addProjectVersion(
211        uint16 projectId,
212        bytes4 lang,
213        uint32 publicationDate,
214        string memory uri,
215        string memory name,
216        string memory headline,
217        string memory arweaveAddressLogo,
218        string memory arweaveAddressBanner,
219        string memory descriptionMarkdown,
220        uint256 minimumFundingBeforeStartWork,
221        uint256 maximumFunding,
222        uint8[] memory tags,
223        uint8 status,
224        uint8 complexity
225    ) public onlyOwner returns (uint16) {
226        uint16 totalProjects = projectStorage.nbProjects();
227        if (projectId == 0 || projectId > totalProjects)
228            revert ErrorLibrary.InvalidProjectId();
229
230        uint16 currentNbVersions;
231        if (projectStorage.getLanguageIndex(projectId, lang) == 0) {
232            uint16 nbLangs = projectStorage.getNbLanguages(projectId) + 1;
233            projectStorage.setNbLanguages(projectId, nbLangs);
234            projectStorage.setLanguageMapping(projectId, lang, nbLangs);
235            currentNbVersions = 1;
236        } else {
237            currentNbVersions =
238                projectStorage.getNbVersions(projectId, lang) +
239                1;
240        }
241
242        projectStorage.setNbVersions(projectId, lang, currentNbVersions);
243        projectStorage.setVersion(
244            projectId,
245            lang,
246            currentNbVersions,
247            IProjectManagerStorage.Version({
248                publicationDate: publicationDate,
249                uri: string.concat(DOMAIN_AUTHORITY, uri),
250                name: name,
251                headline: headline,
252                arweaveAddressLogo: arweaveAddressLogo,
253                arweaveAddressBanner: arweaveAddressBanner,
254                descriptionMarkdown: descriptionMarkdown,
255                minimumFundingBeforeStartWork: minimumFundingBeforeStartWork,
256                maximumFunding: maximumFunding,
257                tags: tags,
258                status: status,
259                complexity: complexity
260            })
261        );
262
263        return currentNbVersions;
264    }
265
266    function deleteProject(uint16 projectId, bytes4 lang) public onlyOwner returns (uint16) {
267        uint16 totalProjects = projectStorage.nbProjects();
268        if (projectId == 0 || projectId > totalProjects)
269            revert ErrorLibrary.InvalidProjectId();
270
271        uint256 balance = projectStorage.getVotingBalance(projectId, lang);
272        require(balance < 1000, "Voting balance must be < 1000");
273
274        uint16 currentNbVersions = projectStorage.getNbVersions(projectId, lang);
275        if (currentNbVersions == 0) revert ErrorLibrary.InvalidProjectId();
276
277        IProjectManagerStorage.Version memory v = projectStorage.getVersion(projectId, lang, currentNbVersions);
278        
279        v.status = 0; // STATUS_DELETED
280
281        projectStorage.setNbVersions(projectId, lang, currentNbVersions + 1);
282        projectStorage.setVersion(projectId, lang, currentNbVersions + 1, v);
283
284        return currentNbVersions + 1;
285    }
286
287    function getProjectVersion(
288        uint16 projectId,
289        bytes4 lang,
290        uint16 versionNumber
291    ) public view returns (IProjectManagerStorage.Version memory) {
292        uint16 totalProjects = projectStorage.nbProjects();
293        if (projectId == 0 || projectId > totalProjects)
294            revert ErrorLibrary.InvalidProjectId();
295        IProjectManagerStorage.Version memory v = projectStorage.getVersion(
296            projectId,
297            lang,
298            versionNumber
299        );
300        v.uri = string.concat(DOMAIN_AUTHORITY, v.uri);
301        return v;
302    }
303
304    // Only called internally by the Accountant, because Accountant is ProjectManager()
305    function fundAddress(address addr, uint256 amount) internal {
306        uint256[3] memory b = projectStorage.getUserBalances(addr);
307        b[0] += amount; // credit
308        b[2] += amount; // balance
309        projectStorage.updateUserBalances(addr, b);
310    }
311
312    function fundAddressForTesting(
313        address addr,
314        uint256 amount
315    ) external onlyOwner {
316        require(block.chainid == 31337, "only on local testnet");
317        fundAddress(addr, amount);
318    }
319
320    function balance() public view returns (uint256, uint256, uint256) {
321        uint256[3] memory b = projectStorage.getUserBalances(msg.sender);
322        return (b[0], b[1], b[2]);
323    }
324
325    function getBalanceOfAddress(
326        address addr
327    )
328        public
329        view
330        returns (uint256 donated, uint256 allocated, uint256 remaining)
331    {
332        uint256[3] memory b = projectStorage.getUserBalances(addr);
333        return (b[0], b[1], b[2]);
334    }
335
336    function recordStripeDonation(
337        string memory stripePaymentId,
338        uint256 amountUSD,
339        string memory username,
340        address donor,
341        string memory country,
342        string memory language
343    ) internal {
344        projectStorage.recordStripeDonation(
345            block.timestamp / 604800,
346            IProjectManagerStorage.StripeDonation({
347                stripePaymentId: stripePaymentId,
348                amountUSD: amountUSD,
349                username: username,
350                donor: donor,
351                country: country,
352                language: language,
353                timestamp: block.timestamp,
354                chainId: block.chainid
355            })
356        );
357    }
358}
359