Smart Contract
FRC20MarketManager
A.d2abb5dbf5e08666.FRC20MarketManager
1/**
2> Author: Fixes Lab <https://github.com/fixes-world/>
3
4# FRC20MarketManager
5
6THe resource manager for the FRC20Marketplace contract.
7
8*/
9import Fixes from 0xd2abb5dbf5e08666
10import FixesInscriptionFactory from 0xd2abb5dbf5e08666
11import FixesHeartbeat from 0xd2abb5dbf5e08666
12import FRC20FTShared from 0xd2abb5dbf5e08666
13import FRC20Indexer from 0xd2abb5dbf5e08666
14import FRC20AccountsPool from 0xd2abb5dbf5e08666
15import FRC20Marketplace from 0xd2abb5dbf5e08666
16import FRC20TradingRecord from 0xd2abb5dbf5e08666
17
18access(all) contract FRC20MarketManager {
19
20 access(all) entitlement Manage
21
22 /* --- Events --- */
23
24 /// Event emitted when the contract is initialized
25 access(all) event ContractInitialized()
26
27 /// Event emitted when a new market is enabled
28 access(all) event NewMarketEnabled(tick: String, address: Address, by: Address)
29
30 /* --- Variable, Enums and Structs --- */
31
32 access(all)
33 let FRC20MarketManagerStoragePath: StoragePath
34
35 /* --- Interfaces & Resources --- */
36
37 /// The resource manager for the FRC20MarketManager
38 ///
39 access(all)
40 resource Manager: FRC20Marketplace.MarketManager {
41 /// Update the admin whitelist
42 ///
43 access(Manage)
44 fun updateAdminWhitelist(
45 tick: String,
46 address: Address,
47 isWhitelisted: Bool
48 ) {
49 let market = FRC20MarketManager.borrowMarket(tick)
50 ?? panic("The market is not enabled")
51 market.updateAdminWhitelist(
52 mananger: (&self as &{FRC20Marketplace.MarketManager}),
53 address: address,
54 isWhitelisted: isWhitelisted
55 )
56 }
57
58 /// Update the marketplace properties
59 ///
60 access(Manage)
61 fun updateMarketplaceProperties(
62 tick: String,
63 _ props: {FRC20FTShared.ConfigType: String}
64 ) {
65 let market = FRC20MarketManager.borrowMarket(tick)
66 ?? panic("The market is not enabled")
67 market.updateMarketplaceProperties(
68 mananger: (&self as &{FRC20Marketplace.MarketManager}),
69 props
70 )
71 }
72 }
73
74 /* --- Contract access methods --- */
75
76 access(contract)
77 fun _ensureMarketResourcesAvailable(tick: String) {
78 let acctsPool = FRC20AccountsPool.borrowAccountsPool()
79
80 // try to borrow the account to check if it was created
81 let childAcctRef = acctsPool.borrowChildAccount(type: FRC20AccountsPool.ChildAccountType.Market, tick)
82 ?? panic("The market account was not created")
83
84 // The market should have the following resources in the account:
85 // - FRC20Marketplace.Market: Market resource
86 // - FRC20FTShared.SharedStore: Market configuration
87 // - FRC20FTShared.Hooks: Hooks for the transactions done in the market
88 // - FRC20TradingRecord.TradingRecordingHook: Hook for the trading records
89 // - FRC20TradingRecord.TradingRecords: Trading records resource
90
91 if let market = childAcctRef.storage.borrow<&FRC20Marketplace.Market>(from: FRC20Marketplace.FRC20MarketStoragePath) {
92 assert(
93 market.tick == tick,
94 message: "The market tick is not the same as the expected one"
95 )
96 } else {
97 // create the market and save it in the account
98 let market <- FRC20Marketplace.createMarket(tick)
99 // save the market in the account
100 childAcctRef.storage.save(<- market, to: FRC20Marketplace.FRC20MarketStoragePath)
101
102 // link the market to the public path
103 childAcctRef.capabilities.unpublish(FRC20Marketplace.FRC20MarketPublicPath)
104 childAcctRef.capabilities.publish(
105 childAcctRef.capabilities.storage.issue<&FRC20Marketplace.Market>(FRC20Marketplace.FRC20MarketStoragePath),
106 at: FRC20Marketplace.FRC20MarketPublicPath
107 )
108 }
109
110 // create the shared store and save it in the account
111 if childAcctRef.storage.borrow<&AnyResource>(from: FRC20FTShared.SharedStoreStoragePath) == nil {
112 let sharedStore <- FRC20FTShared.createSharedStore()
113 childAcctRef.storage.save(<- sharedStore, to: FRC20FTShared.SharedStoreStoragePath)
114 // link the shared store to the public path
115 childAcctRef.capabilities.unpublish(FRC20FTShared.SharedStorePublicPath)
116 childAcctRef.capabilities.publish(
117 childAcctRef.capabilities.storage.issue<&FRC20FTShared.SharedStore>(FRC20FTShared.SharedStoreStoragePath),
118 at: FRC20FTShared.SharedStorePublicPath
119 )
120 }
121
122 // create the hooks and save it in the account
123 if childAcctRef.storage.borrow<&AnyResource>(from: FRC20FTShared.TransactionHookStoragePath) == nil {
124 let hooks <- FRC20FTShared.createHooks()
125 childAcctRef.storage.save(<- hooks, to: FRC20FTShared.TransactionHookStoragePath)
126 }
127 // link the hooks to the public path
128 if childAcctRef
129 .capabilities.get<&FRC20FTShared.Hooks>(FRC20FTShared.TransactionHookPublicPath)
130 .borrow() == nil {
131 // link the hooks to the public path
132 childAcctRef.capabilities.unpublish(FRC20FTShared.TransactionHookPublicPath)
133 childAcctRef.capabilities.publish(
134 childAcctRef.capabilities.storage.issue<&FRC20FTShared.Hooks>(FRC20FTShared.TransactionHookStoragePath),
135 at: FRC20FTShared.TransactionHookPublicPath
136 )
137 }
138
139 // borrow the hooks reference
140 let hooksRef = childAcctRef.storage.borrow<auth(FRC20FTShared.Manage) &FRC20FTShared.Hooks>(from: FRC20FTShared.TransactionHookStoragePath)
141 ?? panic("The hooks were not created")
142
143 // ensure trading records are available
144 if childAcctRef.storage.borrow<&AnyResource>(from: FRC20TradingRecord.TradingRecordsStoragePath) == nil {
145 let tradingRecords <- FRC20TradingRecord.createTradingRecords(tick)
146 childAcctRef.storage.save(<- tradingRecords, to: FRC20TradingRecord.TradingRecordsStoragePath)
147 // link the trading records to the public path
148 childAcctRef.capabilities.unpublish(FRC20TradingRecord.TradingRecordsPublicPath)
149 childAcctRef.capabilities.publish(
150 childAcctRef.capabilities.storage.issue<&FRC20TradingRecord.TradingRecords>(FRC20TradingRecord.TradingRecordsStoragePath),
151 at: FRC20TradingRecord.TradingRecordsPublicPath
152 )
153 }
154
155 // add the trading records to the hooks, if it is not added yet
156 // get the public capability of the trading record hook
157 let tradingRecordsCap = childAcctRef
158 .capabilities.get<&FRC20TradingRecord.TradingRecords>(
159 FRC20TradingRecord.TradingRecordsPublicPath
160 )
161 assert(tradingRecordsCap.check(), message: "The trading record hook is not valid")
162 // get the reference of the trading record hook
163 let recordsRef = tradingRecordsCap.borrow()
164 ?? panic("The trading record hook is not valid")
165 if !hooksRef.hasHook(recordsRef.getType()) {
166 hooksRef.addHook(tradingRecordsCap)
167 }
168 }
169
170 // --- Public methods ---
171
172 /// Enable a new market, and create the market account
173 /// The inscription owner should be the deployer of the token
174 ///
175 access(all)
176 fun enableAndCreateFRC20Market(
177 ins: auth(Fixes.Extractable) &Fixes.Inscription,
178 newAccount: Capability<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>,
179 ) {
180 // singletoken resources
181 let frc20Indexer = FRC20Indexer.getIndexer()
182 let acctsPool = FRC20AccountsPool.borrowAccountsPool()
183
184 // inscription data
185 let meta = FixesInscriptionFactory.parseMetadata(ins.borrowData())
186 let op = meta["op"]?.toLower() ?? panic("The token operation is not found")
187 assert(
188 op == "enable-market",
189 message: "The inscription is not for enabling a market"
190 )
191
192 let tick = meta["tick"]?.toLower() ?? panic("The token tick is not found")
193
194 /// Check if the market is already enabled
195 assert(
196 acctsPool.getFRC20MarketAddress(tick: tick) == nil,
197 message: "The market is already enabled"
198 )
199
200 // Check if the token is already registered
201 let tokenMeta = frc20Indexer.getTokenMeta(tick: tick) ?? panic("The token is not registered")
202 assert(
203 tokenMeta.deployer == ins.owner!.address,
204 message: "The token is not deployed by the inscription owner"
205 )
206
207 // execute the inscription to ensure you are the deployer of the token
208 let ret = frc20Indexer.executeByDeployer(ins: ins)
209 assert(
210 ret == true,
211 message: "The inscription execution failed"
212 )
213
214 // create the account for the market at the accounts pool
215 acctsPool.setupNewChildForMarket(
216 tick: tick,
217 newAccount
218 )
219 let address = acctsPool.getFRC20MarketAddress(tick: tick)
220 ?? panic("The market account was not created")
221
222 // ensure all market resources are available
223 self._ensureMarketResourcesAvailable(tick: tick)
224
225 // emit the event
226 emit NewMarketEnabled(
227 tick: tick,
228 address: address,
229 by: ins.owner!.address
230 )
231 }
232
233 /// Borrow the market reference
234 ///
235 access(all)
236 view fun borrowMarket(_ tick: String): &{FRC20Marketplace.MarketPublic}? {
237 let acctsPool = FRC20AccountsPool.borrowAccountsPool()
238 if let address = acctsPool.getFRC20MarketAddress(tick: tick) {
239 return FRC20Marketplace.borrowMarket(address)
240 }
241 return nil
242 }
243
244 /// Anyone can create a market manager resource.
245 ///
246 access(all)
247 fun createManager(): @Manager {
248 return <- create Manager()
249 }
250
251 init() {
252 let identifier = "FRC20MarketManager_".concat(self.account.address.toString())
253 self.FRC20MarketManagerStoragePath = StoragePath(identifier: identifier)!
254
255 emit ContractInitialized()
256 }
257}
258