@flowtyio/flow-contracts 0.0.18 → 0.1.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,21 +2,18 @@
2
2
 
3
3
  # The Flow Fungible Token standard
4
4
 
5
- ## `FungibleToken` contract interface
5
+ ## `FungibleToken` contract
6
6
 
7
- The interface that all Fungible Token contracts would have to conform to.
8
- If a users wants to deploy a new token contract, their contract
9
- would need to implement the FungibleToken interface.
10
-
11
- Their contract would have to follow all the rules and naming
12
- that the interface specifies.
7
+ The Fungible Token standard is no longer an interface
8
+ that all fungible token contracts would have to conform to.
13
9
 
14
- ## `Vault` resource
10
+ If a users wants to deploy a new token contract, their contract
11
+ does not need to implement the FungibleToken interface, but their tokens
12
+ do need to implement the interfaces defined in this contract.
15
13
 
16
- Each account that owns tokens would need to have an instance
17
- of the Vault resource stored in their account storage.
14
+ ## `Vault` resource interface
18
15
 
19
- The Vault resource has methods that the owner and other users can call.
16
+ Each fungible token resource type needs to implement the `Vault` resource interface.
20
17
 
21
18
  ## `Provider`, `Receiver`, and `Balance` resource interfaces
22
19
 
@@ -32,32 +29,43 @@ these interfaces to do various things with the tokens.
32
29
  For example, a faucet can be implemented by conforming
33
30
  to the Provider interface.
34
31
 
35
- By using resources and interfaces, users of Fungible Token contracts
36
- can send and receive tokens peer-to-peer, without having to interact
37
- with a central ledger smart contract. To send tokens to another user,
38
- a user would simply withdraw the tokens from their Vault, then call
39
- the deposit function on another user's Vault to complete the transfer.
40
-
41
32
  */
42
33
 
43
- /// The interface that Fungible Token contracts implement.
44
- ///
45
- pub contract interface FungibleToken {
34
+ import "ViewResolver"
35
+ import "Burner"
46
36
 
47
- /// The total number of tokens in existence.
48
- /// It is up to the implementer to ensure that the total supply
49
- /// stays accurate and up to date
50
- pub var totalSupply: UFix64
37
+ /// FungibleToken
38
+ ///
39
+ /// Fungible Token implementations are no longer required to implement the fungible token
40
+ /// interface. We still have it as an interface here because there are some useful
41
+ /// utility methods that many projects will still want to have on their contracts,
42
+ /// but they are by no means required. all that is required is that the token
43
+ /// implements the `Vault` interface
44
+ access(all) contract interface FungibleToken: ViewResolver {
51
45
 
52
- /// The event that is emitted when the contract is created
53
- pub event TokensInitialized(initialSupply: UFix64)
46
+ // An entitlement for allowing the withdrawal of tokens from a Vault
47
+ access(all) entitlement Withdraw
54
48
 
55
49
  /// The event that is emitted when tokens are withdrawn from a Vault
56
- pub event TokensWithdrawn(amount: UFix64, from: Address?)
50
+ access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64)
51
+
52
+ /// The event that is emitted when tokens are deposited to a Vault
53
+ access(all) event Deposited(type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64)
54
+
55
+ /// Event that is emitted when the global burn method is called with a non-zero balance
56
+ access(all) event Burned(type: String, amount: UFix64, fromUUID: UInt64)
57
57
 
58
- /// The event that is emitted when tokens are deposited into a Vault
59
- pub event TokensDeposited(amount: UFix64, to: Address?)
58
+ /// Balance
59
+ ///
60
+ /// The interface that provides standard functions\
61
+ /// for getting balance information
62
+ ///
63
+ access(all) resource interface Balance {
64
+ access(all) var balance: UFix64
65
+ }
60
66
 
67
+ /// Provider
68
+ ///
61
69
  /// The interface that enforces the requirements for withdrawing
62
70
  /// tokens from the implementing type.
63
71
  ///
@@ -65,35 +73,37 @@ pub contract interface FungibleToken {
65
73
  /// because it leaves open the possibility of creating custom providers
66
74
  /// that do not necessarily need their own balance.
67
75
  ///
68
- pub resource interface Provider {
76
+ access(all) resource interface Provider {
77
+
78
+ /// Function to ask a provider if a specific amount of tokens
79
+ /// is available to be withdrawn
80
+ /// This could be useful to avoid panicing when calling withdraw
81
+ /// when the balance is unknown
82
+ /// Additionally, if the provider is pulling from multiple vaults
83
+ /// it only needs to check some of the vaults until the desired amount
84
+ /// is reached, potentially helping with performance.
85
+ ///
86
+ access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool
69
87
 
70
- /// Subtracts tokens from the owner's Vault
88
+ /// withdraw subtracts tokens from the implementing resource
71
89
  /// and returns a Vault with the removed tokens.
72
90
  ///
73
- /// The function's access level is public, but this is not a problem
74
- /// because only the owner storing the resource in their account
75
- /// can initially call this function.
76
- ///
77
- /// The owner may grant other accounts access by creating a private
78
- /// capability that allows specific other users to access
79
- /// the provider resource through a reference.
91
+ /// The function's access level is `access(Withdraw)`
92
+ /// So in order to access it, one would either need the object itself
93
+ /// or an entitled reference with `Withdraw`.
80
94
  ///
81
- /// The owner may also grant all accounts access by creating a public
82
- /// capability that allows all users to access the provider
83
- /// resource through a reference.
84
- ///
85
- /// @param amount: The amount of tokens to be withdrawn from the vault
86
- /// @return The Vault resource containing the withdrawn funds
87
- ///
88
- pub fun withdraw(amount: UFix64): @Vault {
95
+ access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
89
96
  post {
90
97
  // `result` refers to the return value
91
98
  result.balance == amount:
92
99
  "Withdrawal amount must be the same as the balance of the withdrawn Vault"
100
+ emit Withdrawn(type: self.getType().identifier, amount: amount, from: self.owner?.address, fromUUID: self.uuid, withdrawnUUID: result.uuid)
93
101
  }
94
102
  }
95
103
  }
96
104
 
105
+ /// Receiver
106
+ ///
97
107
  /// The interface that enforces the requirements for depositing
98
108
  /// tokens into the implementing type.
99
109
  ///
@@ -102,30 +112,55 @@ pub contract interface FungibleToken {
102
112
  /// can do custom things with the tokens, like split them up and
103
113
  /// send them to different places.
104
114
  ///
105
- pub resource interface Receiver {
115
+ access(all) resource interface Receiver {
106
116
 
107
- /// Takes a Vault and deposits it into the implementing resource type
108
- ///
109
- /// @param from: The Vault resource containing the funds that will be deposited
117
+ /// deposit takes a Vault and deposits it into the implementing resource type
110
118
  ///
111
- pub fun deposit(from: @Vault)
119
+ access(all) fun deposit(from: @{Vault})
112
120
 
113
- /// Below is referenced from the FLIP #69 https://github.com/onflow/flips/blob/main/flips/20230206-fungible-token-vault-type-discovery.md
114
- ///
115
- /// Returns the dictionary of Vault types that the the receiver is able to accept in its `deposit` method
116
- /// this then it would return `{Type<@FlowToken.Vault>(): true}` and if any custom receiver
117
- /// uses the default implementation then it would return empty dictionary as its parent
118
- /// resource doesn't conform with the `FungibleToken.Vault` resource.
119
- ///
120
- /// Custom receiver implementations are expected to upgrade their contracts to add an implementation
121
- /// that supports this method because it is very valuable for various applications to have.
122
- ///
123
- /// @return dictionary of supported deposit vault types by the implementing resource.
124
- ///
125
- pub fun getSupportedVaultTypes(): {Type: Bool} {
121
+ /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
122
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool}
123
+
124
+ /// Returns whether or not the given type is accepted by the Receiver
125
+ /// A vault that can accept any type should just return true by default
126
+ access(all) view fun isSupportedVaultType(type: Type): Bool
127
+ }
128
+
129
+ /// Vault
130
+ ///
131
+ /// Ideally, this interface would also conform to Receiver, Balance, Transferor, Provider, and Resolver
132
+ /// but that is not supported yet
133
+ ///
134
+ access(all) resource interface Vault: Receiver, Provider, Balance, ViewResolver.Resolver, Burner.Burnable {
135
+
136
+ /// Field that tracks the balance of a vault
137
+ access(all) var balance: UFix64
138
+
139
+ /// Called when a fungible token is burned via the `Burner.burn()` method
140
+ /// Implementations can do any bookkeeping or emit any events
141
+ /// that should be emitted when a vault is destroyed.
142
+ /// Many implementations will want to update the token's total supply
143
+ /// to reflect that the tokens have been burned and removed from the supply.
144
+ /// Implementations also need to set the balance to zero before the end of the function
145
+ /// This is to prevent vault owners from spamming fake Burned events.
146
+ access(contract) fun burnCallback() {
147
+ pre {
148
+ emit Burned(type: self.getType().identifier, amount: self.balance, fromUUID: self.uuid)
149
+ }
150
+ post {
151
+ self.balance == 0.0: "The balance must be set to zero during the burnCallback method so that it cannot be spammed"
152
+ }
153
+ self.balance = 0.0
154
+ }
155
+
156
+ /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
157
+ /// The default implementation is included here because vaults are expected
158
+ /// to only accepted their own type, so they have no need to provide an implementation
159
+ /// for this function
160
+ access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
126
161
  // Below check is implemented to make sure that run-time type would
127
162
  // only get returned when the parent resource conforms with `FungibleToken.Vault`.
128
- if self.getType().isSubtype(of: Type<@FungibleToken.Vault>()) {
163
+ if self.getType().isSubtype(of: Type<@{FungibleToken.Vault}>()) {
129
164
  return {self.getType(): true}
130
165
  } else {
131
166
  // Return an empty dictionary as the default value for resource who don't
@@ -133,104 +168,60 @@ pub contract interface FungibleToken {
133
168
  return {}
134
169
  }
135
170
  }
136
- }
137
-
138
- /// The interface that contains the `balance` field of the Vault
139
- /// and enforces that when new Vaults are created, the balance
140
- /// is initialized correctly.
141
- ///
142
- pub resource interface Balance {
143
-
144
- /// The total balance of a vault
145
- ///
146
- pub var balance: UFix64
147
-
148
- init(balance: UFix64) {
149
- post {
150
- self.balance == balance:
151
- "Balance must be initialized to the initial balance"
152
- }
153
- }
154
-
155
- /// Function that returns all the Metadata Views implemented by a Fungible Token
156
- ///
157
- /// @return An array of Types defining the implemented views. This value will be used by
158
- /// developers to know which parameter to pass to the resolveView() method.
159
- ///
160
- pub fun getViews(): [Type] {
161
- return []
162
- }
163
171
 
164
- /// Function that resolves a metadata view for this fungible token by type.
165
- ///
166
- /// @param view: The Type of the desired view.
167
- /// @return A structure representing the requested view.
168
- ///
169
- pub fun resolveView(_ view: Type): AnyStruct? {
170
- return nil
172
+ /// Checks if the given type is supported by this Vault
173
+ access(all) view fun isSupportedVaultType(type: Type): Bool {
174
+ return self.getSupportedVaultTypes()[type] ?? false
171
175
  }
172
- }
173
-
174
- /// The resource that contains the functions to send and receive tokens.
175
- /// The declaration of a concrete type in a contract interface means that
176
- /// every Fungible Token contract that implements the FungibleToken interface
177
- /// must define a concrete `Vault` resource that conforms to the `Provider`, `Receiver`,
178
- /// and `Balance` interfaces, and declares their required fields and functions
179
- ///
180
- pub resource Vault: Provider, Receiver, Balance {
181
-
182
- /// The total balance of the vault
183
- pub var balance: UFix64
184
176
 
185
- // The conforming type must declare an initializer
186
- // that allows providing the initial balance of the Vault
187
- //
188
- init(balance: UFix64)
189
-
190
- /// Subtracts `amount` from the Vault's balance
177
+ /// withdraw subtracts `amount` from the Vault's balance
191
178
  /// and returns a new Vault with the subtracted balance
192
179
  ///
193
- /// @param amount: The amount of tokens to be withdrawn from the vault
194
- /// @return The Vault resource containing the withdrawn funds
195
- ///
196
- pub fun withdraw(amount: UFix64): @Vault {
180
+ access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
197
181
  pre {
198
182
  self.balance >= amount:
199
183
  "Amount withdrawn must be less than or equal than the balance of the Vault"
200
184
  }
201
185
  post {
186
+ result.getType() == self.getType(): "Must return the same vault type as self"
202
187
  // use the special function `before` to get the value of the `balance` field
203
188
  // at the beginning of the function execution
204
189
  //
205
190
  self.balance == before(self.balance) - amount:
206
- "New Vault balance must be the difference of the previous balance and the withdrawn Vault"
191
+ "New Vault balance must be the difference of the previous balance and the withdrawn Vault balance"
207
192
  }
208
193
  }
209
194
 
210
- /// Takes a Vault and deposits it into the implementing resource type
195
+ /// deposit takes a Vault and adds its balance to the balance of this Vault
211
196
  ///
212
- /// @param from: The Vault resource containing the funds that will be deposited
213
- ///
214
- pub fun deposit(from: @Vault) {
197
+ access(all) fun deposit(from: @{FungibleToken.Vault}) {
215
198
  // Assert that the concrete type of the deposited vault is the same
216
199
  // as the vault that is accepting the deposit
217
200
  pre {
218
201
  from.isInstance(self.getType()):
219
202
  "Cannot deposit an incompatible token type"
203
+ emit Deposited(type: from.getType().identifier, amount: from.balance, to: self.owner?.address, toUUID: self.uuid, depositedUUID: from.uuid)
220
204
  }
221
205
  post {
222
206
  self.balance == before(self.balance) + before(from.balance):
223
207
  "New Vault balance must be the sum of the previous balance and the deposited Vault"
224
208
  }
225
209
  }
210
+
211
+ /// createEmptyVault allows any user to create a new Vault that has a zero balance
212
+ ///
213
+ access(all) fun createEmptyVault(): @{Vault} {
214
+ post {
215
+ result.balance == 0.0: "The newly created Vault must have zero balance"
216
+ }
217
+ }
226
218
  }
227
219
 
228
- /// Allows any user to create a new Vault that has a zero balance
229
- ///
230
- /// @return The new Vault resource
220
+ /// createEmptyVault allows any user to create a new Vault that has a zero balance
231
221
  ///
232
- pub fun createEmptyVault(): @Vault {
222
+ access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault} {
233
223
  post {
224
+ result.getType() == vaultType: "The returned vault does not match the desired type"
234
225
  result.balance == 0.0: "The newly created Vault must have zero balance"
235
226
  }
236
227
  }
@@ -1,24 +1,26 @@
1
1
  import "FungibleToken"
2
2
  import "MetadataViews"
3
+ import "ViewResolver"
3
4
 
4
5
  /// This contract implements the metadata standard proposed
5
6
  /// in FLIP-1087.
6
7
  ///
7
- /// Ref: https://github.com/onflow/flow/blob/master/flips/20220811-fungible-tokens-metadata.md
8
+ /// Ref: https://github.com/onflow/flips/blob/main/application/20220811-fungible-tokens-metadata.md
8
9
  ///
9
10
  /// Structs and resources can implement one or more
10
11
  /// metadata types, called views. Each view type represents
11
12
  /// a different kind of metadata.
12
13
  ///
13
- pub contract FungibleTokenMetadataViews {
14
+ access(all) contract FungibleTokenMetadataViews {
15
+
14
16
  /// FTView wraps FTDisplay and FTVaultData, and is used to give a complete
15
17
  /// picture of a Fungible Token. Most Fungible Token contracts should
16
18
  /// implement this view.
17
19
  ///
18
- pub struct FTView {
19
- pub let ftDisplay: FTDisplay?
20
- pub let ftVaultData: FTVaultData?
21
- init(
20
+ access(all) struct FTView {
21
+ access(all) let ftDisplay: FTDisplay?
22
+ access(all) let ftVaultData: FTVaultData?
23
+ view init(
22
24
  ftDisplay: FTDisplay?,
23
25
  ftVaultData: FTVaultData?
24
26
  ) {
@@ -32,7 +34,7 @@ pub contract FungibleTokenMetadataViews {
32
34
  /// @param viewResolver: A reference to the resolver resource
33
35
  /// @return A FTView struct
34
36
  ///
35
- pub fun getFTView(viewResolver: &{MetadataViews.Resolver}): FTView {
37
+ access(all) fun getFTView(viewResolver: &{ViewResolver.Resolver}): FTView {
36
38
  let maybeFTView = viewResolver.resolveView(Type<FTView>())
37
39
  if let ftView = maybeFTView {
38
40
  return ftView as! FTView
@@ -47,34 +49,34 @@ pub contract FungibleTokenMetadataViews {
47
49
  /// This can be used by applications to give an overview and
48
50
  /// graphics of the FT.
49
51
  ///
50
- pub struct FTDisplay {
52
+ access(all) struct FTDisplay {
51
53
  /// The display name for this token.
52
54
  ///
53
55
  /// Example: "Flow"
54
56
  ///
55
- pub let name: String
57
+ access(all) let name: String
56
58
 
57
59
  /// The abbreviated symbol for this token.
58
60
  ///
59
61
  /// Example: "FLOW"
60
- pub let symbol: String
62
+ access(all) let symbol: String
61
63
 
62
64
  /// A description the provides an overview of this token.
63
65
  ///
64
66
  /// Example: "The FLOW token is the native currency of the Flow network."
65
- pub let description: String
67
+ access(all) let description: String
66
68
 
67
69
  /// External link to a URL to view more information about the fungible token.
68
- pub let externalURL: MetadataViews.ExternalURL
70
+ access(all) let externalURL: MetadataViews.ExternalURL
69
71
 
70
72
  /// One or more versions of the fungible token logo.
71
- pub let logos: MetadataViews.Medias
73
+ access(all) let logos: MetadataViews.Medias
72
74
 
73
75
  /// Social links to reach the fungible token's social homepages.
74
76
  /// Possible keys may be "instagram", "twitter", "discord", etc.
75
- pub let socials: {String: MetadataViews.ExternalURL}
77
+ access(all) let socials: {String: MetadataViews.ExternalURL}
76
78
 
77
- init(
79
+ view init(
78
80
  name: String,
79
81
  symbol: String,
80
82
  description: String,
@@ -96,7 +98,7 @@ pub contract FungibleTokenMetadataViews {
96
98
  /// @param viewResolver: A reference to the resolver resource
97
99
  /// @return An optional FTDisplay struct
98
100
  ///
99
- pub fun getFTDisplay(_ viewResolver: &{MetadataViews.Resolver}): FTDisplay? {
101
+ access(all) fun getFTDisplay(_ viewResolver: &{ViewResolver.Resolver}): FTDisplay? {
100
102
  if let maybeDisplayView = viewResolver.resolveView(Type<FTDisplay>()) {
101
103
  if let displayView = maybeDisplayView as? FTDisplay {
102
104
  return displayView
@@ -109,58 +111,45 @@ pub contract FungibleTokenMetadataViews {
109
111
  /// This can be used by applications to setup a FT vault with proper
110
112
  /// storage and public capabilities.
111
113
  ///
112
- pub struct FTVaultData {
114
+ access(all) struct FTVaultData {
113
115
  /// Path in storage where this FT vault is recommended to be stored.
114
- pub let storagePath: StoragePath
116
+ access(all) let storagePath: StoragePath
115
117
 
116
118
  /// Public path which must be linked to expose the public receiver capability.
117
- pub let receiverPath: PublicPath
119
+ access(all) let receiverPath: PublicPath
118
120
 
119
121
  /// Public path which must be linked to expose the balance and resolver public capabilities.
120
- pub let metadataPath: PublicPath
121
-
122
- /// Private path which should be linked to expose the provider capability to withdraw funds
123
- /// from the vault.
124
- pub let providerPath: PrivatePath
122
+ access(all) let metadataPath: PublicPath
125
123
 
126
124
  /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
127
125
  /// the `FungibleToken.Receiver` interface.
128
- pub let receiverLinkedType: Type
126
+ access(all) let receiverLinkedType: Type
129
127
 
130
128
  /// Type that should be linked at the `receiverPath`. This is a restricted type requiring
131
- /// the `FungibleToken.Balance` and `MetadataViews.Resolver` interfaces.
132
- pub let metadataLinkedType: Type
133
-
134
- /// Type that should be linked at the aforementioned private path. This
135
- /// is normally a restricted type with at a minimum the `FungibleToken.Provider` interface.
136
- pub let providerLinkedType: Type
129
+ /// the `ViewResolver.Resolver` interfaces.
130
+ access(all) let metadataLinkedType: Type
137
131
 
138
132
  /// Function that allows creation of an empty FT vault that is intended
139
133
  /// to store the funds.
140
- pub let createEmptyVault: ((): @FungibleToken.Vault)
134
+ access(all) let createEmptyVault: fun(): @{FungibleToken.Vault}
141
135
 
142
- init(
136
+ view init(
143
137
  storagePath: StoragePath,
144
138
  receiverPath: PublicPath,
145
139
  metadataPath: PublicPath,
146
- providerPath: PrivatePath,
147
140
  receiverLinkedType: Type,
148
141
  metadataLinkedType: Type,
149
- providerLinkedType: Type,
150
- createEmptyVaultFunction: ((): @FungibleToken.Vault)
142
+ createEmptyVaultFunction: fun(): @{FungibleToken.Vault}
151
143
  ) {
152
144
  pre {
153
145
  receiverLinkedType.isSubtype(of: Type<&{FungibleToken.Receiver}>()): "Receiver public type must include FungibleToken.Receiver."
154
- metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Balance, MetadataViews.Resolver}>()): "Metadata public type must include FungibleToken.Balance and MetadataViews.Resolver interfaces."
155
- providerLinkedType.isSubtype(of: Type<&{FungibleToken.Provider}>()): "Provider type must include FungibleToken.Provider interface."
146
+ metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Vault}>()): "Metadata linked type must be a fungible token vault"
156
147
  }
157
148
  self.storagePath = storagePath
158
149
  self.receiverPath = receiverPath
159
150
  self.metadataPath = metadataPath
160
- self.providerPath = providerPath
161
151
  self.receiverLinkedType = receiverLinkedType
162
152
  self.metadataLinkedType = metadataLinkedType
163
- self.providerLinkedType = providerLinkedType
164
153
  self.createEmptyVault = createEmptyVaultFunction
165
154
  }
166
155
  }
@@ -170,7 +159,7 @@ pub contract FungibleTokenMetadataViews {
170
159
  /// @param viewResolver: A reference to the resolver resource
171
160
  /// @return A optional FTVaultData struct
172
161
  ///
173
- pub fun getFTVaultData(_ viewResolver: &{MetadataViews.Resolver}): FTVaultData? {
162
+ access(all) fun getFTVaultData(_ viewResolver: &{ViewResolver.Resolver}): FTVaultData? {
174
163
  if let view = viewResolver.resolveView(Type<FTVaultData>()) {
175
164
  if let v = view as? FTVaultData {
176
165
  return v
@@ -179,5 +168,13 @@ pub contract FungibleTokenMetadataViews {
179
168
  return nil
180
169
  }
181
170
 
171
+ /// View to expose the total supply of the Vault's token
172
+ access(all) struct TotalSupply {
173
+ access(all) let supply: UFix64
174
+
175
+ view init(totalSupply: UFix64) {
176
+ self.supply = totalSupply
177
+ }
178
+ }
182
179
  }
183
180