Smart Contract

Alexandria

A.fed1adffd14ea9d0.Alexandria

Valid From

143,408,009

Deployed

2w ago
Feb 10, 2026, 04:53:30 PM UTC

Dependents

18 imports
1// The Alexandria Library - Preserving Knowledge Forever
2//
3// The ancient Library of Alexandria was one of the largest and most significant libraries 
4// of the ancient world, dedicated to the Muses and housing the greatest collection of 
5// knowledge in human history. Its tragic destruction represented an immeasurable loss 
6// to humanity's collective wisdom.
7//
8// This smart contract honors that legacy by creating a decentralized, immutable library 
9// on the Flow blockchain. By leveraging blockchain technology, we ensure that knowledge 
10// and literature can never be lost, censored, or destroyed - preserving humanity's 
11// cultural heritage and making it freely accessible to all, forever.
12//
13// Written by: @NoahOverflow
14// 
15// License: MIT
16
17access(all)
18contract Alexandria {
19    // -----------------------------------------------------------------------
20	// Alexandria Contract-level information
21	// -----------------------------------------------------------------------
22	access(self) let libraryInfo: {String: AnyStruct}
23    access(self) let titles: {String: String}
24    access(self) let authors: {String: [String]}
25    access(self) let genres: {String: [String]}
26    // Paths
27    access(all) let AdminStoragePath: StoragePath
28    access(all) let LibrarianStoragePath: StoragePath
29    access(all) let UserPreferenceStorage: StoragePath
30    access(all) let UserPreferencePublic: PublicPath
31    // Events
32    access(all) event ContractInitialized()
33    access(all) event BookAdded(title: String, author: String, genre: String)
34    access(all) event ChapterNameAdded(bookTitle: String, chapterTitle: String)
35    access(all) event ChapterAdded(bookTitle: String, chapterTitle: String)
36    access(all) event ChapterRemoved(bookTitle: String, chapterTitle: String)
37    access(all) event ChapterSubmitted(bookTitle: String, chapterTitle: String, librarian: Address)
38    access(all) event ParagraphAdded(bookTitle: String, chapterTitle: String)
39    access(all) event ParagraphRemoved(bookTitle: String, chapterTitle: String)
40
41    // Entitlements
42    access(all) entitlement LibrarianActions
43    access(all) entitlement AdminActions
44        // -----------------------------------------------------------------------
45	// Alexandria Book Attachments
46	// -----------------------------------------------------------------------
47    access(all) attachment Keeper for Book {
48        access(all) var keeper: Address
49        init(keeper: Address) {
50            self.keeper = keeper
51        }
52
53        access(all) fun updateKeeper(keeper: Address) {
54            self.keeper = keeper
55        }
56    }
57    // Add a keeper to a book
58    access(account) fun addKeeper(bookTitle: String, keeper: Address) {
59        pre {
60            Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
61        }
62        let identifier = "Alexandria_Library_\(Alexandria.account.address)_\(bookTitle)"
63        let book <- Alexandria.account.storage.load<@Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
64        // create new keeper attachment
65        let entitledBook <- attach Keeper(keeper: keeper) to <- book
66        // save entitled book to storage
67        Alexandria.account.storage.save(<- entitledBook, to: StoragePath(identifier: identifier)!)
68    }
69    // Get a book's keepers
70     access(all) fun getKeeper(bookTitle: String): Address? {
71        pre {
72            Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
73        }
74        let identifier = "Alexandria_Library_\(Alexandria.account.address)_\(bookTitle)"
75        let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
76        let keeperAttachment = book[Alexandria.Keeper]
77    ?? panic("No keeper set for this book")
78
79        return keeperAttachment.keeper
80    } 
81    // -----------------------------------------------------------------------
82	// Alexandria Book Resource
83	// -----------------------------------------------------------------------
84    access(all)
85    resource Book {
86        access(all) let Title: String
87        access(all) let Author: String
88        access(all) let Genre: String
89        access(all) let Summary: String
90        access(all) let Edition: String
91        // This is a mapping of chapterName -> 
92        access(all) let Chapters: {String: Chapter?}
93        // Dictionary of chapter names
94        access(all) let chapterNames: {String: Bool}
95        // Dictionary of Chapters submitted by Librarians
96        // these Chapters are not automatically added to the book
97        // They go through a review process and then, if approved, they get
98        // added to the book
99        // This is a mapping from chapterName -> Librarian.address -> Chapter
100        access(all) let pendingReview: {String: {Address: Chapter?}}
101        access(all) let extra: {String: AnyStruct}
102
103        init(
104            _ title: String,
105            _ author: String,
106            _ genre: String,
107            _ edition: String,
108            _ summary: String,
109        ) {
110            self.Title = title
111            self.Author = author
112            self.Genre = genre
113            self.Summary = summary
114            self.Edition = edition
115            self.Chapters = {}
116            self.chapterNames = {}
117            self.pendingReview = {}
118            self.extra = {}
119        }
120        // Add a chapter name to the list of chapters
121        // this will be helpful to guide Librarians on what has been submitted
122        // or is still missing
123        access(all)
124        fun addChapterName(chapterName: String): [String] {
125            pre {
126                self.chapterNames[chapterName] == nil: "This chapter already exists"
127            }
128            self.chapterNames[chapterName] = true
129            
130            emit ChapterNameAdded(bookTitle: self.Title, chapterTitle: chapterName)
131
132            return self.Chapters.keys
133        }
134        // Add a Chapter directly into the book
135        // this function is used only by the Admin
136        access(all)
137        fun addChapter(chapterName: String, chapter: Chapter): [String] {
138            pre {
139                // Check that the 
140                self.chapterNames[chapterName] != nil : "This chapter doesn't exists"
141            }
142            self.Chapters[chapterName] = chapter
143
144            return self.Chapters.keys
145        }
146        // This is the function that a Librarian uses
147        // in order to submit a Chapter for review
148        access(all)
149        fun submitChapter(chapterName: String, chapter: Chapter, librarian: Address) {
150            pre {
151                self.chapterNames[chapterName] != nil: "This chapter doesn't exists"
152            }
153
154            self.pendingReview[chapterName] = {librarian : chapter}
155        }
156        // This function is used by the Admin to
157        // approve Chapter submissions
158        access(all)
159        fun approveChapter(chapterName: String, librarian: Address) {
160            pre {
161                self.Chapters[chapterName] != nil: "This chapter doesn't exists"
162                self.pendingReview[chapterName]![librarian] != nil: "There are no Chapters submitted by this Librarian: \(librarian.toString())"
163            }
164            // Remove chapter from the Pending list
165            let chapter = self.pendingReview[chapterName]!.remove(key: librarian)!
166            // Add the Chapter to the approved mapping
167            self.Chapters[chapterName] = chapter
168            // Send Karma and FlowToken to the Librarian's account
169
170        }
171        // Get Chapter
172        access(all)
173        fun getChapter(chapterTitle: String): Chapter? {
174            return self.Chapters[chapterTitle]!
175        }
176     access(all)
177        fun removeChapter(chapterTitle: String): String {
178            pre {
179                self.Chapters[chapterTitle] != nil: "This chapter doesn't exists"
180            }
181            let chapter = self.Chapters.remove(key: chapterTitle)!
182            return chapterTitle
183        }
184
185        // Function to add a paragraph to a chapter
186        access(all)
187        fun addParagraph(chapterTitle: String, paragraph: String) {
188            pre {
189                self.Chapters[chapterTitle] != nil: "This chapter doesn't exists"
190            }
191            post {
192                self.Chapters[chapterTitle]!!.paragraphs.length > 1: "The chapter doesn't have any paragraphs"
193            }
194            let chap = self.Chapters[chapterTitle]!!.paragraphs.append(paragraph)
195            emit ParagraphAdded(bookTitle: self.Title, chapterTitle: chapterTitle)
196        }
197        // Function to remove the last paragraph from a chapter
198        access(all)
199        fun removeLastParagraph(chapterTitle: String) {
200            pre {
201                self.Chapters[chapterTitle] != nil: "This chapter doesn't exists"
202            }
203            post {
204                self.Chapters[chapterTitle]!!.paragraphs.length > 1: "The chapter doesn't have any paragraphs"
205            }
206            let chapter = self.Chapters[chapterTitle]!!.removeLastParagraph()
207            emit ParagraphRemoved(bookTitle: self.Title, chapterTitle: chapterTitle)
208        }
209
210        // Function to get a paragraph from a chapter
211        access(all) view fun getParagraph(chapterTitle: String, paragraphIndex: Int): String {
212            return self.Chapters[chapterTitle]!!.getParagraph(paragraphIndex: paragraphIndex)
213        }
214        // Function to get all chapter titles
215        access(all) view fun getChapterTitles(): [String] {
216            return self.chapterNames.keys 
217        }
218    }
219
220    access(all)
221    struct Chapter {
222        access(all) let bookTitle: String
223        access(all) let chapterTitle: String
224        access(all) let index: Int
225        access(all) let paragraphs: [String]
226        access(all) let extra: {String: AnyStruct}
227
228        init(
229            _ bookTitle: String,
230            _ chapterTitle: String,
231            _ index: Int,
232            _ paragraphs: [String]
233        ) {
234            self.bookTitle = bookTitle
235            self.chapterTitle = chapterTitle
236            self.index = index
237            self.paragraphs = paragraphs
238            self.extra = {}
239        }
240
241        // Function to add to a chapter's paragraphs
242        access(all)
243        fun addParagraph(paragraph: String) {
244            self.paragraphs.append(paragraph)
245        }
246
247        access(all)
248        fun removeLastParagraph(): String {
249            let paragraph = self.paragraphs.removeLast()
250            return paragraph
251        }
252
253        access(all) view fun getParagraph(paragraphIndex: Int): String {
254            return self.paragraphs[paragraphIndex]
255        }
256    }
257    // -----------------------------------------------------------------------
258	// User Preferences Resource
259	// -----------------------------------------------------------------------
260    
261    // Public interface to get a user's favorites and bookmarks
262    access(all) resource interface UserPreferencesPublic {
263        access(all) fun getFavorites(): [String]
264        access(all) fun getBookmarks(): [String]
265    }
266
267    access(all)
268    resource UserPreferences {
269        access(all) var favorites: {String: Bool} 
270        access(all) var bookmarks: {String: Bool} 
271        access(all) var extra: {String: AnyStruct}
272
273        init() {
274            self.favorites = {}
275            self.bookmarks = {}
276            self.extra = {}
277        }
278        // Add a favorite book
279        access(all)
280        fun addFavorite(bookName: String) {
281            pre {
282                self.favorites[bookName] == nil: "This book is already in your favorites"
283                Alexandria.titles[bookName] != nil: "This book is not part of the library"
284            }
285
286            self.favorites[bookName] = true       
287        }
288        // Remove a favorite book
289        access(all)
290        fun removeFavorite(bookName: String) {
291            pre {
292                self.favorites[bookName] == nil: "This book is not in your favorites"
293                Alexandria.titles[bookName] != nil: "This book is not part of the library"
294            }
295
296            let bookName = self.favorites.remove(key: bookName)
297        }
298        // Bookmark a book
299        access(all)
300        fun addBookmark(bookName: String) {
301            pre {
302                self.bookmarks[bookName] == nil: "This book is already in your bookmarks"
303                Alexandria.titles[bookName] != nil: "This book is not part of the library"
304            }
305
306            self.bookmarks[bookName] = true       
307        }
308        // Remove a bookmark
309        access(all)
310        fun removeBookmark(bookName: String) {
311            pre {
312                self.bookmarks[bookName] == nil: "This book is not in your bookmarks"
313                Alexandria.titles[bookName] != nil: "This book is not part of the library"
314            }
315
316            let bookName = self.bookmarks.remove(key: bookName)
317        }
318        // Function to get all favorites
319        access(all)
320        fun getFavorites(): [String] {
321            return self.favorites.keys
322        }
323        // Function to get all bookmarks
324        access(all)
325        fun getBookmarks(): [String] {
326            return self.bookmarks.keys
327        }
328    }
329
330    // -----------------------------------------------------------------------
331	// Alexandria Admin Resource
332	// -----------------------------------------------------------------------
333
334    access(all)
335    resource Admin {
336        // Function to add a book to the library
337        access(AdminActions)
338        fun addBook(
339            title: String,
340            author: String,
341            genre: String,
342            edition: String,
343            summary: String,
344        ) {
345            pre {
346                Alexandria.titles[title] == nil: "This book is already in the Library."
347            }
348            // create new book resource
349            let newBook <- create Book(title, author, genre, edition, summary)
350            // create new path identifier for book
351            let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(title)"
352            // Add the book's details to the library's catalog
353            Alexandria.titles[title] = newBook.Title
354            // Check if this Author already exists
355            if Alexandria.authors[author] == nil {
356                // create new list of titles under this author
357                Alexandria.authors[author] = []
358                // add this title to the list
359                Alexandria.authors[author]!.append(title)  
360            } else {
361                // Add title to the list of titles under this author
362                Alexandria.authors[author]!.append(title)    
363            }
364            Alexandria.titles[author] = newBook.Author
365            // Check if this Genre already exists
366            if Alexandria.genres[genre] == nil {
367                Alexandria.genres[genre] = []
368                Alexandria.genres[genre]!.append(title)    
369            } else {
370                Alexandria.genres[genre]!.append(title)    
371            }
372            // add new book to the library
373		    Alexandria.account.storage.save(<- newBook, to: StoragePath(identifier: identifier)!)
374            // Emit book added event
375            emit BookAdded(title: title, author: author, genre: genre)
376
377        }
378        // Add a chapter to a book
379        access(AdminActions)
380        fun addChapter(bookTitle: String, chapter: Chapter): [String] { 
381            pre {
382                Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
383            }
384            // create book path identifier based on title
385            let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
386            // fetch book
387            let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
388            // Emit event
389            emit ChapterAdded(bookTitle: bookTitle, chapterTitle: chapter.chapterTitle)
390            // add chapter to book and
391            // return the current number of chapters in this book 
392            return book.addChapter(chapterName: chapter.chapterTitle, chapter: chapter)
393        }
394        // Add a chapter title to a book
395        access(AdminActions)
396        fun addChapterName(bookTitle: String, chapterName: String) {
397            // create book path identifier based on title
398            let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
399            // fetch book
400            let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!    
401            // Emit event
402            // emit ChapterAdded(bookTitle: bookTitle, chapterTitle: chapter.chapterTitle)
403            // add chapter name to book
404            let newChapterTitle = book.addChapterName(chapterName: chapterName)        
405            // return newChapterTitle
406        }
407        // Remove the last chapter from a book
408        access(AdminActions)
409        fun removeChapter(bookTitle: String, chapterTitle: String) {
410            pre {
411                Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
412            }
413            // create book path identifier based on title
414            let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
415            // fetch book
416            let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
417            // remove last chapter from book
418            let chapterTitle = book.removeChapter(chapterTitle: chapterTitle)
419            // Emit event
420            emit ChapterRemoved(bookTitle: bookTitle, chapterTitle: chapterTitle)
421        }
422        // Add a genre to the library
423        access(AdminActions)
424        fun addGenre(genre: String) {
425            pre {
426                Alexandria.genres[genre] == nil: "This genre already exists"
427            }
428            Alexandria.genres[genre] = []
429        }
430        // create a new Admin resource
431		access(AdminActions)
432        fun createAdmin(): @Admin {
433			return <- create Admin()
434		}
435		// change Alexandria of library info
436		access(AdminActions)
437        fun changeField(key: String, value: AnyStruct) {
438			Alexandria.libraryInfo[key] = value
439		}
440
441
442    }
443    // -----------------------------------------------------------------------
444	// Alexandria Librarian Resource
445	// -----------------------------------------------------------------------
446    
447    // Librarians are users that are contributing to the creation of the Library
448    // by submitting book chapters to be reviewed, and then added to the storage
449    // upon approval
450
451    access(all)
452    resource Librarian {
453        // The reputation of this librarian
454        access(all) let karma: UFix64
455        // A list of chapters submitted by this Librarian
456        // that have been approved, and the book they belong to
457        access(all) let approvedChapters: {String: [Chapter]}
458        // A list of chapters that pending for approval
459        access(all) let pendingChapters: {String: [Chapter]}
460        access(all) let extra: {String: AnyStruct}
461
462        init() {
463            self.karma = 0.0
464            self.approvedChapters = {}
465            self.pendingChapters = {}
466            self.extra = {}
467        }
468        // Function used to submit a Chapter to a book
469        access(LibrarianActions)
470        fun submitChapter(bookTitle: String, chapter: Chapter) {
471            pre {
472                Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
473            }
474            // create book path identifier based on title
475            let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
476            // fetch book
477            let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
478            // Emit event
479            emit ChapterSubmitted(bookTitle: bookTitle, chapterTitle: chapter.chapterTitle, librarian: self.owner!.address)
480            // return the current number of chapters in this book 
481            book.submitChapter(chapterName: chapter.chapterTitle, chapter: chapter, librarian: self.owner!.address)
482        }
483
484        access(LibrarianActions)
485        fun addGenre(genre: String) {
486            pre {
487                Alexandria.genres[genre] == nil: "This genre already exists"
488            }
489            Alexandria.genres[genre] = []
490        }
491    } 
492
493    // -----------------------------------------------------------------------
494	// Alexandria Public Services
495	// -----------------------------------------------------------------------
496
497    access(all)
498    fun createEmptyPreferences(): @Alexandria.UserPreferences {
499        return <- create UserPreferences()
500    }
501    // Fetch one book
502    access(all)
503    fun getBook(bookTitle: String): &Book {
504        pre {
505            Alexandria.titles[bookTitle] != nil: "This book doesn't exist in the Library."
506        }
507        // create book path identifier based on title
508        let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
509        let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
510        return book
511    }
512    // Fetch a book's chapter titles
513    access(all)
514    fun getBookChapterTitles(bookTitle: String): [String] {
515        let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
516        let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
517        let chapterTitles = book.getChapterTitles()
518        return chapterTitles
519    }   
520    // Fetch a book's chapter
521    access(all)
522    fun getBookChapter(bookTitle: String, chapterTitle: String): Chapter? {
523        let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
524        let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
525        return book.getChapter(chapterTitle: chapterTitle)
526    }
527    // Fetch a book's paragraph
528    access(all)
529    fun getBookParagraph(bookTitle: String, chapterTitle: String, paragraphIndex: Int): String {
530        let identifier = "Alexandria_Library_\(Alexandria.account.address.toString())_\(bookTitle)"
531        let book = Alexandria.account.storage.borrow<&Alexandria.Book>(from: StoragePath(identifier: identifier)!)!
532        return book.getParagraph(chapterTitle: chapterTitle, paragraphIndex: paragraphIndex)
533    }
534    // Fetch all registered authors
535    access(all)
536    fun getAuthors(): [String]? {
537        return self.authors.keys
538    }
539    // Fetch all registered genres
540    access(all)
541    fun getAllGenres(): [String] {
542        return self.genres.keys
543    }
544    // Fetch all titles under a genre
545    access(all)
546    fun getGenre(genre: String): [String]? {
547        return self.genres[genre]
548    }
549    // Fetch all titles under an author
550    access(all)
551    fun getAuthor(author: String): [String]? {
552        return self.authors[author]
553    }
554    init() {
555        let identifier = "Alexandria_Library_\(self.account.address)_"
556
557        self.AdminStoragePath = StoragePath(identifier: "\(identifier)Admin")!
558        self.LibrarianStoragePath = StoragePath(identifier: "\(identifier)Librarian")!
559        self.UserPreferenceStorage = StoragePath(identifier: "\(identifier)User_Preferences")!
560        self.UserPreferencePublic = PublicPath(identifier: "\(identifier)User_Preferences_Public")!
561
562        self.libraryInfo = {}
563        self.titles = {}
564        self.authors = {}
565        self.genres = {
566            "Adventure": [],
567            "Biography": [],
568            "Realism": [],
569            "Dystopian": [],
570            "Fantasy": [],
571            "Horror": [],
572            "Mistery": [],
573            "History": [],
574            "Romance": [],
575            "Thriller": [],
576            "Fiction": [],
577            "Science Fiction": [],
578            "Western": [],
579            "Philosophy": [],
580            "Psychology": [],
581            "Literature": [],
582            "Feminist Literature": []
583            }
584
585		// Create a Admin resource and save it to Alexandria account storage
586		let Admin <- create Admin()
587		self.account.storage.save(<- Admin, to: self.AdminStoragePath)
588
589		// Create a UserPreference resource and save it to Alexandria account storage
590        let UserPreference <- create UserPreferences()
591        self.account.storage.save(<- UserPreference, to: self.UserPreferenceStorage)
592
593        // create a public capability for the user preferences
594	    let storageCap = self.account.capabilities.storage.issue<&{Alexandria.UserPreferencesPublic}>(self.UserPreferenceStorage)
595		self.account.capabilities.publish(storageCap, at: self.UserPreferencePublic)
596
597        emit ContractInitialized()
598    }
599}