Smart Contract

USDCFlow

A.f1ab99c82dee3526.USDCFlow

Valid From

88,010,798

Deployed

1w ago
Feb 19, 2026, 04:11:20 PM UTC

Dependents

9844 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import Burner from 0xf233dcee88fe0abe
5import ViewResolver from 0x1d7e57aa55817448
6import FlowEVMBridgeHandlerInterfaces from 0x1e4aa0b87d10b141
7
8/// The `USDCFlow` smart contract is integrated directly
9/// with the Flow VM bridge as the bridged version of Flow EVM USDC
10/// which is itself the bridged version of official USDC from Ethereum Mainnet.
11
12/// This is not the official Circle USDC, only a bridged version
13/// that is still backed by official USDC on the other side of the bridge
14
15access(all) contract USDCFlow: FungibleToken, ViewResolver {
16
17    /// Total supply of USDCFlows 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
25    /// The event that is emitted when new tokens are minted
26    access(all) event Minted(amount: UFix64, mintedUUID: UInt64)
27    access(all) event Burned(amount: UFix64, burntUUID: UInt64)
28
29    access(all) view fun getContractViews(resourceType: Type?): [Type] {
30        return [
31            Type<FungibleTokenMetadataViews.FTView>(),
32            Type<FungibleTokenMetadataViews.FTDisplay>(),
33            Type<FungibleTokenMetadataViews.FTVaultData>(),
34            Type<FungibleTokenMetadataViews.TotalSupply>()
35        ]
36    }
37
38    access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
39        switch viewType {
40            case Type<FungibleTokenMetadataViews.FTView>():
41                return FungibleTokenMetadataViews.FTView(
42                    ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
43                    ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
44                )
45            case Type<FungibleTokenMetadataViews.FTDisplay>():
46                let media = MetadataViews.Media(
47                        file: MetadataViews.HTTPFile(
48                        url: "https://uploads-ssl.webflow.com/5f734f4dbd95382f4fdfa0ea/66bfae00953c3d7bd09e7ac4_USDC-and-FLOW.svg"
49                    ),
50                    mediaType: "image/svg+xml"
51                )
52                let medias = MetadataViews.Medias([media])
53                return FungibleTokenMetadataViews.FTDisplay(
54                    name: "USDC.e (Flow)",
55                    symbol: "USDC.e",
56                    description: "This fungible token representation of Standard Bridged USDC is bridged from Flow EVM.",
57                    externalURL: MetadataViews.ExternalURL("https://github.com/circlefin/stablecoin-evm/blob/master/doc/bridged_USDC_standard.md"),
58                    logos: medias,
59                    socials: {},
60                )
61            case Type<FungibleTokenMetadataViews.FTVaultData>():
62                return FungibleTokenMetadataViews.FTVaultData(
63                    storagePath: USDCFlow.VaultStoragePath,
64                    receiverPath: USDCFlow.ReceiverPublicPath,
65                    metadataPath: USDCFlow.VaultPublicPath,
66                    receiverLinkedType: Type<&USDCFlow.Vault>(),
67                    metadataLinkedType: Type<&USDCFlow.Vault>(),
68                    createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
69                        return <-USDCFlow.createEmptyVault(vaultType: Type<@USDCFlow.Vault>())
70                    })
71                )
72            case Type<FungibleTokenMetadataViews.TotalSupply>():
73                return FungibleTokenMetadataViews.TotalSupply(
74                    totalSupply: USDCFlow.totalSupply
75                )
76        }
77        return nil
78    }
79
80    access(all) resource Vault: FungibleToken.Vault {
81
82        /// The total balance of this vault
83        access(all) var balance: UFix64
84
85        /// Initialize the balance at resource creation time
86        init(balance: UFix64) {
87            self.balance = balance
88        }
89
90        /// Called when a fungible token is burned via the `Burner.burn()` method
91        /// The total supply will only reflect the supply in the Cadence version
92        /// of the USDCFlow smart contract
93        access(contract) fun burnCallback() {
94            if self.balance > 0.0 {
95                assert(USDCFlow.totalSupply >= self.balance, message: "Cannot burn more than the total supply")
96                emit Burned(amount: self.balance, burntUUID: self.uuid)
97                USDCFlow.totalSupply = USDCFlow.totalSupply - self.balance
98            }
99            self.balance = 0.0
100        }
101
102        /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
103        access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
104            let supportedTypes: {Type: Bool} = {}
105            supportedTypes[self.getType()] = true
106            return supportedTypes
107        }
108
109        /// Returns whether the specified type can be deposited
110        access(all) view fun isSupportedVaultType(type: Type): Bool {
111            return self.getSupportedVaultTypes()[type] ?? false
112        }
113
114        /// Asks if the amount can be withdrawn from this vault
115        access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
116            return amount <= self.balance
117        }
118
119        access(all) fun createEmptyVault(): @USDCFlow.Vault {
120            return <-create Vault(balance: 0.0)
121        }
122
123        /// withdraw
124        /// @param amount: The amount of tokens to be withdrawn from the vault
125        /// @return The Vault resource containing the withdrawn funds
126        ///
127        access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
128            self.balance = self.balance - amount
129            return <-create Vault(balance: amount)
130        }
131
132        /// deposit
133        /// @param from: The Vault resource containing the funds that will be deposited
134        ///
135        access(all) fun deposit(from: @{FungibleToken.Vault}) {
136            let vault <- from as! @USDCFlow.Vault
137            self.balance = self.balance + vault.balance
138            destroy vault
139        }
140
141        /// Gets an array of all the Metadata Views implemented by USDCFlow
142        ///
143        /// @return An array of Types defining the implemented views. This value will be used by
144        ///         developers to know which parameter to pass to the resolveView() method.
145        ///
146        access(all) view fun getViews(): [Type] {
147            return USDCFlow.getContractViews(resourceType: nil)
148        }
149
150        /// Resolves Metadata Views out of the USDCFlow
151        ///
152        /// @param view: The Type of the desired view.
153        /// @return A structure representing the requested view.
154        ///
155        access(all) fun resolveView(_ view: Type): AnyStruct? {
156            return USDCFlow.resolveContractView(resourceType: nil, viewType: view)
157        }
158    }
159
160    access(all) resource Minter: FlowEVMBridgeHandlerInterfaces.TokenMinter {
161
162        /// Required function for the bridge to be able to work with the Minter
163        access(all) view fun getMintedType(): Type {
164            return Type<@USDCFlow.Vault>()
165        }
166
167        /// Function for the bridge to mint tokens that are bridged from Flow EVM
168        access(FlowEVMBridgeHandlerInterfaces.Mint) fun mint(amount: UFix64): @{FungibleToken.Vault} {
169            let newTotalSupply = USDCFlow.totalSupply + amount
170            USDCFlow.totalSupply = newTotalSupply
171
172            let vault <-create Vault(balance: amount)
173
174            emit Minted(amount: amount, mintedUUID: vault.uuid)
175            return <-vault
176        }
177
178        /// Function for the bridge to burn tokens that are bridged back to Flow EVM
179        access(all) fun burn(vault: @{FungibleToken.Vault}) {
180            let toBurn <- vault as! @USDCFlow.Vault
181            let amount = toBurn.balance
182
183            // This function updates USDCFlow.totalSupply
184            Burner.burn(<-toBurn)
185        }
186    }
187
188    /// createEmptyVault
189    ///
190    /// @return The new Vault resource with a balance of zero
191    ///
192    access(all) fun createEmptyVault(vaultType: Type): @Vault {
193        let r <-create Vault(balance: 0.0)
194        return <-r
195    }
196
197    init() {
198        self.totalSupply = 0.0
199        self.VaultStoragePath = /storage/usdcFlowVault
200        self.VaultPublicPath = /public/usdcFlowMetadata
201        self.ReceiverPublicPath = /public/usdcFlowReceiver
202
203        let minter <- create Minter()
204        self.account.storage.save(<-minter, to: /storage/usdcFlowMinter)
205
206        // Create the Vault with the total supply of tokens and save it in storage.
207        let vault <- create Vault(balance: self.totalSupply)
208        self.account.storage.save(<-vault, to: self.VaultStoragePath)
209
210        let tokenCap = self.account.capabilities.storage.issue<&USDCFlow.Vault>(self.VaultStoragePath)
211        self.account.capabilities.publish(tokenCap, at: self.ReceiverPublicPath)
212        self.account.capabilities.publish(tokenCap, at: self.VaultPublicPath)
213    }
214}
215