Smart Contract

FRC20MarketManager

A.d2abb5dbf5e08666.FRC20MarketManager

Valid From

86,128,897

Deployed

3d ago
Feb 24, 2026, 11:54:28 PM UTC

Dependents

6 imports
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