Smart Contract
StarlyCardMarket
A.5b82f21c0edf76e3.StarlyCardMarket
1import FUSD from 0x3c5959b568896393
2import StarlyCard from 0x5b82f21c0edf76e3
3import FungibleToken from 0xf233dcee88fe0abe
4import NonFungibleToken from 0x1d7e57aa55817448
5
6access(all) contract StarlyCardMarket {
7
8 access(all) struct SaleCutReceiver {
9 access(all) let receiver: Capability<&FUSD.Vault>
10 access(all) let percent: UFix64
11
12 init(receiver: Capability<&FUSD.Vault>, percent: UFix64) {
13 self.receiver = receiver
14 self.percent = percent
15 }
16 }
17
18 access(all) view fun checkSaleCutReceiver(saleCutReceiver: StarlyCardMarket.SaleCutReceiver): Bool {
19 return saleCutReceiver.receiver.borrow() != nil
20 }
21
22 access(all) view fun checkSaleCutReceivers(saleCutReceivers: [StarlyCardMarket.SaleCutReceiver]): Bool {
23 for saleCutReceiver in saleCutReceivers {
24 if (saleCutReceiver.receiver.borrow() == nil) {
25 return false
26 }
27 }
28 return true
29 }
30
31 access(all) struct SaleCutReceiverV2 {
32 access(all) let receiver: Capability<&{FungibleToken.Receiver}>
33 access(all) let percent: UFix64
34
35 init(receiver: Capability<&{FungibleToken.Receiver}>, percent: UFix64) {
36 self.receiver = receiver
37 self.percent = percent
38 }
39 }
40
41 access(all) view fun checkSaleCutReceiverV2(saleCutReceiver: StarlyCardMarket.SaleCutReceiverV2): Bool {
42 return saleCutReceiver.receiver.borrow() != nil
43 }
44
45 access(all) view fun checkSaleCutReceiversV2(saleCutReceivers: [StarlyCardMarket.SaleCutReceiverV2]): Bool {
46 for saleCutReceiver in saleCutReceivers {
47 if (saleCutReceiver.receiver.borrow() == nil) {
48 return false
49 }
50 }
51 return true
52 }
53
54 access(all) struct SaleCut {
55 access(all) let address: Address
56 access(all) let amount: UFix64
57 access(all) let percent: UFix64
58
59 init(address: Address, amount: UFix64, percent: UFix64) {
60 self.address = address
61 self.amount = amount
62 self.percent = percent
63 }
64 }
65
66 access(all) event SaleOfferCreated(
67 itemID: UInt64,
68 starlyID: String,
69 price: UFix64,
70 sellerSaleCut: SaleCut,
71 beneficiarySaleCut: SaleCut,
72 creatorSaleCut: SaleCut,
73 additionalSaleCuts: [SaleCut])
74 access(all) event SaleOfferAccepted(
75 itemID: UInt64,
76 starlyID: String,
77 price: UFix64,
78 buyerAddress: Address,
79 sellerSaleCut: SaleCut,
80 beneficiarySaleCut: SaleCut,
81 creatorSaleCut: SaleCut,
82 additionalSaleCuts: [SaleCut])
83 access(all) event SaleOfferFinished(itemID: UInt64, sellerAddress: Address)
84 access(all) event CollectionRemovedSaleOffer(itemID: UInt64, sellerAddress: Address)
85 access(all) event CollectionInsertedSaleOffer(
86 itemID: UInt64,
87 starlyID: String,
88 price: UFix64,
89 sellerAddress: Address)
90
91 access(all) let CollectionStoragePath: StoragePath
92 access(all) let CollectionPublicPath: PublicPath
93
94 access(all) resource interface SaleOfferPublicView {
95 access(all) let itemID: UInt64
96 access(all) let starlyID: String
97 access(all) let price: UFix64
98 }
99
100 access(all) resource SaleOffer: SaleOfferPublicView {
101 access(self) var saleCompleted: Bool
102 access(all) let itemID: UInt64
103 access(all) let starlyID: String
104 access(all) let price: UFix64
105 access(self) let sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &StarlyCard.Collection>
106 access(self) let sellerSaleCutReceiver: SaleCutReceiver
107 access(self) let beneficiarySaleCutReceiver: SaleCutReceiver
108 access(self) let creatorSaleCutReceiver: SaleCutReceiver
109 access(self) let additionalSaleCutReceivers: [SaleCutReceiver]
110
111 access(all) fun accept(
112 buyerCollection: &StarlyCard.Collection,
113 buyerPayment: @{FungibleToken.Vault},
114 buyerAddress: Address
115 ) {
116 pre {
117 buyerPayment.balance == self.price: "payment does not equal offer price"
118 self.saleCompleted == false: "the sale offer has already been accepted"
119 }
120
121 self.saleCompleted = true
122
123 let beneficiaryCutAmount = self.price * self.beneficiarySaleCutReceiver.percent
124 let beneficiaryCut <- buyerPayment.withdraw(amount: beneficiaryCutAmount)
125 self.beneficiarySaleCutReceiver.receiver.borrow()!.deposit(from: <- beneficiaryCut)
126
127 let creatorCutAmount = self.price * self.creatorSaleCutReceiver.percent
128 let creatorCut <- buyerPayment.withdraw(amount: creatorCutAmount)
129 self.creatorSaleCutReceiver.receiver.borrow()!.deposit(from: <- creatorCut)
130
131 var additionalSaleCuts: [SaleCut] = []
132 for additionalSaleCutReceiver in self.additionalSaleCutReceivers {
133 let additionalCutAmount = self.price * additionalSaleCutReceiver.percent
134 let additionalCut <- buyerPayment.withdraw(amount: additionalCutAmount)
135 additionalSaleCutReceiver.receiver.borrow()!.deposit(from: <- additionalCut)
136 additionalSaleCuts.append(StarlyCardMarket.SaleCut(
137 address: additionalSaleCutReceiver.receiver.address,
138 amount: additionalCutAmount,
139 percent: additionalSaleCutReceiver.percent));
140 }
141
142 let sellerCutAmount = buyerPayment.balance
143 self.sellerSaleCutReceiver.receiver.borrow()!.deposit(from: <- buyerPayment)
144
145 let nft <- self.sellerItemProvider.borrow()!.withdraw(withdrawID: self.itemID)
146 buyerCollection.deposit(token: <- nft)
147
148 emit SaleOfferAccepted(
149 itemID: self.itemID,
150 starlyID: self.starlyID,
151 price: self.price,
152 buyerAddress: buyerAddress,
153 sellerSaleCut: SaleCut(
154 address: self.sellerSaleCutReceiver.receiver.address,
155 amount: sellerCutAmount,
156 percent: self.sellerSaleCutReceiver.percent),
157 beneficiarySaleCut: SaleCut(
158 address: self.beneficiarySaleCutReceiver.receiver.address,
159 amount: beneficiaryCutAmount,
160 percent: self.beneficiarySaleCutReceiver.percent),
161 creatorSaleCut: SaleCut(
162 address: self.creatorSaleCutReceiver.receiver.address,
163 amount: creatorCutAmount,
164 percent: self.creatorSaleCutReceiver.percent),
165 additionalSaleCuts: additionalSaleCuts)
166 }
167
168 init(
169 itemID: UInt64,
170 starlyID: String,
171 price: UFix64,
172 sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &StarlyCard.Collection>,
173 sellerSaleCutReceiver: SaleCutReceiver,
174 beneficiarySaleCutReceiver: SaleCutReceiver,
175 creatorSaleCutReceiver: SaleCutReceiver,
176 additionalSaleCutReceivers: [SaleCutReceiver],
177 ) {
178 pre {
179 sellerItemProvider.borrow() != nil: "Cannot borrow seller"
180 StarlyCardMarket.checkSaleCutReceiver(saleCutReceiver: sellerSaleCutReceiver): "Cannot borrow receiver in sellerSaleCutReceiver"
181 StarlyCardMarket.checkSaleCutReceiver(saleCutReceiver: beneficiarySaleCutReceiver): "Cannot borrow receiver in beneficiarySaleCutReceiver"
182 StarlyCardMarket.checkSaleCutReceiver(saleCutReceiver: creatorSaleCutReceiver): "Cannot borrow receiver in creatorSaleCutReceiver"
183 StarlyCardMarket.checkSaleCutReceivers(saleCutReceivers: additionalSaleCutReceivers): "Cannot borrow receiver in additionalSaleCutReceivers"
184 }
185
186 self.saleCompleted = false
187 self.itemID = itemID
188 self.starlyID = starlyID
189 self.price = price
190 self.sellerItemProvider = sellerItemProvider
191 self.sellerSaleCutReceiver = sellerSaleCutReceiver
192 self.beneficiarySaleCutReceiver = beneficiarySaleCutReceiver
193 self.creatorSaleCutReceiver = creatorSaleCutReceiver
194 self.additionalSaleCutReceivers = additionalSaleCutReceivers
195
196 let sellerCutAmount = price * sellerSaleCutReceiver.percent
197 let beneficiaryCutAmount = price * beneficiarySaleCutReceiver.percent
198 let creatorCutAmount = price * creatorSaleCutReceiver.percent
199
200 var additionalSaleCuts: [SaleCut] = []
201 for additionalSaleCutReceiver in additionalSaleCutReceivers {
202 let additionalCutAmount = price * additionalSaleCutReceiver.percent
203 additionalSaleCuts.append(StarlyCardMarket.SaleCut(
204 address: additionalSaleCutReceiver.receiver.address,
205 amount: additionalCutAmount,
206 percent: additionalSaleCutReceiver.percent));
207 }
208
209 emit SaleOfferCreated(
210 itemID: self.itemID,
211 starlyID: self.starlyID,
212 price: self.price,
213 sellerSaleCut: SaleCut(
214 address: self.sellerSaleCutReceiver.receiver.address,
215 amount: sellerCutAmount,
216 percent: self.sellerSaleCutReceiver.percent),
217 beneficiarySaleCut: SaleCut(
218 address: self.beneficiarySaleCutReceiver.receiver.address,
219 amount: beneficiaryCutAmount,
220 percent: self.beneficiarySaleCutReceiver.percent),
221 creatorSaleCut: SaleCut(
222 address: self.creatorSaleCutReceiver.receiver.address,
223 amount: creatorCutAmount,
224 percent: self.creatorSaleCutReceiver.percent),
225 additionalSaleCuts: additionalSaleCuts)
226 }
227 }
228
229 access(all) fun createSaleOffer (
230 itemID: UInt64,
231 starlyID: String,
232 price: UFix64,
233 sellerItemProvider: Capability<auth(NonFungibleToken.Withdraw) &StarlyCard.Collection>,
234 sellerSaleCutReceiver: SaleCutReceiver,
235 beneficiarySaleCutReceiver: SaleCutReceiver,
236 creatorSaleCutReceiver: SaleCutReceiver,
237 additionalSaleCutReceivers: [SaleCutReceiver]
238 ): @SaleOffer {
239 return <- create SaleOffer(
240 itemID: itemID,
241 starlyID: starlyID,
242 price: price,
243 sellerItemProvider: sellerItemProvider,
244 sellerSaleCutReceiver: sellerSaleCutReceiver,
245 beneficiarySaleCutReceiver: beneficiarySaleCutReceiver,
246 creatorSaleCutReceiver: creatorSaleCutReceiver,
247 additionalSaleCutReceivers: additionalSaleCutReceivers)
248 }
249
250 access(all) resource interface CollectionManager {
251 access(all) fun insert(offer: @StarlyCardMarket.SaleOffer)
252 access(all) fun remove(itemID: UInt64): @SaleOffer
253 }
254
255 access(all) resource interface CollectionPurchaser {
256 access(all) fun purchase(
257 itemID: UInt64,
258 buyerCollection: &StarlyCard.Collection,
259 buyerPayment: @{FungibleToken.Vault},
260 buyerAddress: Address
261 )
262 }
263
264 access(all) resource interface CollectionPublic {
265 access(all) view fun getSaleOfferIDs(): [UInt64]
266 access(all) view fun borrowSaleItem(itemID: UInt64): &SaleOffer?
267 access(all) fun purchase(
268 itemID: UInt64,
269 buyerCollection: &StarlyCard.Collection,
270 buyerPayment: @{FungibleToken.Vault},
271 buyerAddress: Address
272 )
273 }
274
275 access(all) resource Collection : CollectionManager, CollectionPurchaser, CollectionPublic {
276 access(all) var saleOffers: @{UInt64: SaleOffer}
277
278 access(all) fun insert(offer: @StarlyCardMarket.SaleOffer) {
279 let itemID: UInt64 = offer.itemID
280 let starlyID: String = offer.starlyID
281 let price: UFix64 = offer.price
282
283 let oldOffer <- self.saleOffers[itemID] <- offer
284 destroy oldOffer
285
286 emit CollectionInsertedSaleOffer(
287 itemID: itemID,
288 starlyID: starlyID,
289 price: price,
290 sellerAddress: self.owner?.address!
291 )
292 }
293
294 access(all) fun remove(itemID: UInt64): @SaleOffer {
295 emit CollectionRemovedSaleOffer(itemID: itemID, sellerAddress: self.owner?.address!)
296 return <- (self.saleOffers.remove(key: itemID) ?? panic("missing SaleOffer"))
297 }
298
299 access(all) fun purchase(
300 itemID: UInt64,
301 buyerCollection: &StarlyCard.Collection,
302 buyerPayment: @{FungibleToken.Vault},
303 buyerAddress: Address
304 ) {
305 pre {
306 self.saleOffers[itemID] != nil: "SaleOffer does not exist in the collection!"
307 }
308 let offer <- self.remove(itemID: itemID)
309 offer.accept(buyerCollection: buyerCollection, buyerPayment: <- buyerPayment, buyerAddress: buyerAddress)
310 destroy offer
311 }
312
313 access(all) view fun getSaleOfferIDs(): [UInt64] {
314 return self.saleOffers.keys
315 }
316
317 access(all) view fun borrowSaleItem(itemID: UInt64): &SaleOffer? {
318 if self.saleOffers[itemID] == nil {
319 return nil
320 } else {
321 return (&self.saleOffers[itemID] as &SaleOffer?)!
322 }
323 }
324
325 init () {
326 self.saleOffers <- {}
327 }
328 }
329
330 access(all) fun createEmptyCollection(): @Collection {
331 return <- create Collection()
332 }
333
334 init () {
335 self.CollectionStoragePath = /storage/starlyCardMarketCollection
336 self.CollectionPublicPath = /public/starlyCardMarketCollection
337 }
338}
339