Smart Contract

PierLPToken

A.609e10301860b683.PierLPToken

Deployed

1w ago
Mar 06, 2026, 04:41:30 PM UTC

Dependents

0 imports
1import MultiFungibleToken from 0xa378eeb799df8387
2
3/**
4
5PierLPToken is the LP token contract for Metapier Wharf v1.
6For each liquidity pool, we have one dedicated LP token, with
7the `tokenId` of the LP token equals to the `poolId` of the pool.
8
9@author Metapier Foundation Ltd.
10
11 */
12pub contract PierLPToken: MultiFungibleToken {
13
14    // Event that is emitted when the contract is created
15    pub event ContractInitialized()
16
17    // Event that is emitted when tokens are minted
18    pub event TokensMinted(tokenId: UInt64, amount: UFix64)
19
20    // Event that is emitted when tokens are burned
21    pub event TokensBurned(tokenId: UInt64, amount: UFix64)
22
23    // Event that is emitted when tokens are withdrawn from a Vault of some token ID
24    pub event TokensWithdrawn(tokenId: UInt64, amount: UFix64, from: Address?)
25
26    // Event that is emitted when tokens are deposited into a Vault of some token ID
27    pub event TokensDeposited(tokenId: UInt64, amount: UFix64, to: Address?)
28
29    // Event that is emitted when a new TokenMaster resource is created
30    pub event TokenMasterCreated(tokenId: UInt64)
31
32    // The common storage path for storing a PierLPToken.Collection
33    pub let CollectionStoragePath: StoragePath
34
35    // The common public path for linking to the PierLPToken.Collection{MultiFungibleToken.CollectionPublic}
36    pub let CollectionPublicPath: PublicPath
37
38    // A mapping from token ID to the corresponding total supply
39    access(contract) let totalSupply: {UInt64: UFix64}
40
41    // PierLPToken Vault
42    pub resource Vault: MultiFungibleToken.Receiver, MultiFungibleToken.View {
43
44        // The total balance of the Vault
45        pub var balance: UFix64
46
47        // The liquidity pool id that the Vault corresponds to
48        pub let tokenId: UInt64
49
50        // Vault initializer
51        //
52        // @param tokenId The liquidity pool id that the Vault corresponds to
53        // @param balance The initial balance of the Vault
54        init(tokenId: UInt64, balance: UFix64) {
55            self.tokenId = tokenId
56            self.balance = balance
57        }
58
59        // Subtracts the amount of tokens from the owner's Vault
60        // and returns a Vault with the subtracted balance.
61        //
62        // @param amount The amount of tokens to withdraw
63        // @return A new Vault (of the same token id) that contains the requested amount of tokens
64        pub fun withdraw(amount: UFix64): @PierLPToken.Vault {
65            self.balance = self.balance - amount
66            emit TokensWithdrawn(tokenId: self.tokenId, amount: amount, from: self.owner?.address)
67            return <- create Vault(tokenId: self.tokenId, balance: amount)
68        }
69
70        // Takes a Vault of the same token id and adds its
71        // balance to the balance of this Vault
72        //
73        // @param from A Vault that is ready to have its balance added to this Vault
74        pub fun deposit(from: @MultiFungibleToken.Vault) {
75            let vault <- from as! @Vault
76            self.balance = self.balance + vault.balance
77            emit TokensDeposited(tokenId: self.tokenId, amount: vault.balance, to: self.owner?.address)
78            // no need to reset the vault's balance because destroy() do not change the total supply
79            destroy vault
80        }
81
82        destroy() {
83            // Destroys a non-empty Vault will not affect the total supply but lock the balance forever instead
84        }
85    }
86
87    // A collection to help with storing and organizing multiple LP tokens.
88    pub resource Collection: 
89        MultiFungibleToken.Provider, 
90        MultiFungibleToken.Receiver, 
91        MultiFungibleToken.CollectionPublic 
92    {
93        // A mapping from token id (pool id) to LP token Vault
94        pub var vaults: @{UInt64: PierLPToken.Vault}
95
96        // For a given token id, subtracts the amount of tokens from the corresponding Vault
97        // in the collection and returns a Vault with the token id and the subtracted balance.
98        //
99        // Note: A new empty Vault will be created in the collection if the collection doesn't
100        //  contain the requested LP token.
101        //
102        // @param tokenId The token id (pool id) of the Vault from which to withdraw
103        // @param amount The amount of tokens to withdraw
104        // @return A new Vault (of the same token id) that contains the requested amount of tokens
105        pub fun withdraw(tokenId: UInt64, amount: UFix64): @PierLPToken.Vault {
106            if !self.vaults.containsKey(tokenId) {
107                self.vaults[tokenId] <-! PierLPToken.createEmptyVault(tokenId: tokenId)
108            }
109
110            let vault = (&self.vaults[tokenId] as &PierLPToken.Vault?)!
111            return <- vault.withdraw(amount: amount)
112        }
113
114        // Adds the balance of the deposit Vault to the Vault with the same token id
115        // in the collection.
116        //
117        // Note: A new Vault will be created in the collection if the collection doesn't
118        //  contain the given LP token.
119        //
120        // @param from The LP token Vault to deposit into the collection
121        pub fun deposit(from: @MultiFungibleToken.Vault) {
122            if from.balance == 0.0 {
123                // ignore zero-balance vaults to prevent spamming
124                destroy from
125                return
126            }
127
128            let tokenId = from.tokenId
129            if !self.vaults.containsKey(tokenId) {
130                self.vaults[tokenId] <-! PierLPToken.createEmptyVault(tokenId: tokenId)
131            }
132
133            let vault = (&self.vaults[tokenId] as &PierLPToken.Vault?)!
134            vault.deposit(from: <- from)
135        }
136
137        // Gets the token ids of all vaults stored in the collection, including the
138        // empty ones.
139        //
140        // @return An array of the token ids of all vaults stored in the collection.
141        pub fun getTokenIds(): [UInt64] {
142            return self.vaults.keys
143        }
144
145        // Checks if the internal `vaults` contains the requested LP token.
146        // 
147        // @return `true` iff the internal `vaults` contains a Vault of the
148        //  requested token id ()
149        pub fun hasToken(tokenId: UInt64): Bool {
150            return self.vaults.containsKey(tokenId)
151        }
152 
153        // Returns a restricted Vault of the requested token id for public access,
154        // or throws an exception if it doesn't have a Vault of the requested token
155        // id
156        //
157        // @param tokenId The token id (pool id) of the Vault to query
158        // @return A Vault reference of the requested token id, which exposes only
159        //  the Receiver and View
160        pub fun getPublicVault(tokenId: UInt64):
161            &PierLPToken.Vault{MultiFungibleToken.Receiver, MultiFungibleToken.View}
162        {
163            return (&self.vaults[tokenId] as &PierLPToken.Vault{MultiFungibleToken.Receiver, MultiFungibleToken.View}?)!
164        }
165
166        // Initializes the Collection
167        init() {
168            self.vaults <- {}
169        }
170
171        // Destroys the Collection and permanently locks all the stored LP tokens (if there are any) 
172        destroy() {
173            destroy self.vaults
174        }
175    }
176
177    // TokenMaster is a resource to manage the LP token supply for
178    // one specific liquidity pool
179    pub resource TokenMaster {
180
181        // The id of the LP token this TokenMaster can manage
182        pub let tokenId: UInt64
183
184        // Mints the given amount of LP tokens and returns a Vault
185        // that stores the minted tokens.
186        //
187        // @param amount The amount of tokens to mint and return
188        // @return A Vault that contains the requested amount of LP
189        //  tokens (of the predefined token id)
190        pub fun mintTokens(amount: UFix64): @PierLPToken.Vault {
191            pre {
192                amount > 0.0: "Metapier PierLPToken: Amount to mint must be greater than zero"
193            }
194
195            PierLPToken.totalSupply[self.tokenId] = PierLPToken.totalSupply[self.tokenId]! + amount
196            emit TokensMinted(tokenId: self.tokenId, amount: amount)
197
198            return <- create Vault(tokenId: self.tokenId, balance: amount)
199        }
200
201        // Burns the given LP token Vault by subtracting its balance
202        // from the total supply.
203        //
204        // @param vault The LP token Vault to burn (must have the 
205        //  expected token id)
206        pub fun burnTokens(vault: @PierLPToken.Vault) {
207            pre {
208                self.tokenId == vault.tokenId: "Metapier PierLPToken: Cannot burn other LP tokens"
209            }
210
211            PierLPToken.totalSupply[self.tokenId] = PierLPToken.totalSupply[self.tokenId]! - vault.balance
212            emit TokensBurned(tokenId: vault.tokenId, amount: vault.balance)
213
214            destroy vault
215        }
216
217        // Initializes the TokenMaster for a specific LP token
218        //
219        // @param tokenId The id of the LP token this TokenMaster can manage
220        init(tokenId: UInt64) {
221            self.tokenId = tokenId
222        }
223    }
224
225    // Admin is a resource for initializing new LP tokens
226    pub resource Admin {
227
228        // Initializes a new LP token and returns a TokenMaster for
229        // managing its total supply. Will throw an error if the token
230        // already exists.
231        // 
232        // @param tokenId The id of the new LP token
233        // @return A TokenMaster for the new LP token (implied by 
234        //  token id)
235        pub fun initNewLPToken(tokenId: UInt64): @TokenMaster {
236            pre {
237                !PierLPToken.totalSupply.containsKey(tokenId): "Metapier PierLPToken: LP Token already exists"
238            }
239            // initializes the total supply entry
240            PierLPToken.totalSupply[tokenId] = 0.0
241            emit TokenMasterCreated(tokenId: tokenId)
242            return <- create TokenMaster(tokenId: tokenId)
243        }
244    }
245
246    // Creates an empty collection
247    //
248    // @return A new empty Collection
249    pub fun createEmptyCollection(): @PierLPToken.Collection {
250        return <- create Collection()
251    }
252
253    // Creates a new Vault of the requested token id that has a zero balance,
254    // or throws an error if the requested token id has not yet been initialized
255    //
256    // @param tokenId The token id (pool id) the new Vault is associate with
257    // @return A new empty Vault of the requested token id
258    pub fun createEmptyVault(tokenId: UInt64): @PierLPToken.Vault {
259        return <- create Vault(tokenId: tokenId, balance: 0.0)
260    }
261
262    // Gets the total supply of the requested token id, or nil if the token
263    // id has not yet been initialized
264    //
265    // @param tokenId The token id (pool id) to query
266    // @return The total supply of the requested token id, or nil if the
267    //  token doesn't exist
268    pub fun getTotalSupply(tokenId: UInt64): UFix64? {
269        return self.totalSupply[tokenId]
270    }
271
272    // Initializes the contract
273    init() {
274        self.CollectionStoragePath = /storage/metapierLPTokenCollection
275        self.CollectionPublicPath = /public/metapierLPTokenCollection
276
277        self.totalSupply = {}
278
279        let admin <- create Admin()
280        self.account.save(<- admin, to: /storage/metapierLPTokenAdmin)
281
282        emit ContractInitialized()
283    }
284}
285