@flowtyio/flow-contracts 0.0.2 → 0.0.3

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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # flow-contracts
2
2
 
3
3
  ```
4
- npm i @flowtyio/flow-contracts
4
+ npm i @flowty/flow-contracts
5
5
  ```
6
6
 
7
7
  This repository publishes common Flow contracts as an npm package so that they can be more easily consumed.
@@ -36,7 +36,7 @@ For example, here is how you might add `NonFungibleToken` to your flow.json :
36
36
  },
37
37
  "contracts": {
38
38
  "NonFungibleToken": {
39
- "source": "./node_modules/@flowtyio/flow-contracts/contracts/NonFungibleToken.cdc",
39
+ "source": "./node_modules/@flowty/flow-contracts/contracts/NonFungibleToken.cdc",
40
40
  "aliases": {
41
41
  "emulator": "0xf8d6e0586b0a20c7",
42
42
  "testnet": "0x631e88ae7f1d7c20",
@@ -83,7 +83,7 @@ you will need to:
83
83
  },
84
84
  "contracts": {
85
85
  "FungibleToken": {
86
- "source": "./node_modules/@flowtyio/flow-contracts/contracts/FungibleToken.cdc",
86
+ "source": "./node_modules/@flowty/flow-contracts/contracts/FungibleToken.cdc",
87
87
  "aliases": {
88
88
  "emulator": "0xee82856bf20e2aa6",
89
89
  }
package/add.js ADDED
@@ -0,0 +1,133 @@
1
+ const {getConfig, writeConfig, getProjectConfig, getDefaultConfigPath} = require("./utils");
2
+ const {getImports} = require("./dependency-tree");
3
+
4
+ const specialContractsHandlers = {
5
+ "FungibleToken": (contract, userConfig, account) => {
6
+ console.log("FungibleToken requires some special setup. The account `emulator-ft` " +
7
+ "will be created and the contract will be deployed to it on the emulator. \nGoing forward, any deployments to the " +
8
+ "flow emulator will require the --update flag to work correctly.")
9
+
10
+ const name = "FungibleToken"
11
+
12
+ const serverPK = userConfig.accounts[account].key
13
+ const ftAccount = {
14
+ address: "ee82856bf20e2aa6", // this is the FungibleToken address on the flow emulator
15
+ key: serverPK
16
+ }
17
+ const emulatorAcct = "emulator-ft"
18
+
19
+ // ensure emulator-ft is an account
20
+ userConfig.accounts[emulatorAcct] = ftAccount
21
+ if (!userConfig.deployments) {
22
+ userConfig.deployments = {}
23
+ }
24
+
25
+ // ensure that emulator-ft is a deployment account
26
+ if (!userConfig.deployments.emulator) {
27
+ userConfig.deployments.emulator = {}
28
+ }
29
+
30
+ if (!userConfig.deployments.emulator[emulatorAcct]) {
31
+ userConfig.deployments.emulator[emulatorAcct] = []
32
+ }
33
+
34
+ userConfig.contracts[name] = contract
35
+
36
+ if (!userConfig.deployments.emulator[emulatorAcct].includes(name)) {
37
+ userConfig.deployments.emulator[emulatorAcct].push(name)
38
+ }
39
+ }
40
+ }
41
+
42
+ const importContract = (contractName, source, config, account) => {
43
+ let newConfig = {...config}
44
+
45
+ const contract = source.contracts[contractName]
46
+ if (!contract) {
47
+ console.error(`Contract "${contractName}" could not be found`)
48
+ return
49
+ }
50
+
51
+ if (specialContractsHandlers[contractName]) {
52
+ specialContractsHandlers[contractName](contract, newConfig, account)
53
+ } else {
54
+ // only add the contract if it doesn't already exist
55
+ if (!newConfig.contracts[contractName]) {
56
+ newConfig.contracts[contractName] = contract
57
+ }
58
+
59
+ // add this contract to the deployment list of the specified account
60
+ if (!newConfig.deployments.emulator[account].includes(contractName)) {
61
+ newConfig.deployments.emulator[account].push(contractName)
62
+ }
63
+ }
64
+
65
+ return newConfig
66
+ }
67
+
68
+ const add = ({name, config, account}) => {
69
+ let userConfig = getConfig(config)
70
+
71
+ const exampleConfigLocation = `${__dirname}/flow.json`
72
+ const exampleConfig = getConfig(exampleConfigLocation)
73
+ const contract = exampleConfig.contracts[name]
74
+ if (!contract) {
75
+ console.error(`Contract "${name}" could not be found`)
76
+ return
77
+ }
78
+
79
+ if (!userConfig.contracts) {
80
+ userConfig.contracts = {}
81
+ }
82
+
83
+ // validate the specified account exists
84
+ if (!userConfig.accounts[account]) {
85
+ console.error(`Account "${account}" could not be found`)
86
+ return
87
+ }
88
+
89
+ if (!userConfig.deployments) {
90
+ userConfig.deployments = {}
91
+ }
92
+
93
+ if (!userConfig.deployments.emulator) {
94
+ userConfig.deployments.emulator = {}
95
+ }
96
+
97
+ if (!userConfig.deployments.emulator[account]) {
98
+ userConfig.deployments.emulator[account] = []
99
+ }
100
+
101
+ // get all imports, add them first, then add the one being requested.
102
+ const imports = getImports(name)
103
+ if (imports) {
104
+ console.log("The following contracts will also be added to the config: ", imports.join(", "))
105
+ imports.forEach(contractName => {
106
+ userConfig = importContract(contractName, exampleConfig, userConfig, account)
107
+ })
108
+ }
109
+
110
+ console.log("finished adding dependencies, adding requested contract")
111
+ userConfig = importContract(name, exampleConfig, userConfig, account)
112
+
113
+ writeConfig(config, userConfig)
114
+
115
+ console.log(`Contract "${name}" added to ${config}`)
116
+ }
117
+
118
+ const addAll = (path, account) => {
119
+ const configPath = path ?? getDefaultConfigPath()
120
+
121
+ console.log(`Adding all contracts to config found at ${configPath}`)
122
+ const projectConfig = getProjectConfig()
123
+ const userConfig = getConfig(configPath)
124
+
125
+ Object.keys(projectConfig.contracts).forEach(name => {
126
+ add({name, config: configPath, account})
127
+ })
128
+ }
129
+
130
+ module.exports = {
131
+ add,
132
+ addAll
133
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+
3
+ # The Flow Fungible Token standard
4
+
5
+ ## `FungibleToken` contract interface
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.
13
+
14
+ ## `Vault` resource
15
+
16
+ Each account that owns tokens would need to have an instance
17
+ of the Vault resource stored in their account storage.
18
+
19
+ The Vault resource has methods that the owner and other users can call.
20
+
21
+ ## `Provider`, `Receiver`, and `Balance` resource interfaces
22
+
23
+ These interfaces declare pre-conditions and post-conditions that restrict
24
+ the execution of the functions in the Vault.
25
+
26
+ They are separate because it gives the user the ability to share
27
+ a reference to their Vault that only exposes the fields functions
28
+ in one or more of the interfaces.
29
+
30
+ It also gives users the ability to make custom resources that implement
31
+ these interfaces to do various things with the tokens.
32
+ For example, a faucet can be implemented by conforming
33
+ to the Provider interface.
34
+
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
+ */
42
+
43
+ /// The interface that Fungible Token contracts implement.
44
+ ///
45
+ pub contract interface FungibleToken {
46
+
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
51
+
52
+ /// The event that is emitted when the contract is created
53
+ pub event TokensInitialized(initialSupply: UFix64)
54
+
55
+ /// The event that is emitted when tokens are withdrawn from a Vault
56
+ pub event TokensWithdrawn(amount: UFix64, from: Address?)
57
+
58
+ /// The event that is emitted when tokens are deposited into a Vault
59
+ pub event TokensDeposited(amount: UFix64, to: Address?)
60
+
61
+ /// The interface that enforces the requirements for withdrawing
62
+ /// tokens from the implementing type.
63
+ ///
64
+ /// It does not enforce requirements on `balance` here,
65
+ /// because it leaves open the possibility of creating custom providers
66
+ /// that do not necessarily need their own balance.
67
+ ///
68
+ pub resource interface Provider {
69
+
70
+ /// Subtracts tokens from the owner's Vault
71
+ /// and returns a Vault with the removed tokens.
72
+ ///
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.
80
+ ///
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 {
89
+ post {
90
+ // `result` refers to the return value
91
+ result.balance == amount:
92
+ "Withdrawal amount must be the same as the balance of the withdrawn Vault"
93
+ }
94
+ }
95
+ }
96
+
97
+ /// The interface that enforces the requirements for depositing
98
+ /// tokens into the implementing type.
99
+ ///
100
+ /// We do not include a condition that checks the balance because
101
+ /// we want to give users the ability to make custom receivers that
102
+ /// can do custom things with the tokens, like split them up and
103
+ /// send them to different places.
104
+ ///
105
+ pub resource interface Receiver {
106
+
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
110
+ ///
111
+ pub fun deposit(from: @Vault)
112
+
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} {
126
+ // Below check is implemented to make sure that run-time type would
127
+ // only get returned when the parent resource conforms with `FungibleToken.Vault`.
128
+ if self.getType().isSubtype(of: Type<@FungibleToken.Vault>()) {
129
+ return {self.getType(): true}
130
+ } else {
131
+ // Return an empty dictionary as the default value for resource who don't
132
+ // implement `FungibleToken.Vault`, such as `FungibleTokenSwitchboard`, `TokenForwarder` etc.
133
+ return {}
134
+ }
135
+ }
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
+
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
171
+ }
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
+
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
191
+ /// and returns a new Vault with the subtracted balance
192
+ ///
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 {
197
+ pre {
198
+ self.balance >= amount:
199
+ "Amount withdrawn must be less than or equal than the balance of the Vault"
200
+ }
201
+ post {
202
+ // use the special function `before` to get the value of the `balance` field
203
+ // at the beginning of the function execution
204
+ //
205
+ self.balance == before(self.balance) - amount:
206
+ "New Vault balance must be the difference of the previous balance and the withdrawn Vault"
207
+ }
208
+ }
209
+
210
+ /// Takes a Vault and deposits it into the implementing resource type
211
+ ///
212
+ /// @param from: The Vault resource containing the funds that will be deposited
213
+ ///
214
+ pub fun deposit(from: @Vault) {
215
+ // Assert that the concrete type of the deposited vault is the same
216
+ // as the vault that is accepting the deposit
217
+ pre {
218
+ from.isInstance(self.getType()):
219
+ "Cannot deposit an incompatible token type"
220
+ }
221
+ post {
222
+ self.balance == before(self.balance) + before(from.balance):
223
+ "New Vault balance must be the sum of the previous balance and the deposited Vault"
224
+ }
225
+ }
226
+ }
227
+
228
+ /// Allows any user to create a new Vault that has a zero balance
229
+ ///
230
+ /// @return The new Vault resource
231
+ ///
232
+ pub fun createEmptyVault(): @Vault {
233
+ post {
234
+ result.balance == 0.0: "The newly created Vault must have zero balance"
235
+ }
236
+ }
237
+ }