← flow-mainnet0x73cd…04c4Revertedblock 168391034
No position yet0 / 71
FetchingOpeningPositioning
cadence
Narrow session: Code, Call Trace and Values only, read-only. The event log and stepping need a wider viewport.
  • CODE
1 import FungibleToken from 0xf233dcee88fe0abe
2 import MetadataViews from 0x1d7e57aa55817448
3 import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
4
5 access(all) contract FlowToken: FungibleToken {
6
7 // Total supply of Flow tokens in existence
8 access(all) var totalSupply: UFix64
9
10 // Event that is emitted when tokens are withdrawn from a Vault
11 access(all) event TokensWithdrawn(amount: UFix64, from: Address?)
12
13 // Event that is emitted when tokens are deposited to a Vault
14 access(all) event TokensDeposited(amount: UFix64, to: Address?)
15
16 // Event that is emitted when new tokens are minted
17 access(all) event TokensMinted(amount: UFix64)
18
19 // Event that is emitted when a new minter resource is created
20 access(all) event MinterCreated(allowedAmount: UFix64)
21
22 // Event that is emitted when a new burner resource is created
23 access(all) event BurnerCreated()
24
25 // Vault
26 //
27 // Each user stores an instance of only the Vault in their storage
28 // The functions in the Vault and governed by the pre and post conditions
29 // in FungibleToken when they are called.
30 // The checks happen at runtime whenever a function is called.
31 //
32 // Resources can only be created in the context of the contract that they
33 // are defined in, so there is no way for a malicious user to create Vaults
34 // out of thin air. A special Minter resource needs to be defined to mint
35 // new tokens.
36 //
37 access(all) resource Vault: FungibleToken.Vault {
38
39 // holds the balance of a users tokens
40 access(all) var balance: UFix64
41
42 // initialize the balance at resource creation time
43 init(balance: UFix64) {
44 self.balance = balance
45 }
46
47 /// Called when a fungible token is burned via the `Burner.burn()` method
48 access(contract) fun burnCallback() {
49 if self.balance > 0.0 {
50 FlowToken.totalSupply = FlowToken.totalSupply - self.balance
51 }
52 self.balance = 0.0
53 }
54
55 /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
56 access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
57 return {self.getType(): true}
58 }
59
60 access(all) view fun isSupportedVaultType(type: Type): Bool {
61 if (type == self.getType()) { return true } else { return false }
62 }
63
64 /// Asks if the amount can be withdrawn from this vault
65 access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {
66 return amount <= self.balance
67 }
68
69 // withdraw
70 //
71 // Function that takes an integer amount as an argument
72 // and withdraws that amount from the Vault.
73 // It creates a new temporary Vault that is used to hold
74 // the money that is being transferred. It returns the newly
75 // created Vault to the context that called so it can be deposited
76 // elsewhere.
77 //
78 access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
79 self.balance = self.balance - amount
80
81 // If the owner is the staking account, do not emit the contract defined events
82 // this is to help with the performance of the epoch transition operations
83 // Either way, event listeners should be paying attention to the
84 // FungibleToken.Withdrawn events anyway because those contain
85 // much more comprehensive metadata
86 // Additionally, these events will eventually be removed from this contract completely
87 // in favor of the FungibleToken events
88 //
89 // NOTE: The address exclusions below are NOT a security bypass — they are a documented
90 // performance optimization for known service accounts that process high volumes of
91 // token movements during epoch transitions. These are fixed, well-known protocol
92 // addresses (emulator service account, testnet staking contract, mainnet staking contract).
93 // This does not affect the security of user funds in any way.
94 if let address = self.owner?.address {
95 if address != 0xf8d6e0586b0a20c7 &&
96 address != 0xf4527793ee68aede &&
97 address != 0x9eca2b38b18b5dfe &&
98 address != 0x8624b52f9ddcd04a
99 {
100 emit TokensWithdrawn(amount: amount, from: address)
101 }
102 } else {
103 emit TokensWithdrawn(amount: amount, from: nil)
104 }
105 return <-create Vault(balance: amount)
106 }
107
108 // deposit
109 //
110 // Function that takes a Vault object as an argument and adds
111 // its balance to the balance of the owners Vault.
112 // It is allowed to destroy the sent Vault because the Vault
113 // was a temporary holder of the tokens. The Vault's balance has
114 // been consumed and therefore can be destroyed.
115 access(all) fun deposit(from: @{FungibleToken.Vault}) {
116 let vault <- from as! @FlowToken.Vault
117 self.balance = self.balance + vault.balance
118
119 // If the owner is the staking account, do not emit the contract defined events
120 // this is to help with the performance of the epoch transition operations
121 // Either way, event listeners should be paying attention to the
122 // FungibleToken.Deposited events anyway because those contain
123 // much more comprehensive metadata
124 // Additionally, these events will eventually be removed from this contract completely
125 // in favor of the FungibleToken events
126 if let address = self.owner?.address {
127 if address != 0xf8d6e0586b0a20c7 &&
128 address != 0xf4527793ee68aede &&
129 address != 0x9eca2b38b18b5dfe &&
130 address != 0x8624b52f9ddcd04a
131 {
132 emit TokensDeposited(amount: vault.balance, to: address)
133 }
134 } else {
135 emit TokensDeposited(amount: vault.balance, to: nil)
136 }
137 vault.balance = 0.0
138 destroy vault
139 }
140
141 /// Get all the Metadata Views implemented by FlowToken
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 FlowToken.getContractViews(resourceType: nil)
148 }
149
150 /// Get a Metadata View from FlowToken
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 FlowToken.resolveContractView(resourceType: nil, viewType: view)
157 }
158
159 access(all) fun createEmptyVault(): @{FungibleToken.Vault} {
160 return <-create Vault(balance: 0.0)
161 }
162 }
163
164 // createEmptyVault
165 //
166 // Function that creates a new Vault with a balance of zero
167 // and returns it to the calling context. A user must call this function
168 // and store the returned Vault in their storage in order to allow their
169 // account to be able to receive deposits of this token type.
170 //
171 access(all) fun createEmptyVault(vaultType: Type): @FlowToken.Vault {
172 return <-create Vault(balance: 0.0)
173 }
174
175 /// Gets a list of the metadata views that this contract supports
176 access(all) view fun getContractViews(resourceType: Type?): [Type] {
177 return [Type<FungibleTokenMetadataViews.FTView>(),
178 Type<FungibleTokenMetadataViews.FTDisplay>(),
179 Type<FungibleTokenMetadataViews.FTVaultData>(),
180 Type<FungibleTokenMetadataViews.TotalSupply>()]
181 }
182
183 /// Get a Metadata View from FlowToken
184 ///
185 /// @param view: The Type of the desired view.
186 /// @return A structure representing the requested view.
187 ///
188 access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
189 switch viewType {
190 case Type<FungibleTokenMetadataViews.FTView>():
191 return FungibleTokenMetadataViews.FTView(
192 ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
193 ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
194 )
195 case Type<FungibleTokenMetadataViews.FTDisplay>():
196 let media = MetadataViews.Media(
197 file: MetadataViews.HTTPFile(
198 url: FlowToken.getLogoURI()
199 ),
200 mediaType: "image/svg+xml"
201 )
202 let medias = MetadataViews.Medias([media])
203 return FungibleTokenMetadataViews.FTDisplay(
204 name: "FLOW Network Token",
205 symbol: "FLOW",
206 description: "FLOW is the native token for the Flow blockchain. It is required for securing the network, transaction fees, storage fees, staking, FLIP voting and may be used by applications built on the Flow Blockchain",
207 externalURL: MetadataViews.ExternalURL("https://flow.com"),
208 logos: medias,
209 socials: {
210 "twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")
211 }
212 )
213 case Type<FungibleTokenMetadataViews.FTVaultData>():
214 let vaultRef = FlowToken.account.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)
215 ?? panic("FlowToken.resolveContractView: Could not borrow reference to the contract's Vault!")
216 return FungibleTokenMetadataViews.FTVaultData(
217 storagePath: /storage/flowTokenVault,
218 receiverPath: /public/flowTokenReceiver,
219 metadataPath: /public/flowTokenBalance,
220 receiverLinkedType: Type<&{FungibleToken.Receiver, FungibleToken.Vault}>(),
221 metadataLinkedType: Type<&{FungibleToken.Balance, FungibleToken.Vault}>(),
222 createEmptyVaultFunction: (fun (): @{FungibleToken.Vault} {
223 return <-vaultRef.createEmptyVault()
224 })
225 )
226 case Type<FungibleTokenMetadataViews.TotalSupply>():
227 return FungibleTokenMetadataViews.TotalSupply(totalSupply: FlowToken.totalSupply)
228 }
229 return nil
230 }
231
232 access(all) resource Administrator {
233 // createNewMinter
234 //
235 // Function that creates and returns a new minter resource
236 //
237 access(all) fun createNewMinter(allowedAmount: UFix64): @Minter {
238 emit MinterCreated(allowedAmount: allowedAmount)
239 return <-create Minter(allowedAmount: allowedAmount)
240 }
241 }
242
243 // Minter
244 //
245 // Resource object that token admin accounts can hold to mint new tokens.
246 //
247 access(all) resource Minter {
248
249 // the amount of tokens that the minter is allowed to mint
250 access(all) var allowedAmount: UFix64
251
252 // mintTokens
253 //
254 // Function that mints new tokens, adds them to the total supply,
255 // and returns them to the calling context.
256 //
257 access(all) fun mintTokens(amount: UFix64): @FlowToken.Vault {
258 pre {
259 amount > UFix64(0): "FlowToken.Minter.mintTokens: Amount minted must be greater than zero but got \(amount)"
260 amount <= self.allowedAmount: "FlowToken.Minter.mintTokens: Amount minted (\(amount)) must be less than or equal to the allowed amount (\(self.allowedAmount))"
261 }
262 FlowToken.totalSupply = FlowToken.totalSupply + amount
263 self.allowedAmount = self.allowedAmount - amount
264 emit TokensMinted(amount: amount)
265 return <-create Vault(balance: amount)
266 }
267
268 init(allowedAmount: UFix64) {
269 self.allowedAmount = allowedAmount
270 }
271 }
272
273 /// Gets the Flow Logo XML URI from storage
274 access(all) view fun getLogoURI(): String {
275 return FlowToken.account.storage.copy<String>(from: /storage/flowTokenLogoURI) ?? ""
276 }
277
278 init() {
279 self.totalSupply = 0.0
280
281 // Create the Vault with the total supply of tokens and save it in storage
282 //
283 let vault <- create Vault(balance: self.totalSupply)
284
285 self.account.storage.save(<-vault, to: /storage/flowTokenVault)
286
287 // Create a public capability to the stored Vault that only exposes
288 // the `deposit` method through the `Receiver` interface
289 //
290 let receiverCapability = self.account.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault)
291 self.account.capabilities.publish(receiverCapability, at: /public/flowTokenReceiver)
292
293 // Create a public capability to the stored Vault that only exposes
294 // the `balance` field through the `Balance` interface
295 //
296 let balanceCapability = self.account.capabilities.storage.issue<&FlowToken.Vault>(/storage/flowTokenVault)
297 self.account.capabilities.publish(balanceCapability, at: /public/flowTokenBalance)
298
299 let admin <- create Administrator()
300 self.account.storage.save(<-admin, to: /storage/flowTokenAdmin)
301
302 }
303 }
FunctionCallsSelf ACIR opcodes
<transaction>10
<toplevel>10
<host>10
FlowFees.verifyPayersBalanceForTransactionExecution10
FlowFees.computeFees20
FlowFees.getFeeParameters20
FlowStorageFees.defaultTokenReservedBalance10
FlowStorageFees.convertUInt64StorageBytesToUFix64Megabytes10
FlowStorageFees.storageCapacityToFlow10
FlowFees.VerifyPayerBalanceResult.init10
FlowFees.deductTransactionFee20
FungibleToken.Vault.withdraw20
FlowToken.Vault.withdraw10
FlowToken.Vault.init10
FungibleToken.Provider.withdraw10
FlowFees.collectFeesOnChildAccounts20
FlowFees.borrowChildFeeAccounts10
FungibleToken.Vault.deposit10
FlowToken.Vault.deposit10
By function — self cost, callees excluded.Call order
FrameACIR opcodes
<transaction>
<toplevel>:1
<host>:237
FlowFees.verifyPayersBalanceForTransactionExecution:237
FlowFees.computeFees:335
FlowFees.getFeeParameters:322
FlowStorageFees.defaultTokenReservedBalance:158
FlowStorageFees.convertUInt64StorageBytesToUFix64Megabytes:134
FlowStorageFees.storageCapacityToFlow:123
FlowFees.VerifyPayerBalanceResult.init:217
FlowFees.deductTransactionFee:268
FlowFees.computeFees:335
FlowFees.getFeeParameters:322
FungibleToken.Vault.withdraw:234
FlowToken.Vault.withdraw:79
FlowToken.Vault.init:44
FungibleToken.Provider.withdraw:126
FungibleToken.Vault.withdraw:228
FlowFees.deductTransactionFee:291
FlowFees.collectFeesOnChildAccounts:300
FlowFees.borrowChildFeeAccounts:58
FungibleToken.Vault.deposit:259
FlowToken.Vault.deposit:116
FlowFees.collectFeesOnChildAccounts:305
By call order.Self cost

25 recorded entries — every call this recording opened, in the order it opened them, and 0 storage writes. Each row's step is its coordinate in the recording. The replay's reproduced effects are published as a count rather than one record each, so they are in the transaction pane and not in this timeline.

→call0<transaction>
→call0<toplevel>A.f919ee77447b7497.FlowFees.cdc:1
→call1<host>A.f919ee77447b7497.FlowFees.cdc:237
→call1FlowFees.verifyPayersBalanceForTransactionExecutionA.f919ee77447b7497.FlowFees.cdc:237
→call2FlowFees.computeFeesA.f919ee77447b7497.FlowFees.cdc:335
→call3FlowFees.getFeeParametersA.f919ee77447b7497.FlowFees.cdc:322
→call7FlowStorageFees.defaultTokenReservedBalanceA.e467b9dd11fa00df.FlowStorageFees.cdc:158
→call9FlowStorageFees.convertUInt64StorageBytesToUFix64MegabytesA.e467b9dd11fa00df.FlowStorageFees.cdc:134
→call12FlowStorageFees.storageCapacityToFlowA.e467b9dd11fa00df.FlowStorageFees.cdc:123
→call21FlowFees.VerifyPayerBalanceResult.initA.f919ee77447b7497.FlowFees.cdc:217
→call24FlowFees.deductTransactionFeeA.f919ee77447b7497.FlowFees.cdc:268
→call25FlowFees.computeFeesA.f919ee77447b7497.FlowFees.cdc:335
→call26FlowFees.getFeeParametersA.f919ee77447b7497.FlowFees.cdc:322
→call34FungibleToken.Vault.withdrawA.f233dcee88fe0abe.FungibleToken.cdc:234
→call37FlowToken.Vault.withdrawA.1654653399040a61.FlowToken.cdc:79
→call42FlowToken.Vault.initA.1654653399040a61.FlowToken.cdc:44
→call43FungibleToken.Provider.withdrawA.f233dcee88fe0abe.FungibleToken.cdc:126
→call44FungibleToken.Vault.withdrawA.f233dcee88fe0abe.FungibleToken.cdc:228
→call47FlowFees.deductTransactionFeeA.f919ee77447b7497.FlowFees.cdc:291
→call48FlowFees.collectFeesOnChildAccountsA.f919ee77447b7497.FlowFees.cdc:300
→call49FlowFees.borrowChildFeeAccountsA.f919ee77447b7497.FlowFees.cdc:58
→call52FungibleToken.Vault.depositA.f233dcee88fe0abe.FungibleToken.cdc:259
→call60FlowToken.Vault.depositA.1654653399040a61.FlowToken.cdc:116
→call69FlowFees.collectFeesOnChildAccountsA.f919ee77447b7497.FlowFees.cdc:305
✕revert69revertedthe transaction reverted; the recording is complete up to here
  • VALUES

The recorded values are in the published recording. Reading them needs the replay engine, which this page has not started.