Smart Contract
DoodlesWearablesProxy
A.e81193c424cfd3fb.DoodlesWearablesProxy
1import NonFungibleToken from 0x1d7e57aa55817448
2import MetadataViews from 0x1d7e57aa55817448
3import Doodles from 0xe81193c424cfd3fb
4import Wearables from 0xe81193c424cfd3fb
5
6/// A proxy contract that allows users to deposit Doodles and Wearables NFTs,
7/// assigning them to a specific address on the Base chain. An admin can then burn these NFTs.
8access(all) contract DoodlesWearablesProxy {
9 /**//////////////////////////////////////////////////////////////
10 // EVENTS //
11 //////////////////////////////////////////////////////////////**/
12
13 /// Event emitted when a Doodles NFT is deposited
14 access(all) event DoodlesDeposited(id: UInt64, baseAddress: String, flowAddress: Address)
15
16 /// Event emitted when a Wearables NFT is deposited
17 access(all) event WearablesDeposited(id: UInt64, baseAddress: String, flowAddress: Address)
18
19 /// Event emitted when a Doodles NFT is burned
20 access(all) event DoodlesBurned(id: UInt64, baseAddress: String, flowAddress: Address)
21
22 /// Event emitted when a Wearables NFT is burned
23 access(all) event WearablesBurned(id: UInt64, baseAddress: String, flowAddress: Address)
24
25 /**//////////////////////////////////////////////////////////////
26 // STRUCTS //
27 //////////////////////////////////////////////////////////////**/
28
29 /// A struct representing an address on the Base chain and burn status
30 access(all) struct BridgeInfo {
31 // The destination address on Base
32 access(all) let baseAddress: String
33 // The source address on Flow
34 access(all) let flowAddress: Address
35 // Whether the NFT has been burned
36 access(self) var burned: Bool
37
38 init(baseAddress: String, flowAddress: Address) {
39 // Make sure the address is a valid Ethereum address
40 assert(baseAddress.length == 42, message: "Invalid address")
41 self.baseAddress = baseAddress
42 self.flowAddress = flowAddress
43 self.burned = false
44 }
45
46 // Set the NFT as burned
47 access(contract) fun setBurned() {
48 self.burned = true
49 }
50
51 // Check if the NFT has been burned
52 access(all) fun isBurned(): Bool {
53 return self.burned
54 }
55 }
56
57 /**//////////////////////////////////////////////////////////////
58 // PROPERTIES //
59 //////////////////////////////////////////////////////////////**/
60
61 /// A mapping of Doodles NFTs to their BridgeInfo
62 access(contract) var doodles: {UInt64: BridgeInfo}
63
64 /// A mapping of Wearables NFTs to their BridgeInfo
65 access(contract) var wearables: {UInt64: BridgeInfo}
66
67 /// The contract's recipient capability that can receive Doodles NFTs
68 access(contract) var doodlesRecipient: Capability<&Doodles.Collection>
69
70 /// The contract's recipient capability that can receive Wearables NFTs
71 access(contract) var wearablesRecipient: Capability<&Wearables.Collection>
72
73 /// The storage path for the Admin resource
74 access(all) let AdminStoragePath: StoragePath
75
76 /**//////////////////////////////////////////////////////////////
77 // METHODS //
78 //////////////////////////////////////////////////////////////**/
79
80 /// Deposit an array of Doodles NFTs to the proxy contract
81 access(all) fun depositDoodlesNFT(doodlesProvider: Capability<auth(NonFungibleToken.Withdraw) &Doodles.Collection>, doodlesIDs: [UInt64], baseAddress: String) {
82 // Borrow the Doodles collection
83 let doodlesCollection = doodlesProvider.borrow() ?? panic("Missing Doodles collection")
84 // Iterate over the Doodles NFTs
85 for id in doodlesIDs {
86 // Withdraw the Doodles NFT from the collection
87 let doodle <- doodlesCollection.withdraw(withdrawID: id)
88 // Store bridge info
89 let bridgeInfo = BridgeInfo(baseAddress: baseAddress, flowAddress: doodlesProvider.address)
90 self.doodles[id] = bridgeInfo
91 // Deposit the Doodles NFT to the proxy contract
92 self.doodlesRecipient.borrow()!.deposit(token: <-doodle)
93 // Emit an event
94 emit DoodlesDeposited(id: id, baseAddress: baseAddress, flowAddress: doodlesProvider.address)
95 }
96 }
97
98 /// Deposit an array of Wearables NFTs to the proxy contract
99 access(all) fun depositWearablesNFT(wearablesProvider: Capability<auth(NonFungibleToken.Withdraw) &Wearables.Collection>, wearablesIDs: [UInt64], baseAddress: String) {
100 // Borrow the Wearables collection
101 let wearablesCollection = wearablesProvider.borrow() ?? panic("Missing Wearables collection")
102 // Iterate over the Wearables NFTs
103 for id in wearablesIDs {
104 // Withdraw the Wearables NFT from the collection
105 let wearable <- wearablesCollection.withdraw(withdrawID: id)
106 // Store bridge info
107 let bridgeInfo = BridgeInfo(baseAddress: baseAddress, flowAddress: wearablesProvider.address)
108 self.wearables[id] = bridgeInfo
109 // Deposit the Wearables NFT to the proxy contract
110 self.wearablesRecipient.borrow()!.deposit(token: <-wearable)
111 // Emit an event
112 emit WearablesDeposited(id: id, baseAddress: baseAddress, flowAddress: wearablesProvider.address)
113 }
114 }
115
116 /// Deposit a Doodle NFT to the proxy contract
117 access(all) fun depositDoodle(doodle: @Doodles.NFT, baseAddress: String, flowAddress: Address) {
118 let id = doodle.id
119 // Store bridge info
120 let bridgeInfo = BridgeInfo(baseAddress: baseAddress, flowAddress: flowAddress)
121 self.doodles[id] = bridgeInfo
122 // Deposit the Doodle NFT to the proxy contract
123 self.doodlesRecipient.borrow()!.deposit(token: <-doodle)
124 // Emit an event
125 emit DoodlesDeposited(id: id, baseAddress: baseAddress, flowAddress: flowAddress)
126 }
127
128 /// Deposit a Wearable NFT to the proxy contract
129 access(all) fun depositWearable(wearable: @Wearables.NFT, baseAddress: String, flowAddress: Address) {
130 let id = wearable.id
131 // Store bridge info
132 let bridgeInfo = BridgeInfo(baseAddress: baseAddress, flowAddress: flowAddress)
133 self.wearables[id] = bridgeInfo
134 // Deposit the Wearable NFT to the proxy contract
135 self.wearablesRecipient.borrow()!.deposit(token: <-wearable)
136 // Emit an event
137 emit WearablesDeposited(id: id, baseAddress: baseAddress, flowAddress: flowAddress)
138 }
139
140 /**//////////////////////////////////////////////////////////////
141 // ADMIN //
142 //////////////////////////////////////////////////////////////**/
143
144 /// An entitlement that allows the admin to burn NFTs
145 access(all) entitlement Burner
146
147 /// The admin resource holds the admin methods, allowing the admin to burn NFTs
148 access(all) resource Admin {
149
150 /// Burn an array of Doodles NFTs
151 access(Burner) fun burnDoodlesNFT(ids: [UInt64]) {
152 // Borrow the Doodles collection provider
153 let doodlesCollectionProvider = DoodlesWearablesProxy.account.storage.borrow<auth(NonFungibleToken.Withdraw) &Doodles.Collection>(from: Doodles.CollectionStoragePath)
154 ?? panic("Could not borrow a reference to the collection")
155 // Iterate over the Doodles NFTs
156 for id in ids {
157 // Get the BridgeInfo for the Doodles NFT
158 let bridgeInfo = DoodlesWearablesProxy.doodles[id] ?? panic("Doodles NFT not found")
159 // Withdraw the Doodles NFT from the proxy contract
160 let doodle <- doodlesCollectionProvider.withdraw(withdrawID: id)
161 // Destroy the Doodles NFT
162 destroy <-doodle
163 // Ensure the Doodles NFT has not already been burned
164 if !bridgeInfo.isBurned() {
165 // Set the Doodles NFT as burned
166 bridgeInfo.setBurned()
167 // Emit an event
168 emit DoodlesBurned(id: id, baseAddress: bridgeInfo.baseAddress, flowAddress: bridgeInfo.flowAddress)
169 }
170 }
171 }
172
173 /// Burn an array of Wearables NFTs
174 access(Burner) fun burnWearablesNFT(ids: [UInt64]) {
175 // Borrow the Wearables collection provider
176 let wearablesCollectionProvider = DoodlesWearablesProxy.account.storage.borrow<auth(NonFungibleToken.Withdraw) &Wearables.Collection>(from: Wearables.CollectionStoragePath)
177 ?? panic("Could not borrow a reference to the collection")
178 // Iterate over the Wearables NFTs
179 for id in ids {
180 // Get the BridgeInfo for the Wearables NFT
181 let bridgeInfo = DoodlesWearablesProxy.wearables[id] ?? panic("Wearables NFT not found")
182 // Withdraw the Wearables NFT from the proxy contract
183 let wearable <- wearablesCollectionProvider.withdraw(withdrawID: id)
184 // Destroy the Wearables NFT
185 destroy <-wearable
186 // Ensure the Wearables NFT has not already been burned
187 if !bridgeInfo.isBurned() {
188 // Set the Wearables NFT as burned
189 bridgeInfo.setBurned()
190 // Emit an event
191 emit WearablesBurned(id: id, baseAddress: bridgeInfo.baseAddress, flowAddress: bridgeInfo.flowAddress)
192 }
193 }
194 }
195 }
196
197 /**//////////////////////////////////////////////////////////////
198 // GETTERS //
199 //////////////////////////////////////////////////////////////**/
200
201 /// Get the BridgeInfo for a Doodles NFT
202 access(all) fun getDoodlesBridgeInfo(id: UInt64): BridgeInfo {
203 return self.doodles[id] ?? panic("Doodles NFT not found")
204 }
205
206 /// Get the BridgeInfo for a Wearables NFT
207 access(all) fun getWearablesBridgeInfo(id: UInt64): BridgeInfo {
208 return self.wearables[id] ?? panic("Wearables NFT not found")
209 }
210
211 /// Get all doodles bridge info
212 access(all) fun getAllDoodlesBridgeInfo(): {UInt64: BridgeInfo} {
213 return self.doodles
214 }
215
216 /// Get all wearables bridge info
217 access(all) fun getAllWearablesBridgeInfo(): {UInt64: BridgeInfo} {
218 return self.wearables
219 }
220
221 /**//////////////////////////////////////////////////////////////
222 // INITIALIZER //
223 //////////////////////////////////////////////////////////////**/
224
225 init() {
226 // Set the storage path for the Admin resource
227 self.AdminStoragePath = /storage/DoodlesWearablesProxyAdmin
228
229 // Init the Doodles and Wearables bridge info mappings
230 self.doodles = {}
231 self.wearables = {}
232
233 // Create the Admin resource
234 let admin <- create Admin()
235
236 // Save the Admin resource to storage
237 self.account.storage.save(<-admin, to: self.AdminStoragePath)
238 self.account.capabilities.storage.issue<auth(Burner) &Admin>(self.AdminStoragePath)
239
240 // Init the Doodles collection
241 let wearableRef= self.account.storage.borrow<&Wearables.Collection>(from: Wearables.CollectionStoragePath)
242 if wearableRef == nil {
243 self.account.storage.save<@{NonFungibleToken.Collection}>( <- Wearables.createEmptyCollection(nftType: Type<@Wearables.NFT>()), to: Wearables.CollectionStoragePath)
244 let cap = self.account.capabilities.storage.issue<&Wearables.Collection>(Wearables.CollectionStoragePath)
245 self.account.capabilities.publish(cap, at: Wearables.CollectionPublicPath)
246 }
247
248 // Init the Wearables collectionß
249 let doodlesRef= self.account.storage.borrow<&Doodles.Collection>(from: Doodles.CollectionStoragePath)
250 if doodlesRef == nil {
251 self.account.storage.save<@{NonFungibleToken.Collection}>( <- Doodles.createEmptyCollection(nftType: Type<@Wearables.NFT>()), to: Doodles.CollectionStoragePath)
252 let cap = self.account.capabilities.storage.issue<&Doodles.Collection>(Doodles.CollectionStoragePath)
253 self.account.capabilities.publish(cap, at: Doodles.CollectionPublicPath)
254 }
255
256 // Get the recipient capability for Doodles NFTs
257 self.doodlesRecipient = self.account.capabilities.get<&Doodles.Collection>(Doodles.CollectionPublicPath)
258
259 // Get the recipient capability for Wearables NFTs
260 self.wearablesRecipient = self.account.capabilities.get<&Wearables.Collection>(Wearables.CollectionPublicPath)
261 }
262}
263