Smart Contract

BaitCoin

A.44100f14f70e3f78.BaitCoin

Valid From

118,614,871

Deployed

6d ago
Feb 22, 2026, 03:29:05 PM UTC

Dependents

2 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import MetadataViews from 0x1d7e57aa55817448
3import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4import FUSD from 0x3c5959b568896393
5
6access(all) contract BaitCoin: FungibleToken {
7
8    /// The event that is emitted when new tokens are minted
9    access(all) event TokensMinted(amount: UFix64, type: String)
10
11    /// The event that is emitted when FUSD is swapped for BaitCoin
12    access(all) event FUSDSwappedForBaitCoin(fusdAmount: UFix64, baitCoinAmount: UFix64, account: Address)
13
14    /// The event that is emitted when BaitCoin is swapped for FUSD
15    access(all) event BaitCoinSwappedForFUSD(baitCoinAmount: UFix64, fusdAmount: UFix64, account: Address)
16
17    /// Total supply of BaitCoins in existence
18    access(all) var totalSupply: UFix64
19
20    /// Storage and Public Paths
21    access(all) let VaultStoragePath: StoragePath
22    access(all) let VaultPublicPath: PublicPath
23    access(all) let ReceiverPublicPath: PublicPath
24    access(all) let MinterStoragePath: StoragePath
25
26    access(all) view fun getContractViews(resourceType: Type?): [Type] {
27        return [
28            Type<FungibleTokenMetadataViews.FTView>(),
29            Type<FungibleTokenMetadataViews.FTDisplay>(),
30            Type<FungibleTokenMetadataViews.FTVaultData>(),
31            Type<FungibleTokenMetadataViews.TotalSupply>()
32        ]
33    }
34
35    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
36        switch viewType {
37            case Type<FungibleTokenMetadataViews.FTView>():
38                return FungibleTokenMetadataViews.FTView(
39                    ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
40                    ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
41                )
42            case Type<FungibleTokenMetadataViews.FTDisplay>():
43                let media = MetadataViews.Media(
44                        file: MetadataViews.HTTPFile(
45                        // Change this to your own SVG image
46                        url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"
47                    ),
48                    mediaType: "image/svg+xml"
49                )
50                let medias = MetadataViews.Medias([media])
51                return FungibleTokenMetadataViews.FTDisplay(
52                    // Change these to represent your own token
53                    name: "BaitCoin",
54                    symbol: "BAIT",
55                    description: "BaitCoin is the primary currency for the DerbyFish ecosystem.",
56                    externalURL: MetadataViews.ExternalURL("https://derbyfish.example.com"),
57                    logos: medias,
58                    socials: {
59                        "twitter": MetadataViews.ExternalURL("https://twitter.com/derbyfish")
60                    }
61                )
62            case Type<FungibleTokenMetadataViews.FTVaultData>():
63                return FungibleTokenMetadataViews.FTVaultData(
64                    storagePath: self.VaultStoragePath,
65                    receiverPath: self.VaultPublicPath,
66                    metadataPath: self.VaultPublicPath,
67                    receiverLinkedType: Type<&BaitCoin.Vault>(),
68                    metadataLinkedType: Type<&BaitCoin.Vault>(),
69                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
70                        return <-BaitCoin.createEmptyVault(vaultType: Type<@BaitCoin.Vault>())
71                    })
72                )
73            case Type<FungibleTokenMetadataViews.TotalSupply>():
74                return FungibleTokenMetadataViews.TotalSupply(
75                    totalSupply: BaitCoin.totalSupply
76                )
77        }
78        return nil
79    }
80
81    access(all) resource Vault: FungibleToken.Vault {
82
83        /// The total balance of this vault
84        access(all) var balance: UFix64
85
86        // initialize the balance at resource creation time
87        init(balance: UFix64) {
88            self.balance = balance
89        }
90
91        /// Called when a fungible token is burned via the `Burner.burn()` method
92        access(contract) fun burnCallback() {
93            if self.balance > 0.0 {
94                BaitCoin.totalSupply = BaitCoin.totalSupply - self.balance
95            }
96            self.balance = 0.0
97        }
98
99        access(all) view fun getViews(): [Type] {
100            return BaitCoin.getContractViews(resourceType: nil)
101        }
102
103        access(all) fun resolveView(_ view: Type): AnyStruct? {
104            return BaitCoin.resolveContractView(resourceType: nil, viewType: view)
105        }
106
107        access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
108            let supportedTypes: {Type: Bool} = {}
109            supportedTypes[self.getType()] = true
110            return supportedTypes
111        }
112
113        access(all) view fun isSupportedVaultType(type: Type): Bool {
114            return self.getSupportedVaultTypes()[type] ?? false
115        }
116
117        access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
118            return amount <= self.balance
119        }
120
121        access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @BaitCoin.Vault {
122            self.balance = self.balance - amount
123            return <-create Vault(balance: amount)
124        }
125
126        access(all) fun deposit(from: @{FungibleToken.Vault}) {
127            let vault <- from as! @BaitCoin.Vault
128            self.balance = self.balance + vault.balance
129            vault.balance = 0.0
130            destroy vault
131        }
132
133        access(all) fun createEmptyVault(): @BaitCoin.Vault {
134            return <-create Vault(balance: 0.0)
135        }
136    }
137
138    access(all) resource Minter {
139        /// Internal minting function - only accessible within this resource
140        access(self) fun mintTokensInternal(amount: UFix64): @BaitCoin.Vault {
141            BaitCoin.totalSupply = BaitCoin.totalSupply + amount
142            let vault <-create Vault(balance: amount)
143            emit TokensMinted(amount: amount, type: vault.getType().identifier)
144            return <-vault
145        }
146    }
147
148    access(all) fun createEmptyVault(vaultType: Type): @BaitCoin.Vault {
149        return <- create Vault(balance: 0.0)
150    }
151
152    /// Get the FUSD balance stored in the contract
153    access(all) fun getContractFUSDBalance(): UFix64 {
154        let fusdVault = self.account.storage.borrow<&FUSD.Vault>(from: /storage/BaitCoinFUSDVault)
155            ?? panic("Could not borrow contract FUSD vault")
156        return fusdVault.balance
157    }
158
159    /// Public swap function: FUSD for BaitCoin
160    access(all) fun swapFUSDForBaitCoin(from: @FUSD.Vault, recipient: Address) {
161        let fusdAmount = from.balance
162        
163        // Get reference to contract's FUSD vault and deposit received FUSD
164        let contractFUSDVault = self.account.storage.borrow<&FUSD.Vault>(from: /storage/BaitCoinFUSDVault)
165            ?? panic("Could not borrow reference to contract's FUSD vault")
166        contractFUSDVault.deposit(from: <-from)
167
168        // Mint equivalent BaitCoin internally
169        self.totalSupply = self.totalSupply + fusdAmount
170        let newBaitCoin <- create Vault(balance: fusdAmount)
171        emit TokensMinted(amount: fusdAmount, type: newBaitCoin.getType().identifier)
172        
173        // Get recipient's BaitCoin receiver
174        let recipientReceiver = getAccount(recipient).capabilities.borrow<&{FungibleToken.Receiver}>(self.VaultPublicPath)
175            ?? panic("Could not borrow receiver capability for recipient")
176        
177        // Deposit BaitCoin to recipient
178        recipientReceiver.deposit(from: <-newBaitCoin)
179        
180        emit FUSDSwappedForBaitCoin(fusdAmount: fusdAmount, baitCoinAmount: fusdAmount, account: recipient)
181    }
182
183    /// Public swap function: BaitCoin for FUSD
184    access(all) fun swapBaitCoinForFUSD(from: @BaitCoin.Vault, recipient: Address) {
185        let baitCoinAmount = from.balance
186        
187        // Burn the received BaitCoin
188        self.totalSupply = self.totalSupply - baitCoinAmount
189        destroy from
190        
191        // Get reference to contract's FUSD vault and withdraw equivalent FUSD
192        let contractFUSDVault = self.account.storage.borrow<auth(FungibleToken.Withdraw) &FUSD.Vault>(from: /storage/BaitCoinFUSDVault)
193            ?? panic("Could not borrow reference to contract's FUSD vault")
194        
195        let fusdToSend <- contractFUSDVault.withdraw(amount: baitCoinAmount)
196        
197        // Get recipient's FUSD receiver
198        let recipientReceiver = getAccount(recipient).capabilities.borrow<&{FungibleToken.Receiver}>(/public/fusdReceiver)
199            ?? panic("Could not borrow FUSD receiver capability for recipient")
200        
201        // Deposit FUSD to recipient
202        recipientReceiver.deposit(from: <-fusdToSend)
203        
204        emit BaitCoinSwappedForFUSD(baitCoinAmount: baitCoinAmount, fusdAmount: baitCoinAmount, account: recipient)
205    }
206
207    init() {
208        self.totalSupply = 0.0
209
210        self.VaultStoragePath = /storage/BaitCoinVault
211        self.VaultPublicPath = /public/BaitCoinVault
212        self.ReceiverPublicPath = /public/BaitCoinReceiver
213        self.MinterStoragePath = /storage/BaitCoinMinter
214
215        // Create the Vault with the total supply of tokens and save it in storage
216        //
217        let vault <- create Vault(balance: self.totalSupply)
218        emit TokensMinted(amount: vault.balance, type: vault.getType().identifier)
219        self.account.storage.save(<-vault, to: self.VaultStoragePath)
220
221        // Create a public capability to the stored Vault that exposes
222        // the `deposit` method and getAcceptedTypes method through the `Receiver` interface
223        // and the `balance` method through the `Balance` interface
224        //
225        let BaitCoinCap = self.account.capabilities.storage.issue<&BaitCoin.Vault>(self.VaultStoragePath)
226        self.account.capabilities.publish(BaitCoinCap, at: self.VaultPublicPath)
227
228        // Create a FUSD vault for the contract to store received FUSD
229        let fusdVault <- FUSD.createEmptyVault(vaultType: Type<@FUSD.Vault>())
230        self.account.storage.save(<-fusdVault, to: /storage/BaitCoinFUSDVault)
231
232        let minter <- create Minter()
233        self.account.storage.save(<-minter, to: self.MinterStoragePath)
234    }
235}
236