@flowtyio/flow-contracts 0.0.15 → 0.0.17

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
@@ -57,6 +57,11 @@ Currently, the list includes:
57
57
  | TokenForwarding | 0x51ea0e37c27a1f1a | 0xe544175ee0461c4b |
58
58
  | DapperUtilityCoin | 0x82ec283f88a62e65 | 0xead892083b3e2c6c |
59
59
  | FlowUtilityToken | 0x82ec283f88a62e65 | 0xead892083b3e2c6c |
60
+ | ArrayUtils| 0x31ad40c07a2a9788 | 0xa340dc0a4ec828ab |
61
+ | StringUtils| 0x31ad40c07a2a9788 | 0xa340dc0a4ec828ab |
62
+ | AddressUtils | 0x31ad40c07a2a9788 | 0xa340dc0a4ec828ab |
63
+ | ScopedNFTProviders | 0x31ad40c07a2a9788 | 0xa340dc0a4ec828ab |
64
+ | ScopedFTProviders | 0x31ad40c07a2a9788 | 0xa340dc0a4ec828ab |
60
65
 
61
66
 
62
67
  ## Using a contract
@@ -0,0 +1,109 @@
1
+ import "StringUtils"
2
+
3
+ pub contract AddressUtils {
4
+
5
+ pub fun withoutPrefix(_ input: String): String {
6
+ var address = input
7
+
8
+ // get rid of 0x
9
+ if address.length > 1 && address.utf8[1] == 120 {
10
+ address = address.slice(from: 2, upTo: address.length)
11
+ }
12
+
13
+ // ensure even length
14
+ if address.length % 2 == 1 {
15
+ address = "0".concat(address)
16
+ }
17
+ return address
18
+ }
19
+
20
+ pub fun parseUInt64(_ input: AnyStruct): UInt64? {
21
+ var stringValue = ""
22
+
23
+ if let string = input as? String {
24
+ stringValue = string
25
+ } else if let address = input as? Address {
26
+ stringValue = address.toString()
27
+ } else if let type = input as? Type {
28
+ let parts = StringUtils.split(type.identifier, ".")
29
+ if parts.length == 1 {
30
+ return nil
31
+ }
32
+ stringValue = parts[1]
33
+ } else {
34
+ return nil
35
+ }
36
+
37
+ let address = self.withoutPrefix(stringValue)
38
+ let bytes = address.decodeHex()
39
+ var r: UInt64 = 0
40
+ var length = bytes.length
41
+ for byte in bytes {
42
+ length = length - 1
43
+ r = r + (UInt64(byte) << UInt64(length * 8))
44
+ }
45
+ return r
46
+ }
47
+
48
+ pub fun parseAddress(_ input: AnyStruct): Address? {
49
+ if let parsed = self.parseUInt64(input) {
50
+ return Address(parsed)
51
+ }
52
+ return nil
53
+ }
54
+
55
+ pub fun isValidAddress(_ input: AnyStruct, forNetwork: String): Bool {
56
+ let address = self.parseUInt64(input)
57
+ if address == nil {
58
+ return false
59
+ }
60
+
61
+ let codeWords: {String: UInt64} = {
62
+ "MAINNET" : 0,
63
+ "TESTNET" : 0x6834ba37b3980209,
64
+ "EMULATOR": 0x1cb159857af02018
65
+ }
66
+
67
+ let parityCheckMatrixColumns: [UInt64] = [
68
+ 0x00001, 0x00002, 0x00004, 0x00008, 0x00010, 0x00020, 0x00040, 0x00080,
69
+ 0x00100, 0x00200, 0x00400, 0x00800, 0x01000, 0x02000, 0x04000, 0x08000,
70
+ 0x10000, 0x20000, 0x40000, 0x7328d, 0x6689a, 0x6112f, 0x6084b, 0x433fd,
71
+ 0x42aab, 0x41951, 0x233ce, 0x22a81, 0x21948, 0x1ef60, 0x1deca, 0x1c639,
72
+ 0x1bdd8, 0x1a535, 0x194ac, 0x18c46, 0x1632b, 0x1529b, 0x14a43, 0x13184,
73
+ 0x12942, 0x118c1, 0x0f812, 0x0e027, 0x0d00e, 0x0c83c, 0x0b01d, 0x0a831,
74
+ 0x0982b, 0x07034, 0x0682a, 0x05819, 0x03807, 0x007d2, 0x00727, 0x0068e,
75
+ 0x0067c, 0x0059d, 0x004eb, 0x003b4, 0x0036a, 0x002d9, 0x001c7, 0x0003f
76
+ ]
77
+
78
+ var parity: UInt64 = 0
79
+ var codeWord: UInt64 = codeWords[forNetwork]!
80
+ codeWord = codeWord ^ address!
81
+
82
+ if codeWord == 0 {
83
+ return false
84
+ }
85
+
86
+ for column in parityCheckMatrixColumns{
87
+ if codeWord & 1 == 1 {
88
+ parity = parity ^ column
89
+ }
90
+ codeWord = codeWord >> 1
91
+ }
92
+
93
+ return parity == 0 && codeWord == 0
94
+ }
95
+
96
+ pub fun getNetworkFromAddress(_ input: AnyStruct): String? {
97
+ for network in ["MAINNET", "TESTNET", "EMULATOR"]{
98
+ if self.isValidAddress(input, forNetwork: network){
99
+ return network
100
+ }
101
+ }
102
+ return nil
103
+ }
104
+
105
+ pub fun currentNetwork(): String {
106
+ return self.getNetworkFromAddress(self.account.address)!
107
+ }
108
+
109
+ }
@@ -0,0 +1,69 @@
1
+ // Copied from https://github.com/bluesign/flow-utils/blob/dnz/cadence/contracts/ArrayUtils.cdc with minor adjustments
2
+
3
+ pub contract ArrayUtils {
4
+
5
+ pub fun rangeFunc(_ start: Int, _ end: Int, _ f: ((Int): Void)) {
6
+ var current = start
7
+ while current < end {
8
+ f(current)
9
+ current = current + 1
10
+ }
11
+ }
12
+
13
+ pub fun range(_ start: Int, _ end: Int): [Int] {
14
+ var res: [Int] = []
15
+ self.rangeFunc(start, end, fun (i: Int) {
16
+ res.append(i)
17
+ })
18
+ return res
19
+ }
20
+
21
+ pub fun reverse(_ array: [Int]): [Int] {
22
+ var res: [Int] = []
23
+ var i: Int = array.length - 1
24
+ while i >= 0 {
25
+ res.append(array[i])
26
+ i = i - 1
27
+ }
28
+ return res
29
+ }
30
+
31
+ pub fun transform(_ array: &[AnyStruct], _ f : ((AnyStruct): AnyStruct)){
32
+ for i in self.range(0, array.length){
33
+ array[i] = f(array[i])
34
+ }
35
+ }
36
+
37
+ pub fun iterate(_ array: [AnyStruct], _ f : ((AnyStruct): Bool)){
38
+ for item in array{
39
+ if !f(item){
40
+ break
41
+ }
42
+ }
43
+ }
44
+
45
+ pub fun map(_ array: [AnyStruct], _ f : ((AnyStruct): AnyStruct)) : [AnyStruct] {
46
+ var res : [AnyStruct] = []
47
+ for item in array{
48
+ res.append(f(item))
49
+ }
50
+ return res
51
+ }
52
+
53
+ pub fun mapStrings(_ array: [String], _ f: ((String) : String) ) : [String] {
54
+ var res : [String] = []
55
+ for item in array{
56
+ res.append(f(item))
57
+ }
58
+ return res
59
+ }
60
+
61
+ pub fun reduce(_ array: [AnyStruct], _ initial: AnyStruct, _ f : ((AnyStruct, AnyStruct): AnyStruct)) : AnyStruct{
62
+ var res: AnyStruct = f(initial, array[0])
63
+ for i in self.range(1, array.length){
64
+ res = f(res, array[i])
65
+ }
66
+ return res
67
+ }
68
+
69
+ }
@@ -0,0 +1,120 @@
1
+ import "FungibleToken"
2
+ import "StringUtils"
3
+
4
+ // ScopedFTProviders
5
+ //
6
+ // TO AVOID RISK, PLEASE DEPLOY YOUR OWN VERSION OF THIS CONTRACT SO THAT
7
+ // MALICIOUS UPDATES ARE NOT POSSIBLE
8
+ //
9
+ // ScopedProviders are meant to solve the issue of unbounded access FungibleToken vaults
10
+ // when a provider is called for.
11
+ pub contract ScopedFTProviders {
12
+ pub struct interface FTFilter {
13
+ pub fun canWithdrawAmount(_ amount: UFix64): Bool
14
+ pub fun markAmountWithdrawn(_ amount: UFix64)
15
+ pub fun getDetails(): {String: AnyStruct}
16
+ }
17
+
18
+ pub struct AllowanceFilter: FTFilter {
19
+ access(self) let allowance: UFix64
20
+ access(self) var allowanceUsed: UFix64
21
+
22
+ init(_ allowance: UFix64) {
23
+ self.allowance = allowance
24
+ self.allowanceUsed = 0.0
25
+ }
26
+
27
+ pub fun canWithdrawAmount(_ amount: UFix64): Bool {
28
+ return amount + self.allowanceUsed <= self.allowance
29
+ }
30
+
31
+ pub fun markAmountWithdrawn(_ amount: UFix64) {
32
+ self.allowanceUsed = self.allowanceUsed + amount
33
+ }
34
+
35
+ pub fun getDetails(): {String: AnyStruct} {
36
+ return {
37
+ "allowance": self.allowance,
38
+ "allowanceUsed": self.allowanceUsed
39
+ }
40
+ }
41
+ }
42
+
43
+ // ScopedFTProvider
44
+ //
45
+ // A ScopedFTProvider is a wrapped FungibleTokenProvider with
46
+ // filters that can be defined by anyone using the ScopedFTProvider.
47
+ pub resource ScopedFTProvider: FungibleToken.Provider {
48
+ access(self) let provider: Capability<&{FungibleToken.Provider}>
49
+ access(self) var filters: [{FTFilter}]
50
+
51
+ // block timestamp that this provider can no longer be used after
52
+ access(self) let expiration: UFix64?
53
+
54
+ pub init(provider: Capability<&{FungibleToken.Provider}>, filters: [{FTFilter}], expiration: UFix64?) {
55
+ self.provider = provider
56
+ self.filters = filters
57
+ self.expiration = expiration
58
+ }
59
+
60
+ pub fun check(): Bool {
61
+ return self.provider.check()
62
+ }
63
+
64
+ pub fun isExpired(): Bool {
65
+ if let expiration = self.expiration {
66
+ return getCurrentBlock().timestamp >= expiration
67
+ }
68
+ return false
69
+ }
70
+
71
+ pub fun canWithdraw(_ amount: UFix64): Bool {
72
+ if self.isExpired() {
73
+ return false
74
+ }
75
+
76
+ for filter in self.filters {
77
+ if !filter.canWithdrawAmount(amount) {
78
+ return false
79
+ }
80
+ }
81
+
82
+ return true
83
+ }
84
+
85
+ pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
86
+ pre {
87
+ !self.isExpired(): "provider has expired"
88
+ }
89
+
90
+ var i = 0
91
+ while i < self.filters.length {
92
+ if !self.filters[i].canWithdrawAmount(amount) {
93
+ panic(StringUtils.join(["cannot withdraw tokens. filter of type", self.filters[i].getType().identifier, "failed."], " "))
94
+ }
95
+
96
+ self.filters[i].markAmountWithdrawn(amount)
97
+ i = i + 1
98
+ }
99
+
100
+ return <-self.provider.borrow()!.withdraw(amount: amount)
101
+ }
102
+
103
+ pub fun getDetails(): [{String: AnyStruct}] {
104
+ let details: [{String: AnyStruct}] = []
105
+ for filter in self.filters {
106
+ details.append(filter.getDetails())
107
+ }
108
+
109
+ return details
110
+ }
111
+ }
112
+
113
+ pub fun createScopedFTProvider(
114
+ provider: Capability<&{FungibleToken.Provider}>,
115
+ filters: [{FTFilter}],
116
+ expiration: UFix64?
117
+ ): @ScopedFTProvider {
118
+ return <- create ScopedFTProvider(provider: provider, filters: filters, expiration: expiration)
119
+ }
120
+ }
@@ -0,0 +1,162 @@
1
+ import "NonFungibleToken"
2
+ import "StringUtils"
3
+
4
+ // ScopedNFTProviders
5
+ //
6
+ // TO AVOID RISK, PLEASE DEPLOY YOUR OWN VERSION OF THIS CONTRACT SO THAT
7
+ // MALICIOUS UPDATES ARE NOT POSSIBLE
8
+ //
9
+ // ScopedNFTProviders are meant to solve the issue of unbounded access to NFT Collections.
10
+ // A provider can be given extensible filters which allow limited access to resources based on any trait on the NFT itself.
11
+ //
12
+ // By using a scoped provider, only a subset of assets can be taken if the provider leaks
13
+ // instead of the entire nft collection.
14
+ pub contract ScopedNFTProviders {
15
+ pub struct interface NFTFilter {
16
+ pub fun canWithdraw(_ nft: &NonFungibleToken.NFT): Bool
17
+ pub fun markWithdrawn(_ nft: &NonFungibleToken.NFT)
18
+ pub fun getDetails(): {String: AnyStruct}
19
+ }
20
+
21
+ pub struct NFTIDFilter: NFTFilter {
22
+ // the ids that are allowed to be withdrawn.
23
+ // If ids[num] is false, the id cannot be withdrawn anymore
24
+ access(self) let ids: {UInt64: Bool}
25
+
26
+ init(_ ids: [UInt64]) {
27
+ let d: {UInt64: Bool} = {}
28
+ for i in ids {
29
+ d[i] = true
30
+ }
31
+ self.ids = d
32
+ }
33
+
34
+ pub fun canWithdraw(_ nft: &NonFungibleToken.NFT): Bool {
35
+ return self.ids[nft.id] != nil && self.ids[nft.id] == true
36
+ }
37
+
38
+ pub fun markWithdrawn(_ nft: &NonFungibleToken.NFT) {
39
+ self.ids[nft.id] = false
40
+ }
41
+
42
+ pub fun getDetails(): {String: AnyStruct} {
43
+ return {
44
+ "ids": self.ids
45
+ }
46
+ }
47
+ }
48
+
49
+ pub struct UUIDFilter: NFTFilter {
50
+ // the ids that are allowed to be withdrawn.
51
+ // If ids[num] is false, the id cannot be withdrawn anymore
52
+ access(self) let uuids: {UInt64: Bool}
53
+
54
+ init(_ uuids: [UInt64]) {
55
+ let d: {UInt64: Bool} = {}
56
+ for i in uuids {
57
+ d[i] = true
58
+ }
59
+ self.uuids = d
60
+ }
61
+
62
+ pub fun canWithdraw(_ nft: &NonFungibleToken.NFT): Bool {
63
+ return self.uuids[nft.uuid] != nil && self.uuids[nft.uuid]! == true
64
+ }
65
+
66
+ pub fun markWithdrawn(_ nft: &NonFungibleToken.NFT) {
67
+ self.uuids[nft.uuid] = false
68
+ }
69
+
70
+ pub fun getDetails(): {String: AnyStruct} {
71
+ return {
72
+ "uuids": self.uuids
73
+ }
74
+ }
75
+ }
76
+
77
+ // ScopedNFTProvider
78
+ //
79
+ // Wrapper around an NFT Provider that is restricted to specific ids.
80
+ pub resource ScopedNFTProvider: NonFungibleToken.Provider {
81
+ access(self) let provider: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
82
+ access(self) let filters: [{NFTFilter}]
83
+
84
+ // block timestamp that this provider can no longer be used after
85
+ access(self) let expiration: UFix64?
86
+
87
+ pub fun isExpired(): Bool {
88
+ if let expiration = self.expiration {
89
+ return getCurrentBlock().timestamp >= expiration
90
+ }
91
+ return false
92
+ }
93
+
94
+ pub init(provider: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>, filters: [{NFTFilter}], expiration: UFix64?) {
95
+ self.provider = provider
96
+ self.expiration = expiration
97
+ self.filters = filters
98
+ }
99
+
100
+ pub fun canWithdraw(_ id: UInt64): Bool {
101
+ if self.isExpired() {
102
+ return false
103
+ }
104
+
105
+ let nft = self.provider.borrow()!.borrowNFT(id: id)
106
+ if nft == nil {
107
+ return false
108
+ }
109
+
110
+ var i = 0
111
+ while i < self.filters.length {
112
+ if !self.filters[i].canWithdraw(nft) {
113
+ return false
114
+ }
115
+ i = i + 1
116
+ }
117
+ return true
118
+ }
119
+
120
+ pub fun check(): Bool {
121
+ return self.provider.check()
122
+ }
123
+
124
+ pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT {
125
+ pre {
126
+ !self.isExpired(): "provider has expired"
127
+ }
128
+
129
+ let nft <- self.provider.borrow()!.withdraw(withdrawID: withdrawID)
130
+ let ref = &nft as &NonFungibleToken.NFT
131
+
132
+ var i = 0
133
+ while i < self.filters.length {
134
+ if !self.filters[i].canWithdraw(ref) {
135
+ panic(StringUtils.join(["cannot withdraw nft. filter of type", self.filters[i].getType().identifier, "failed."], " "))
136
+ }
137
+
138
+ self.filters[i].markWithdrawn(ref)
139
+ i = i + 1
140
+ }
141
+
142
+ return <-nft
143
+ }
144
+
145
+ pub fun getDetails(): [{String: AnyStruct}] {
146
+ let details: [{String: AnyStruct}] = []
147
+ for f in self.filters {
148
+ details.append(f.getDetails())
149
+ }
150
+
151
+ return details
152
+ }
153
+ }
154
+
155
+ pub fun createScopedNFTProvider(
156
+ provider: Capability<&{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>,
157
+ filters: [{NFTFilter}],
158
+ expiration: UFix64?
159
+ ): @ScopedNFTProvider {
160
+ return <- create ScopedNFTProvider(provider: provider, filters: filters, expiration: expiration)
161
+ }
162
+ }
@@ -0,0 +1,97 @@
1
+ import "ArrayUtils"
2
+
3
+ pub contract StringUtils {
4
+
5
+ pub fun format(_ s: String, _ args: {String:String}): String{
6
+ var formatted = s
7
+ for key in args.keys{
8
+ formatted = StringUtils.replaceAll(formatted, "{".concat(key).concat("}"), args[key]!)
9
+ }
10
+ return formatted
11
+ }
12
+
13
+ pub fun explode(_ s: String): [String]{
14
+ var chars : [String] = []
15
+ for i in ArrayUtils.range(0, s.length){
16
+ chars.append(s[i].toString())
17
+ }
18
+ return chars
19
+ }
20
+
21
+ pub fun trimLeft(_ s: String): String{
22
+ for i in ArrayUtils.range(0, s.length){
23
+ if s[i] != " "{
24
+ return s.slice(from: i, upTo: s.length)
25
+ }
26
+ }
27
+ return ""
28
+ }
29
+
30
+ pub fun trim(_ s: String): String{
31
+ return self.trimLeft(s)
32
+ }
33
+
34
+ pub fun replaceAll(_ s: String, _ search: String, _ replace: String): String{
35
+ return self.join(self.split(s, search), replace)
36
+ }
37
+
38
+ pub fun hasPrefix(_ s: String, _ prefix: String) : Bool{
39
+ return s.length >= prefix.length && s.slice(from:0, upTo: prefix.length)==prefix
40
+ }
41
+
42
+ pub fun hasSuffix(_ s: String, _ suffix: String) : Bool{
43
+ return s.length >= suffix.length && s.slice(from:s.length-suffix.length, upTo: s.length)==suffix
44
+ }
45
+
46
+ pub fun index(_ s : String, _ substr : String, _ startIndex: Int): Int?{
47
+ for i in ArrayUtils.range(startIndex,s.length-substr.length+1){
48
+ if s[i]==substr[0] && s.slice(from:i, upTo:i+substr.length) == substr{
49
+ return i
50
+ }
51
+ }
52
+ return nil
53
+ }
54
+
55
+ pub fun count(_ s: String, _ substr: String): Int{
56
+ var pos = [self.index(s, substr, 0)]
57
+ while pos[0]!=nil {
58
+ pos.insert(at:0, self.index(s, substr, pos[0]!+pos.length*substr.length+1))
59
+ }
60
+ return pos.length-1
61
+ }
62
+
63
+ pub fun contains(_ s: String, _ substr: String): Bool {
64
+ if let index = self.index(s, substr, 0) {
65
+ return true
66
+ }
67
+ return false
68
+ }
69
+
70
+ pub fun substringUntil(_ s: String, _ until: String, _ startIndex: Int): String{
71
+ if let index = self.index( s, until, startIndex){
72
+ return s.slice(from:startIndex, upTo: index)
73
+ }
74
+ return s.slice(from:startIndex, upTo:s.length)
75
+ }
76
+
77
+ pub fun split(_ s: String, _ delimiter: String): [String] {
78
+ let segments: [String] = []
79
+ var p = 0
80
+ while p<=s.length{
81
+ var preDelimiter = self.substringUntil(s, delimiter, p)
82
+ segments.append(preDelimiter)
83
+ p = p + preDelimiter.length + delimiter.length
84
+ }
85
+ return segments
86
+ }
87
+
88
+ pub fun join(_ strs: [String], _ separator: String): String {
89
+ var joinedStr = ""
90
+ for s in strs {
91
+ joinedStr = joinedStr.concat(s).concat(separator)
92
+ }
93
+ return joinedStr.slice(from: 0, upTo: joinedStr.length - separator.length)
94
+ }
95
+
96
+
97
+ }
package/flow.json CHANGED
@@ -267,6 +267,46 @@
267
267
  "testnet": "0x8a5f647e58dde1ee",
268
268
  "mainnet": "0xb8ea91944fd51c43"
269
269
  }
270
+ },
271
+ "ArrayUtils": {
272
+ "source": "./contracts/flow-utils/ArrayUtils.cdc",
273
+ "aliases": {
274
+ "emulator": "0xf8d6e0586b0a20c7",
275
+ "testnet": "0x31ad40c07a2a9788",
276
+ "mainnet": "0xa340dc0a4ec828ab"
277
+ }
278
+ },
279
+ "StringUtils": {
280
+ "source": "./contracts/flow-utils/StringUtils.cdc",
281
+ "aliases": {
282
+ "emulator": "0xf8d6e0586b0a20c7",
283
+ "testnet": "0x31ad40c07a2a9788",
284
+ "mainnet": "0xa340dc0a4ec828ab"
285
+ }
286
+ },
287
+ "AddressUtils": {
288
+ "source": "./contracts/flow-utils/AddressUtils.cdc",
289
+ "aliases": {
290
+ "emulator": "0xf8d6e0586b0a20c7",
291
+ "testnet": "0x31ad40c07a2a9788",
292
+ "mainnet": "0xa340dc0a4ec828ab"
293
+ }
294
+ },
295
+ "ScopedFTProviders": {
296
+ "source": "./contracts/flow-utils/ScopedFTProviders.cdc",
297
+ "aliases": {
298
+ "emulator": "0xf8d6e0586b0a20c7",
299
+ "testnet": "0x31ad40c07a2a9788",
300
+ "mainnet": "0xa340dc0a4ec828ab"
301
+ }
302
+ },
303
+ "ScopedNFTProviders": {
304
+ "source": "./contracts/flow-utils/ScopedNFTProviders.cdc",
305
+ "aliases": {
306
+ "emulator": "0xf8d6e0586b0a20c7",
307
+ "testnet": "0x31ad40c07a2a9788",
308
+ "mainnet": "0xa340dc0a4ec828ab"
309
+ }
270
310
  }
271
311
  },
272
312
  "deployments": {
@@ -288,7 +328,12 @@
288
328
  "DapperOffersV2",
289
329
  "OffersV2",
290
330
  "TopShot",
291
- "TopShotLocking"
331
+ "TopShotLocking",
332
+ "ArrayUtils",
333
+ "StringUtils",
334
+ "AddressUtils",
335
+ "ScopedNFTProviders",
336
+ "ScopedFTProviders"
292
337
  ],
293
338
  "emulator-ft": [
294
339
  "FungibleToken",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowtyio/flow-contracts",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "main": "index.json",
5
5
  "description": "An NPM package for common flow contracts",
6
6
  "author": "flowtyio",