Smart Contract
Maduro
A.0e964e9e2b53ed06.Maduro
1// MADURO COŃO E TU MADRE
2// CartasParaMaduro - Anonymous Letters to Maduro
3// A decentralized platform for writing anonymous letters to Nicolas Maduro
4
5access(all) contract Maduro {
6
7 // Event emitted when a new letter is submitted
8 access(all) event LetterSubmitted(id: UInt64, timestamp: UFix64)
9 access(all) event LetterReplaced(id: UInt64, timestamp: UFix64)
10
11 // Letter info struct for public reading (view function safe)
12 access(all) struct LetterInfo {
13 access(all) let id: UInt64
14 access(all) let content: String
15 access(all) let timestamp: UFix64
16 access(all) let author: Address?
17
18 init(id: UInt64, content: String, timestamp: UFix64, author: Address?) {
19 self.id = id
20 self.content = content
21 self.timestamp = timestamp
22 self.author = author
23 }
24 }
25
26 // Letter resource - stores the content and metadata
27 access(all) resource Letter {
28 access(all) let id: UInt64
29 access(all) let content: String
30 access(all) let timestamp: UFix64
31 access(all) let author: Address?
32
33 init(id: UInt64, content: String, author: Address?) {
34 self.id = id
35 self.content = content
36 self.timestamp = getCurrentBlock().timestamp
37 self.author = author // Optional anonymity - can be nil for true anonymity
38 }
39 }
40
41 // Storage path for letters collection
42 access(contract) let LettersStoragePath: StoragePath
43
44 // Public path for letter collection capability (if needed for external access)
45 access(contract) let LettersPublicPath: PublicPath
46
47 // Collection of all submitted letters stored in contract account
48 access(contract) var letters: @{UInt64: Letter}
49 access(contract) var nextLetterId: UInt64
50
51 // Public interface for reading letters
52 access(all) fun getLetter(id: UInt64): LetterInfo? {
53 let letterRef = &self.letters[id] as &Letter?
54 if letterRef == nil {
55 return nil
56 }
57 return LetterInfo(
58 id: letterRef!.id,
59 content: letterRef!.content,
60 timestamp: letterRef!.timestamp,
61 author: letterRef!.author
62 )
63 }
64
65 access(all) fun getAllLetterIds(): [UInt64] {
66 return self.letters.keys
67 }
68
69 access(all) fun getLetterCount(): UInt64 {
70 return self.nextLetterId - 1
71 }
72
73 // Validate that letter starts with proper greeting
74 access(all) view fun isValidLetter(_ content: String): Bool {
75 let trimmed = content.toLower()
76 return trimmed.slice(from: 0, upTo: 12) == "para: maduro" ||
77 trimmed.slice(from: 0, upTo: 10) == "to: maduro"
78 }
79
80 // Check if letter contains forbidden text (calling him "Presidente")
81 // HE IS NOT THE PRESIDENT OF VENEZUELA
82 access(all) view fun containsForbiddenText(_ content: String): Bool {
83 let lowerContent = content.toLower()
84 return lowerContent.contains("presidente maduro") ||
85 lowerContent.contains("presidente nicolas maduro") ||
86 lowerContent.contains("presidente nicolás maduro")
87 }
88
89 access(all) fun replaceLetter(id: UInt64, content: String, author: Address?) {
90 pre {
91 content.length > 0: "Letter cannot be empty"
92 content.length <= 10000: "Letter too long (max 10000 characters)"
93 Maduro.isValidLetter(content): "Letter must start with 'Para: Maduro' or 'To: Maduro'"
94 !Maduro.containsForbiddenText(content): "Cannot call him 'Presidente Maduro' - HE IS NOT THE PRESIDENT OF VENEZUELA"
95 }
96 // Remove old letter if it exists
97 if let oldLetter <- self.letters.remove(key: id) {
98 destroy oldLetter
99 }
100 // Create the Letter resource with the correct id
101 let letter <- create Letter(
102 id: id,
103 content: content,
104 author: author
105 )
106 // Store the letter resource in the contract's storage
107 // Use force-assignment since we've removed the old value (slot is guaranteed to be empty)
108 self.letters[id] <-! letter
109 emit LetterReplaced(id: id, timestamp: getCurrentBlock().timestamp)
110 }
111
112 // Submit a new letter (transaction function)
113 access(all) fun submitLetter(content: String, author: Address?) {
114 pre {
115 content.length > 0: "Letter cannot be empty"
116 content.length <= 10000: "Letter too long (max 10000 characters)"
117 Maduro.isValidLetter(content): "Letter must start with 'Para: Maduro' or 'To: Maduro'"
118 !Maduro.containsForbiddenText(content): "Cannot call him 'Presidente Maduro' - HE IS NOT THE PRESIDENT OF VENEZUELA"
119 }
120
121 // Create the Letter resource
122 let letter <- create Letter(
123 id: self.nextLetterId,
124 content: content,
125 author: author
126 )
127
128 // Store the letter resource in the contract's storage
129 self.letters[self.nextLetterId] <-! letter
130 self.nextLetterId = self.nextLetterId + 1
131
132 emit LetterSubmitted(
133 id: self.nextLetterId - 1,
134 timestamp: getCurrentBlock().timestamp
135 )
136 }
137
138 init() {
139 // Initialize storage paths for the contract
140 self.LettersStoragePath = /storage/maduroLetters
141 self.LettersPublicPath = /public/maduroLetters
142
143 // Initialize the letters collection
144 self.letters <- {}
145 self.nextLetterId = 1
146 }
147}