@flowtyio/flow-contracts 0.0.2 → 0.0.4

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.
@@ -0,0 +1,202 @@
1
+ /**
2
+
3
+ ## The Flow Non-Fungible Token standard
4
+
5
+ ## `NonFungibleToken` contract interface
6
+
7
+ The interface that all Non-Fungible Token contracts could conform to.
8
+ If a user wants to deploy a new NFT contract, their contract would need
9
+ to implement the NonFungibleToken interface.
10
+
11
+ Their contract would have to follow all the rules and naming
12
+ that the interface specifies.
13
+
14
+ ## `NFT` resource
15
+
16
+ The core resource type that represents an NFT in the smart contract.
17
+
18
+ ## `Collection` Resource
19
+
20
+ The resource that stores a user's NFT collection.
21
+ It includes a few functions to allow the owner to easily
22
+ move tokens in and out of the collection.
23
+
24
+ ## `Provider` and `Receiver` resource interfaces
25
+
26
+ These interfaces declare functions with some pre and post conditions
27
+ that require the Collection to follow certain naming and behavior standards.
28
+
29
+ They are separate because it gives the user the ability to share a reference
30
+ to their Collection that only exposes the fields and functions in one or more
31
+ of the interfaces. It also gives users the ability to make custom resources
32
+ that implement these interfaces to do various things with the tokens.
33
+
34
+ By using resources and interfaces, users of NFT smart contracts can send
35
+ and receive tokens peer-to-peer, without having to interact with a central ledger
36
+ smart contract.
37
+
38
+ To send an NFT to another user, a user would simply withdraw the NFT
39
+ from their Collection, then call the deposit function on another user's
40
+ Collection to complete the transfer.
41
+
42
+ */
43
+
44
+ /// The main NFT contract interface. Other NFT contracts will
45
+ /// import and implement this interface
46
+ ///
47
+ pub contract interface NonFungibleToken {
48
+
49
+ /// The total number of tokens of this type in existence
50
+ pub var totalSupply: UInt64
51
+
52
+ /// Event that emitted when the NFT contract is initialized
53
+ ///
54
+ pub event ContractInitialized()
55
+
56
+ /// Event that is emitted when a token is withdrawn,
57
+ /// indicating the owner of the collection that it was withdrawn from.
58
+ ///
59
+ /// If the collection is not in an account's storage, `from` will be `nil`.
60
+ ///
61
+ pub event Withdraw(id: UInt64, from: Address?)
62
+
63
+ /// Event that emitted when a token is deposited to a collection.
64
+ ///
65
+ /// It indicates the owner of the collection that it was deposited to.
66
+ ///
67
+ pub event Deposit(id: UInt64, to: Address?)
68
+
69
+ /// Interface that the NFTs have to conform to
70
+ /// The metadata views methods are included here temporarily
71
+ /// because enforcing the metadata interfaces in the standard
72
+ /// would break many contracts in an upgrade. Those breaking changes
73
+ /// are being saved for the stable cadence milestone
74
+ ///
75
+ pub resource interface INFT {
76
+ /// The unique ID that each NFT has
77
+ pub let id: UInt64
78
+
79
+ /// Function that returns all the Metadata Views implemented by a Non Fungible Token
80
+ ///
81
+ /// @return An array of Types defining the implemented views. This value will be used by
82
+ /// developers to know which parameter to pass to the resolveView() method.
83
+ ///
84
+ pub fun getViews(): [Type] {
85
+ return []
86
+ }
87
+
88
+ /// Function that resolves a metadata view for this token.
89
+ ///
90
+ /// @param view: The Type of the desired view.
91
+ /// @return A structure representing the requested view.
92
+ ///
93
+ pub fun resolveView(_ view: Type): AnyStruct? {
94
+ return nil
95
+ }
96
+ }
97
+
98
+ /// Requirement that all conforming NFT smart contracts have
99
+ /// to define a resource called NFT that conforms to INFT
100
+ ///
101
+ pub resource NFT: INFT {
102
+ pub let id: UInt64
103
+ }
104
+
105
+ /// Interface to mediate withdraws from the Collection
106
+ ///
107
+ pub resource interface Provider {
108
+ /// Removes an NFT from the resource implementing it and moves it to the caller
109
+ ///
110
+ /// @param withdrawID: The ID of the NFT that will be removed
111
+ /// @return The NFT resource removed from the implementing resource
112
+ ///
113
+ pub fun withdraw(withdrawID: UInt64): @NFT {
114
+ post {
115
+ result.id == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
116
+ }
117
+ }
118
+ }
119
+
120
+ /// Interface to mediate deposits to the Collection
121
+ ///
122
+ pub resource interface Receiver {
123
+
124
+ /// Adds an NFT to the resource implementing it
125
+ ///
126
+ /// @param token: The NFT resource that will be deposited
127
+ ///
128
+ pub fun deposit(token: @NFT)
129
+ }
130
+
131
+ /// Interface that an account would commonly
132
+ /// publish for their collection
133
+ ///
134
+ pub resource interface CollectionPublic {
135
+ pub fun deposit(token: @NFT)
136
+ pub fun getIDs(): [UInt64]
137
+ pub fun borrowNFT(id: UInt64): &NFT
138
+ /// Safe way to borrow a reference to an NFT that does not panic
139
+ ///
140
+ /// @param id: The ID of the NFT that want to be borrowed
141
+ /// @return An optional reference to the desired NFT, will be nil if the passed id does not exist
142
+ ///
143
+ pub fun borrowNFTSafe(id: UInt64): &NFT? {
144
+ post {
145
+ result == nil || result!.id == id: "The returned reference's ID does not match the requested ID"
146
+ }
147
+ return nil
148
+ }
149
+ }
150
+
151
+ /// Requirement for the concrete resource type
152
+ /// to be declared in the implementing contract
153
+ ///
154
+ pub resource Collection: Provider, Receiver, CollectionPublic {
155
+
156
+ /// Dictionary to hold the NFTs in the Collection
157
+ pub var ownedNFTs: @{UInt64: NFT}
158
+
159
+ /// Removes an NFT from the collection and moves it to the caller
160
+ ///
161
+ /// @param withdrawID: The ID of the NFT that will be withdrawn
162
+ /// @return The resource containing the desired NFT
163
+ ///
164
+ pub fun withdraw(withdrawID: UInt64): @NFT
165
+
166
+ /// Takes a NFT and adds it to the collections dictionary
167
+ /// and adds the ID to the ID array
168
+ ///
169
+ /// @param token: An NFT resource
170
+ ///
171
+ pub fun deposit(token: @NFT)
172
+
173
+ /// Returns an array of the IDs that are in the collection
174
+ ///
175
+ /// @return An array containing all the IDs on the collection
176
+ ///
177
+ pub fun getIDs(): [UInt64]
178
+
179
+ /// Returns a borrowed reference to an NFT in the collection
180
+ /// so that the caller can read data and call methods from it
181
+ ///
182
+ /// @param id: The ID of the NFT that want to be borrowed
183
+ /// @return A reference to the NFT
184
+ ///
185
+ pub fun borrowNFT(id: UInt64): &NFT {
186
+ pre {
187
+ self.ownedNFTs[id] != nil: "NFT does not exist in the collection!"
188
+ }
189
+ }
190
+ }
191
+
192
+ /// Creates an empty Collection and returns it to the caller so that they can own NFTs
193
+ ///
194
+ /// @return A new Collection resource
195
+ ///
196
+ pub fun createEmptyCollection(): @Collection {
197
+ post {
198
+ result.getIDs().length == 0: "The created collection must be empty!"
199
+ }
200
+ }
201
+ }
202
+
@@ -0,0 +1,25 @@
1
+ // Taken from the NFT Metadata standard, this contract exposes an interface to let
2
+ // anyone borrow a contract and resolve views on it.
3
+ //
4
+ // This will allow you to obtain information about a contract without necessarily knowing anything about it.
5
+ // All you need is its address and name and you're good to go!
6
+ pub contract interface ViewResolver {
7
+ /// Function that returns all the Metadata Views implemented by the resolving contract
8
+ ///
9
+ /// @return An array of Types defining the implemented views. This value will be used by
10
+ /// developers to know which parameter to pass to the resolveView() method.
11
+ ///
12
+ pub fun getViews(): [Type] {
13
+ return []
14
+ }
15
+
16
+ /// Function that resolves a metadata view for this token.
17
+ ///
18
+ /// @param view: The Type of the desired view.
19
+ /// @return A structure representing the requested view.
20
+ ///
21
+ pub fun resolveView(_ view: Type): AnyStruct? {
22
+ return nil
23
+ }
24
+ }
25
+
@@ -0,0 +1,49 @@
1
+ const {getContractCode, getProjectConfig} = require("./utils")
2
+
3
+ // Read a contract from ./contracts and determine what other contracts need to be imported
4
+ const getImports = (contractName) => {
5
+ const projectConfig = getProjectConfig()
6
+ const contract = projectConfig.contracts[contractName]
7
+ if (!contract) {
8
+ throw new Error(`Contract "${contractName}" could not be found`)
9
+ }
10
+
11
+ const contractCode = getContractCode(contractName)
12
+ const linesWithImport = contractCode.match(/^import ".+/gm);
13
+ if (!linesWithImport) {
14
+ return []
15
+ }
16
+
17
+ // we need to split these up next so that we can get the contract name
18
+ // from each import statement
19
+ // import NonFungibleToken from "..."
20
+
21
+ const imports = {}
22
+
23
+ // keep track of all imports we find in all dependencies
24
+ const allImports = {}
25
+
26
+ if (!linesWithImport) {
27
+ return []
28
+ }
29
+
30
+ linesWithImport.forEach(line => {
31
+ const foundName = line.split(" ")[1].replace(/^"|"$/g, '')
32
+ imports[foundName] = true
33
+ allImports[foundName] = true
34
+ })
35
+
36
+ Object.keys(imports).forEach(importedContract => {
37
+ allImports[importedContract] = true
38
+ const subImports = getImports(importedContract)
39
+ for (const item of subImports) {
40
+ allImports[item] = true
41
+ }
42
+ })
43
+
44
+ return Object.keys(allImports)
45
+ }
46
+
47
+ module.exports = {
48
+ getImports
49
+ }
package/index.js ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const {add, addAll} = require("./add");
5
+ const {getDefaultConfigPath} = require("./utils");
6
+ const program = new Command();
7
+
8
+ program
9
+ .name("flow-contracts")
10
+ .description("A CLI to help manage and import Flow contracts")
11
+
12
+ program.command('add')
13
+ .description('Add a contract (and its dependencies) to your flow.json config')
14
+ .argument('<contractName>', 'The contract to be added')
15
+ .option('-c, --config <config>', 'File location of the config to be edited')
16
+ .option('-a, --account <account>', 'Account that will deploy this imported contract', 'emulator-account')
17
+ .action((contractName, options) => {
18
+ if(!options.config) {
19
+ options.config = getDefaultConfigPath()
20
+ }
21
+
22
+ add(
23
+ {
24
+ name: contractName,
25
+ ...options
26
+ })
27
+ });
28
+
29
+ program.command("add-all")
30
+ .description("Add all contracts to your flow.json config")
31
+ .option('-c, --config <config>', 'File location of the config to be edited')
32
+ .option('-a, --account <account>', 'Account to be used for signing', 'emulator-account')
33
+ .action(({config, account}) => {
34
+ if(!config) {
35
+ config = getDefaultConfigPath()
36
+ }
37
+
38
+ addAll(config, account)
39
+ })
40
+
41
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flowtyio/flow-contracts",
3
- "version": "0.0.2",
4
- "main": "index.json",
3
+ "version": "0.0.4",
4
+ "main": "index.json",
5
5
  "description": "An NPM package for common flow contracts",
6
6
  "author": "flowtyio",
7
7
  "license": "MIT",
@@ -10,14 +10,26 @@
10
10
  "url": "git+https://github.com/Flowtyio/flow-contracts.git"
11
11
  },
12
12
  "keywords": [
13
- "cadence",
14
- "nft",
15
- "NonFungibleToken",
16
- "flow"
13
+ "cadence",
14
+ "nft",
15
+ "NonFungibleToken",
16
+ "flow"
17
17
  ],
18
18
  "bugs": {
19
19
  "issues": "https://github.com/Flowtyio/flow-contracts/issues"
20
20
  },
21
- "homepage": "https://github.com/Flowtyio/flow-contracts/blob/main/README.md"
22
- }
23
-
21
+ "homepage": "https://github.com/Flowtyio/flow-contracts/blob/main/README.md",
22
+ "dependencies": {
23
+ "commander": "^11.0.0"
24
+ },
25
+ "bin": {
26
+ "flow-contracts": "./index.js"
27
+ },
28
+ "files": [
29
+ "*.js",
30
+ "README.md",
31
+ "index.json",
32
+ "contracts/*.cdc",
33
+ "LICENSE"
34
+ ]
35
+ }
package/utils.js ADDED
@@ -0,0 +1,38 @@
1
+ const fs = require("fs");
2
+
3
+ const getDefaultConfigPath = () => {
4
+ const currentWorkingDirectory = process.cwd();
5
+ return `${currentWorkingDirectory}/flow.json`
6
+ }
7
+
8
+ const getConfig = (path) => {
9
+ const data = fs.readFileSync(path, 'utf8')
10
+ try {
11
+ return JSON.parse(data)
12
+ } catch (parseError) {
13
+ throw new Error(`Failed to parse config file ${parseError}`)
14
+ }
15
+ }
16
+
17
+ const getContractCode = (contractName) => {
18
+ const path = `${__dirname}/contracts/${contractName}.cdc`
19
+ return fs.readFileSync(path, 'utf8')
20
+ }
21
+
22
+ const writeConfig = (path, config) => {
23
+ const jsonData = JSON.stringify(config, null, 2)
24
+ fs.writeFileSync(path, jsonData);
25
+ }
26
+
27
+ const getProjectConfig = () => {
28
+ const configLocation = `${__dirname}/flow.json`
29
+ return getConfig(configLocation)
30
+ }
31
+
32
+ module.exports = {
33
+ getContractCode,
34
+ getDefaultConfigPath,
35
+ getConfig,
36
+ getProjectConfig,
37
+ writeConfig
38
+ }