๐Ÿ“„ src/web3/client/types.ts
D-OPEN SOVEREIGN
1import type { TransactionRequest, WalletClient } from 'viem';
2
3export interface ContractAddresses {
4  factory: string;
5  bouncerStorage: string;
6  scientistStorage: string;
7  backupStorage: string;
8  projectManagerStorage: string;
9  subscriptionManager?: string;
10  libraryRegistryStorage?: string;
11  publicLibraryStorage?: string;
12}
13
14export interface StripeDonation {
15  stripePaymentId: string;
16  amountUSD: bigint;
17  username: string;
18  donor: string;
19  country: string;
20  language: string;
21  timestamp: number;
22  chainId: number | string;
23}
24
25export interface BouncerStorageReadAPI {
26  hasAccount(address: string): Promise<boolean>;
27  userInfos(address: string): Promise<[username: string, country: string, language: string, userId: number]>;
28  userData(userId: number): Promise<[id: number, addr: string, username: string, language: string, country: string]>;
29  getUsernameByAddress(address: string): Promise<string>;
30  userIdOf(address: string): Promise<number>;
31  userIdToUsername(userId: number): Promise<string>;
32  usernameToUserId(username: string): Promise<number>;
33  userAddr(userId: number): Promise<string>;
34  getAccountCountByCountryLanguage(country: string, language: string): Promise<number>;
35  getAllLocationCounts(): Promise<any[]>;
36  getActiveLocationCount(): Promise<number>;
37  nbAccounts(): Promise<number>;
38  identityStats(): Promise<[nbVerbs: number, nbAdjectives: number]>;
39  isAuthorized(address: string): Promise<boolean>;
40  getLatestRegisteredUser(): Promise<string | null>;
41  getTokenName(): Promise<string>;
42}
43
44export interface ArchivistStorageReadAPI {
45  userLevel(userId: number): Promise<number>;
46  isAuthorized(address: string): Promise<boolean>;
47  // BackupStorage's userId-keyed patch history โ€” the on-chain source subscription
48  // patch-resolution reads from (spec ยง15.2/implementation plan: bypasses Arweave
49  // TX lookups entirely; loadActions/loadActionsSince already exist on IBackup).
50  nbWrites(userId: number): Promise<number>;
51  loadActions(userId: number): Promise<[bigint[], bigint[], string[]]>;
52  loadActionsSince(userId: number, index: number): Promise<[bigint[], bigint[], string[]]>;
53}
54
55export interface ScientistStorageReadAPI {
56  getLastMonthId(): Promise<number>;
57  getDeploymentDate(): Promise<[year: number, month: number]>;
58  getStatsRaw(action: number, objectType: number, monthId: number): Promise<[bigint[], number[]]>;
59  isAuthorized(address: string): Promise<boolean>;
60}
61
62export interface ProjectManagerStorageReadAPI {
63  nbProjects(): Promise<number>;
64  getProjectSupportedLanguages(projectId: number): Promise<Array<string>>;
65  addressDonationsPerProject(address: string): Promise<Array<number>>;
66  balanceOfAddress(address: string): Promise<{
67    total: number;
68    donations: number;
69    unspent: number;
70  }>;
71  loadProject(projectId: number): Promise<{
72    id: number;
73    balance: number;
74    versions: any[];
75    availableLanguages: string[];
76  }>;
77  projectVotingBalances(projectId: number): Promise<number>;
78  getWeeklyDonations(week: number): Promise<StripeDonation[]>;
79  getDonationsRange(fromWeek: number, toWeek: number): Promise<StripeDonation[]>;
80  isAuthorized(address: string): Promise<boolean>;
81  // Per-language/per-version accessors (verified against the deployed ABI) โ€”
82  // `lang` is always a human-readable code ("eng"), converted to/from bytes4
83  // internally, matching the convention FactoryWriteAPI.donateToProject etc.
84  // already use. Needed for admin tooling that must compare a project's state
85  // across every language/version it has, not just the latest (loadProject
86  // above only resolves the current version per language).
87  admin(): Promise<string>;
88  getNbLanguages(projectId: number): Promise<number>;
89  getLanguageAtIndex(projectId: number, index: number): Promise<string>;
90  getNbVersions(projectId: number, lang: string): Promise<number>;
91  getVersion(projectId: number, lang: string, versionNumber: number): Promise<ProjectVersion>;
92  getVotingBalance(projectId: number, lang: string): Promise<bigint>;
93  getUserBalances(address: string): Promise<[donated: bigint, allocated: bigint, remaining: bigint]>;
94}
95
96export interface FactoryReadAPI {
97  nbWritesByUsername(username: string): Promise<number>;
98  loadUsername(username: string): Promise<[bigint[], bigint[], string[]]>;
99  loadUsernameSince(username: string, index: number): Promise<[bigint[], bigint[], string[]]>;
100  load(): Promise<[bigint[], bigint[], string[]]>;
101  isAuthorized(address: string): Promise<boolean>;
102  owner(): Promise<string>;
103  oracleSigner(): Promise<string>;
104  bouncerStorage(): Promise<string>;
105  scientistStorage(): Promise<string>;
106  backupStorage(): Promise<string>;
107  projectManagerStorage(): Promise<string>;
108  usedPaymentIntents(paymentIntentId: string): Promise<boolean>;
109}
110
111export interface TransactionOptions {
112  feeLimit?: number;
113  gasLimit?: bigint;
114  value?: number;
115  onAccepted?: (txId: string) => void;
116}
117
118export interface FactoryWriteAPI {
119  register(country: string, language: string, paymentValue: number, options?: TransactionOptions): Promise<any>;
120  unregister(options?: TransactionOptions): Promise<any>;
121  save(patches: Array<bigint>, level: number, textIndex: Array<number>, text: Array<string>, options?: TransactionOptions): Promise<any>;
122  // Names/arg lists match Factory.sol's actual on-chain signatures (verified
123  // against the deployed ABI) โ€” these are admin-only operations (Factory owner),
124  // distinct from the public donor journey (register/save/donateToProject below).
125  createProject(
126    lang: string,
127    uri: string,
128    publicationDate: number,
129    name: string,
130    headline: string,
131    arweaveAddressLogo: string,
132    arweaveAddressBanner: string,
133    descriptionMarkdown: string,
134    minimumFundingBeforeStartWork: number,
135    maximumFunding: number,
136    tags: number[],
137    complexity: number,
138    options?: TransactionOptions
139  ): Promise<any>;
140  addProjectVersion(
141    projectId: number,
142    lang: string,
143    publicationDate: number,
144    uri: string,
145    name: string,
146    headline: string,
147    arweaveAddressLogo: string,
148    arweaveAddressBanner: string,
149    descriptionMarkdown: string,
150    minimumFundingBeforeStartWork: number,
151    maximumFunding: number,
152    tags: number[],
153    status: number,
154    complexity: number,
155    options?: TransactionOptions
156  ): Promise<any>;
157  deleteProject(projectId: number, lang: string, options?: TransactionOptions): Promise<any>;
158  registerStripe(country: string, language: string, paymentIntentId: string, votes: bigint, signature: string, options?: TransactionOptions): Promise<any>;
159  fundStripe(paymentIntentId: string, votes: bigint, signature: string, options?: TransactionOptions): Promise<any>;
160  donateToProject(projectId: number, lang: string, amount: number, options?: TransactionOptions): Promise<any>;
161}
162
163export interface AddressRegistryWriteAPI {
164  addData(key: string, value: string, options?: TransactionOptions): Promise<any>;
165}
166
167export interface AddressRegistryContractRecord {
168  contractAddress: string;
169  abiUrl: string;
170}
171
172export interface AddressRegistryUpdateInfo {
173  timestamp: number;
174  blockNumber: bigint;
175  transactionHash: string;
176}
177
178export interface AddressRegistryReadAPI {
179  getLatestContract(name: string): Promise<[address: string, abiUrl: string]>;
180  getContractHistory(name: string): Promise<AddressRegistryContractRecord[]>;
181  getAllContractNames(): Promise<string[]>;
182  getLatestData(key: string): Promise<string>;
183  getAllDataKeys(): Promise<string[]>;
184  getLastUpdateInfo(name: string): Promise<AddressRegistryUpdateInfo | null>;
185  admin(): Promise<string>;
186}
187
188export interface CollectionInput {
189  id: string;
190  slug: string;
191  manifestUrl: string;
192  importerUsername: string;
193  owner: string;
194  fileCount: number;
195  libraryType: number;
196  publicLibraryId: number;
197  country: string;
198  language: string;
199  isPublic: boolean;
200  initialBookCount: number;
201  dSafeBookCount: number;
202}
203
204export interface Collection extends CollectionInput {
205  verified: boolean;
206  createdAt: number;
207  updatedAt: number;
208  importStatus: number;
209  published: boolean;
210}
211
212export interface LibraryRegistryStorageReadAPI {
213  getUnverifiedCollectionIds(): Promise<string[]>;
214  getVerifiedCollectionIds(): Promise<string[]>;
215  getCollection(id: string): Promise<Collection>;
216}
217
218export interface LibraryRegistryStorageWriteAPI {
219  addUnverifiedCollection(input: CollectionInput, options?: TransactionOptions): Promise<any>;
220  verifyCollection(id: string, options?: TransactionOptions): Promise<any>;
221  rejectCollection(id: string, options?: TransactionOptions): Promise<any>;
222  updateCollectionStats(id: string, initialBookCount: number, dSafeBookCount: number, options?: TransactionOptions): Promise<any>;
223  setImportStatus(id: string, status: number, options?: TransactionOptions): Promise<any>;
224  setPublished(id: string, isPublished: boolean, options?: TransactionOptions): Promise<any>;
225  setCollectionPublicLibrary(id: string, publicLibraryId: number, options?: TransactionOptions): Promise<any>;
226  setPublicLibraryStorage(storageAddr: string, options?: TransactionOptions): Promise<any>;
227}
228
229export enum LibraryOwnership {
230  Public = 0,
231  Private = 1
232}
233
234export enum PreferredFormat {
235  MARC21 = 0,
236  UNIMARC = 1,
237  MARCXML = 2,
238  DublinCore = 3,
239  MODS = 4,
240  BIBFRAME = 5,
241  ONIX = 6,
242  OPDS = 7,
243  InternetArchive = 8,
244  HathiTrust = 9,
245  EAD = 10,
246  Other = 11
247}
248
249export interface PublicLibraryInput {
250  name: string;
251  physicalAddress: string;
252  country: string;
253  language: string;
254  ownership: LibraryOwnership | number;
255  website: string;
256  preferredFormat: PreferredFormat | number;
257}
258
259export interface PublicLibrary extends PublicLibraryInput {
260  id: number;
261  createdAt: number;
262  updatedAt: number;
263}
264
265export interface PublicLibraryStorageReadAPI {
266  libraryExists(id: number): Promise<boolean>;
267  getPublicLibrary(id: number): Promise<PublicLibrary>;
268  nbLibraries(): Promise<number>;
269}
270
271export interface PublicLibraryStorageWriteAPI {
272  addPublicLibrary(input: PublicLibraryInput, options?: TransactionOptions): Promise<any>;
273  updatePublicLibrary(id: number, input: PublicLibraryInput, options?: TransactionOptions): Promise<any>;
274}
275
276export interface SubscriptionManagerReadAPI {
277  getSubscriptions(userId: number): Promise<number[]>;
278  getSubscriptionCount(userId: number): Promise<bigint>;
279  isSubscribed(subscriber: number, target: number): Promise<boolean>;
280  scoreOf(userId: number): Promise<bigint>;
281}
282
283export interface SubscriptionManagerWriteAPI {
284  subscribeTo(subscriber: number, target: number, options?: TransactionOptions): Promise<any>;
285  unsubscribeFrom(subscriber: number, target: number, options?: TransactionOptions): Promise<any>;
286}
287
288export interface ReadOnlyDomainAPI {
289  readonly chainId: number;
290  readonly ready: Promise<void>;
291  readonly bouncerStorage: BouncerStorageReadAPI;
292  readonly scientistStorage: ScientistStorageReadAPI;
293  readonly archivistStorage: ArchivistStorageReadAPI;
294  readonly projectManagerStorage: ProjectManagerStorageReadAPI;
295  readonly factory: FactoryReadAPI;
296  readonly addressRegistry: AddressRegistryReadAPI;
297  readonly subscriptionManager: SubscriptionManagerReadAPI;
298  readonly libraryRegistryStorage: LibraryRegistryStorageReadAPI;
299  readonly publicLibraryStorage: PublicLibraryStorageReadAPI;
300  getUsdPrice(): Promise<number>;
301  getRegistryStatus(): Promise<any>;
302}
303
304export interface WriteOnlyDomainAPI {
305  readonly factory: FactoryWriteAPI;
306  readonly subscriptionManager: SubscriptionManagerWriteAPI;
307  readonly addressRegistry: AddressRegistryWriteAPI;
308  readonly libraryRegistryStorage: LibraryRegistryStorageWriteAPI;
309  readonly publicLibraryStorage: PublicLibraryStorageWriteAPI;
310}
311
312export interface ProjectVersion {
313  uri: string;
314  publicationDate: Date;
315  name: string;
316  headline: string;
317  descriptionMarkdown: string;
318  arweaveAddressLogo: string;
319  arweaveAddressBanner: string;
320  minimumFundingBeforeStartWork: number;
321  maximumFunding: number;
322  tags: Array<number>;
323  status: number;
324  complexity: number;
325}
326
327export interface Project {
328  id: number;
329  balance: number;
330  availableLanguages: Array<string>;
331  versions: Array<ProjectVersion>;
332}
333
334export type ErrorCode =
335  | 'REVERT'
336  | 'INSUFFICIENT_FUNDS'
337  | 'NONCE_TOO_LOW'
338  | 'USER_REJECTED'
339  | 'NETWORK_ERROR'
340  | 'TIMEOUT'
341  | 'INVALID_ARGUMENT'
342  | 'INVALID_COMPRESSED_ACTIONS'
343  | 'UNSUPPORTED_OPERATION'
344  | 'SWITCH_CHAIN_ERROR'
345  | 'SWITCH_TO_UNSUPPORTED_CHAIN'
346  | 'NO_WALLET_CONNECTED'
347  | 'WALLET_NOT_INSTALLED'
348  | 'UNKNOWN';
349
350export interface NormalizedError {
351  code: ErrorCode;
352  message: string;
353  reason?: string;
354  data?: any;
355  originalError?: any;
356}
357
358export type WalletType = 'metamask' | 'private_key';
359
360export interface ConnectResult {
361  address: string;
362  chainId: number;
363}
364
365export interface WalletConnector {
366  readonly type: WalletType;
367  isAvailable(): boolean;
368  connect(): Promise<ConnectResult>;
369  getAccounts(): Promise<string[]>;
370  getChainId(): Promise<number>;
371  requestSwitchChain(chainId: number): Promise<void>;
372  sendTransaction(tx: TransactionRequest, chainId: number): Promise<string>;
373  signMessage(message: string): Promise<string>;
374  onAccountChanged(cb: (address: string) => void): () => void;
375  onChainChanged(cb: (chainId: number) => void): () => void;
376  disconnect(): void;
377}
378
379// EVM-specific capability, kept off the base WalletConnector to preserve the
380// adapter-agnostic boundary (viem must not leak onto the behavioral interface โ€”
381// mirrors why `publicClient` is kept off ReadOnlyDomainAPI). Only EVM connectors
382// implement this; it's how session/writeClient.ts builds an EvmWriteClient
383// without coupling WalletConnector itself to viem.
384export interface EvmWalletConnector extends WalletConnector {
385  getWalletClient(chainId: number): Promise<WalletClient>;
386}
387
388