@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 +5 -0
- package/contracts/flow-utils/AddressUtils.cdc +109 -0
- package/contracts/flow-utils/ArrayUtils.cdc +69 -0
- package/contracts/flow-utils/ScopedFTProviders.cdc +120 -0
- package/contracts/flow-utils/ScopedNFTProviders.cdc +162 -0
- package/contracts/flow-utils/StringUtils.cdc +97 -0
- package/flow.json +46 -1
- package/package.json +1 -1
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",
|