@flowtyio/flow-contracts 0.1.0-beta.8 → 0.1.0
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 +1 -1
- package/contracts/FungibleTokenSwitchboard.cdc +360 -0
- package/contracts/MetadataViews.cdc +79 -6
- package/contracts/NonFungibleToken.cdc +17 -10
- package/contracts/TokenForwarding.cdc +19 -11
- package/contracts/capability-cache/CapabilityCache.cdc +97 -0
- package/contracts/dapper/TopShot.cdc +323 -259
- package/contracts/dapper/TopShotLocking.cdc +41 -15
- package/contracts/dapper/offers/DapperOffersV2.cdc +46 -43
- package/contracts/dapper/offers/OffersV2.cdc +40 -56
- package/contracts/dapper/offers/Resolver.cdc +20 -13
- package/contracts/emerald-city/FLOAT.cdc +259 -254
- package/contracts/example/ExampleNFT.cdc +2 -2
- package/contracts/find/FindViews.cdc +357 -353
- package/contracts/flow-utils/ScopedFTProviders.cdc +5 -2
- package/contracts/flow-utils/ScopedNFTProviders.cdc +6 -2
- package/contracts/flowty-drops/ContractManager.cdc +73 -0
- package/contracts/flowty-drops/DropFactory.cdc +75 -0
- package/contracts/flowty-drops/DropTypes.cdc +282 -0
- package/contracts/flowty-drops/FlowtyActiveCheckers.cdc +113 -0
- package/contracts/flowty-drops/FlowtyAddressVerifiers.cdc +64 -0
- package/contracts/flowty-drops/FlowtyDrops.cdc +461 -0
- package/contracts/flowty-drops/FlowtyPricers.cdc +48 -0
- package/contracts/flowty-drops/initializers/ContractBorrower.cdc +14 -0
- package/contracts/flowty-drops/initializers/ContractInitializer.cdc +7 -0
- package/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +57 -0
- package/contracts/flowty-drops/nft/BaseCollection.cdc +97 -0
- package/contracts/flowty-drops/nft/BaseNFT.cdc +107 -0
- package/contracts/flowty-drops/nft/ContractFactory.cdc +13 -0
- package/contracts/flowty-drops/nft/ContractFactoryTemplate.cdc +48 -0
- package/contracts/flowty-drops/nft/NFTMetadata.cdc +140 -0
- package/contracts/flowty-drops/nft/OpenEditionNFT.cdc +42 -0
- package/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +54 -0
- package/contracts/flowty-drops/nft/UniversalCollection.cdc +29 -0
- package/contracts/fungible-token-router/FungibleTokenRouter.cdc +103 -0
- package/contracts/hybrid-custody/CapabilityDelegator.cdc +28 -26
- package/contracts/hybrid-custody/CapabilityFactory.cdc +20 -18
- package/contracts/hybrid-custody/CapabilityFilter.cdc +41 -24
- package/contracts/hybrid-custody/HybridCustody.cdc +303 -242
- package/contracts/hybrid-custody/factories/FTAllFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTProviderFactory.cdc +17 -5
- package/contracts/hybrid-custody/factories/FTReceiverBalanceFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTReceiverFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/FTVaultFactory.cdc +46 -0
- package/contracts/hybrid-custody/factories/NFTCollectionFactory.cdc +45 -0
- package/contracts/hybrid-custody/factories/NFTCollectionPublicFactory.cdc +16 -4
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionFactory.cdc +22 -0
- package/contracts/hybrid-custody/factories/NFTProviderFactory.cdc +16 -4
- package/contracts/lost-and-found/LostAndFound.cdc +21 -17
- package/flow.json +181 -7
- package/package.json +1 -1
- package/contracts/hybrid-custody/factories/NFTProviderAndCollectionPublicFactory.cdc +0 -10
|
@@ -57,6 +57,10 @@ access(all) contract ScopedFTProviders {
|
|
|
57
57
|
self.expiration = expiration
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
access(all) fun getProviderType(): Type {
|
|
61
|
+
return self.provider.borrow()!.getType()
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
access(all) fun check(): Bool {
|
|
61
65
|
return self.provider.check()
|
|
62
66
|
}
|
|
@@ -86,7 +90,7 @@ access(all) contract ScopedFTProviders {
|
|
|
86
90
|
return self.canWithdraw(amount)
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
access(FungibleToken.Withdraw
|
|
93
|
+
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @{FungibleToken.Vault} {
|
|
90
94
|
pre {
|
|
91
95
|
!self.isExpired(): "provider has expired"
|
|
92
96
|
}
|
|
@@ -122,4 +126,3 @@ access(all) contract ScopedFTProviders {
|
|
|
122
126
|
return <- create ScopedFTProvider(provider: provider, filters: filters, expiration: expiration)
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
|
-
|
|
@@ -102,7 +102,11 @@ access(all) contract ScopedNFTProviders {
|
|
|
102
102
|
return false
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
if !self.provider.check() {
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let nft: &{NonFungibleToken.NFT}? = self.provider.borrow()!.borrowNFT(id)
|
|
106
110
|
if nft == nil {
|
|
107
111
|
return false
|
|
108
112
|
}
|
|
@@ -121,7 +125,7 @@ access(all) contract ScopedNFTProviders {
|
|
|
121
125
|
return self.provider.check()
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
access(NonFungibleToken.Withdraw
|
|
128
|
+
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
|
|
125
129
|
pre {
|
|
126
130
|
!self.isExpired(): "provider has expired"
|
|
127
131
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import "FlowToken"
|
|
2
|
+
import "FungibleToken"
|
|
3
|
+
import "FungibleTokenRouter"
|
|
4
|
+
|
|
5
|
+
access(all) contract ContractManager {
|
|
6
|
+
access(all) let StoragePath: StoragePath
|
|
7
|
+
access(all) let PublicPath: PublicPath
|
|
8
|
+
|
|
9
|
+
access(all) entitlement Manage
|
|
10
|
+
|
|
11
|
+
access(all) resource Manager {
|
|
12
|
+
access(self) let acct: Capability<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>
|
|
13
|
+
access(self) let routerCap: Capability<auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router>
|
|
14
|
+
|
|
15
|
+
access(all) let data: {String: AnyStruct}
|
|
16
|
+
access(all) let resources: @{String: AnyResource}
|
|
17
|
+
|
|
18
|
+
access(Manage) fun borrowContractAccount(): auth(Contracts) &Account {
|
|
19
|
+
return self.acct.borrow()!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
access(Manage) fun addOverride(type: Type, addr: Address) {
|
|
23
|
+
let router = self.routerCap.borrow() ?? panic("fungible token router is not valid")
|
|
24
|
+
router.addOverride(type: type, addr: addr)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
access(Manage) fun getSwitchboard(): auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router {
|
|
28
|
+
return self.routerCap.borrow()!
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
access(all) fun addFlowTokensToAccount(_ tokens: @FlowToken.Vault) {
|
|
32
|
+
self.acct.borrow()!.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
access(all) fun getAccount(): &Account {
|
|
36
|
+
return getAccount(self.acct.address)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
init(tokens: @FlowToken.Vault, defaultRouterAddress: Address) {
|
|
40
|
+
pre {
|
|
41
|
+
tokens.balance >= 0.001: "minimum balance of 0.001 required for initialization"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let acct = Account(payer: ContractManager.account)
|
|
45
|
+
self.acct = acct.capabilities.account.issue<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>()
|
|
46
|
+
assert(self.acct.check(), message: "failed to setup account capability")
|
|
47
|
+
|
|
48
|
+
acct.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)
|
|
49
|
+
|
|
50
|
+
let router <- FungibleTokenRouter.createRouter(defaultAddress: defaultRouterAddress)
|
|
51
|
+
acct.storage.save(<-router, to: FungibleTokenRouter.StoragePath)
|
|
52
|
+
|
|
53
|
+
let receiver = acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenRouter.StoragePath)
|
|
54
|
+
assert(receiver.check(), message: "invalid switchboard receiver capability")
|
|
55
|
+
acct.capabilities.publish(receiver, at: FungibleTokenRouter.PublicPath)
|
|
56
|
+
|
|
57
|
+
self.routerCap = acct.capabilities.storage.issue<auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router>(FungibleTokenRouter.StoragePath)
|
|
58
|
+
|
|
59
|
+
self.data = {}
|
|
60
|
+
self.resources <- {}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
access(all) fun createManager(tokens: @FlowToken.Vault, defaultRouterAddress: Address): @Manager {
|
|
65
|
+
return <- create Manager(tokens: <- tokens, defaultRouterAddress: defaultRouterAddress)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
init() {
|
|
69
|
+
let identifier = "ContractManager_".concat(self.account.address.toString())
|
|
70
|
+
self.StoragePath = StoragePath(identifier: identifier)!
|
|
71
|
+
self.PublicPath = PublicPath(identifier: identifier)!
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import "FungibleToken"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
|
|
4
|
+
import "FlowtyDrops"
|
|
5
|
+
import "FlowtyActiveCheckers"
|
|
6
|
+
import "FlowtyAddressVerifiers"
|
|
7
|
+
import "FlowtyPricers"
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
The DropFactory is a contract that helps create common types of drops
|
|
11
|
+
*/
|
|
12
|
+
access(all) contract DropFactory {
|
|
13
|
+
access(all) fun createEndlessOpenEditionDrop(
|
|
14
|
+
price: UFix64,
|
|
15
|
+
paymentTokenType: Type,
|
|
16
|
+
dropDisplay: MetadataViews.Display,
|
|
17
|
+
minterCap: Capability<&{FlowtyDrops.Minter}>,
|
|
18
|
+
nftTypeIdentifier: String
|
|
19
|
+
): @FlowtyDrops.Drop {
|
|
20
|
+
pre {
|
|
21
|
+
paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// This drop is always on and never ends.
|
|
25
|
+
let activeChecker = FlowtyActiveCheckers.AlwaysOn()
|
|
26
|
+
|
|
27
|
+
// All addresses are allowed to participate
|
|
28
|
+
let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10)
|
|
29
|
+
|
|
30
|
+
// The cost of each mint is the same, and only permits one token type as payment
|
|
31
|
+
let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType)
|
|
32
|
+
|
|
33
|
+
let phaseDetails = FlowtyDrops.PhaseDetails(activeChecker: activeChecker, display: nil, pricer: pricer, addressVerifier: addressVerifier)
|
|
34
|
+
let phase <- FlowtyDrops.createPhase(details: phaseDetails)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
|
|
38
|
+
let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
|
|
39
|
+
let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
|
|
40
|
+
|
|
41
|
+
return <- drop
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
access(all) fun createTimeBasedOpenEditionDrop(
|
|
45
|
+
price: UFix64,
|
|
46
|
+
paymentTokenType: Type,
|
|
47
|
+
dropDisplay: MetadataViews.Display,
|
|
48
|
+
minterCap: Capability<&{FlowtyDrops.Minter}>,
|
|
49
|
+
startUnix: UInt64?,
|
|
50
|
+
endUnix: UInt64?,
|
|
51
|
+
nftTypeIdentifier: String
|
|
52
|
+
): @FlowtyDrops.Drop {
|
|
53
|
+
pre {
|
|
54
|
+
paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// This ActiveChecker turns on at a set unix timestamp (or is on by default if nil), and ends at the specified end date if provided
|
|
58
|
+
let activeChecker = FlowtyActiveCheckers.TimestampChecker(start: startUnix, end: endUnix)
|
|
59
|
+
|
|
60
|
+
// All addresses are allowed to participate
|
|
61
|
+
let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10)
|
|
62
|
+
|
|
63
|
+
// The cost of each mint is the same, and only permits one token type as payment
|
|
64
|
+
let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType)
|
|
65
|
+
|
|
66
|
+
let phaseDetails = FlowtyDrops.PhaseDetails(activeChecker: activeChecker, display: nil, pricer: pricer, addressVerifier: addressVerifier)
|
|
67
|
+
let phase <- FlowtyDrops.createPhase(details: phaseDetails)
|
|
68
|
+
|
|
69
|
+
let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
|
|
70
|
+
let dropDetails = FlowtyDrops.DropDetails(display: dropDisplay, medias: nil, commissionRate: 0.05, nftType: nftTypeIdentifier)
|
|
71
|
+
let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- [<-phase])
|
|
72
|
+
|
|
73
|
+
return <- drop
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import "FlowtyDrops"
|
|
2
|
+
import "MetadataViews"
|
|
3
|
+
import "ViewResolver"
|
|
4
|
+
import "AddressUtils"
|
|
5
|
+
|
|
6
|
+
access(all) contract DropTypes {
|
|
7
|
+
access(all) struct Display {
|
|
8
|
+
access(all) let name: String
|
|
9
|
+
access(all) let description: String
|
|
10
|
+
access(all) let url: String
|
|
11
|
+
|
|
12
|
+
init(_ display: MetadataViews.Display) {
|
|
13
|
+
self.name = display.name
|
|
14
|
+
self.description = display.description
|
|
15
|
+
self.url = display.thumbnail.uri()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
access(all) struct Media {
|
|
20
|
+
access(all) let url: String
|
|
21
|
+
access(all) let mediaType: String
|
|
22
|
+
|
|
23
|
+
init(_ media: MetadataViews.Media) {
|
|
24
|
+
self.url = media.file.uri()
|
|
25
|
+
self.mediaType = media.mediaType
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
access(all) struct DropSummary {
|
|
30
|
+
access(all) let id: UInt64
|
|
31
|
+
access(all) let display: Display
|
|
32
|
+
access(all) let medias: [Media]
|
|
33
|
+
access(all) let totalMinted: Int
|
|
34
|
+
access(all) let minterCount: Int
|
|
35
|
+
access(all) let commissionRate: UFix64
|
|
36
|
+
access(all) let nftType: String
|
|
37
|
+
|
|
38
|
+
access(all) let address: Address?
|
|
39
|
+
access(all) let mintedByAddress: Int?
|
|
40
|
+
|
|
41
|
+
access(all) let phases: [PhaseSummary]
|
|
42
|
+
|
|
43
|
+
access(all) let blockTimestamp: UInt64
|
|
44
|
+
access(all) let blockHeight: UInt64
|
|
45
|
+
|
|
46
|
+
init(
|
|
47
|
+
id: UInt64,
|
|
48
|
+
display: MetadataViews.Display,
|
|
49
|
+
medias: MetadataViews.Medias?,
|
|
50
|
+
totalMinted: Int,
|
|
51
|
+
minterCount: Int,
|
|
52
|
+
mintedByAddress: Int?,
|
|
53
|
+
commissionRate: UFix64,
|
|
54
|
+
nftType: Type,
|
|
55
|
+
address: Address?,
|
|
56
|
+
phases: [PhaseSummary]
|
|
57
|
+
) {
|
|
58
|
+
self.id = id
|
|
59
|
+
self.display = Display(display)
|
|
60
|
+
|
|
61
|
+
self.medias = []
|
|
62
|
+
for m in medias?.items ?? [] {
|
|
63
|
+
self.medias.append(Media(m))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
self.totalMinted = totalMinted
|
|
68
|
+
self.commissionRate = commissionRate
|
|
69
|
+
self.minterCount = minterCount
|
|
70
|
+
self.mintedByAddress = mintedByAddress
|
|
71
|
+
self.nftType = nftType.identifier
|
|
72
|
+
self.address = address
|
|
73
|
+
self.phases = phases
|
|
74
|
+
|
|
75
|
+
let b = getCurrentBlock()
|
|
76
|
+
self.blockHeight = b.height
|
|
77
|
+
self.blockTimestamp = UInt64(b.timestamp)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
access(all) struct Quote {
|
|
82
|
+
access(all) let price: UFix64
|
|
83
|
+
access(all) let quantity: Int
|
|
84
|
+
access(all) let paymentIdentifier: String
|
|
85
|
+
access(all) let minter: Address?
|
|
86
|
+
|
|
87
|
+
init(price: UFix64, quantity: Int, paymentIdentifier: String, minter: Address?) {
|
|
88
|
+
self.price = price
|
|
89
|
+
self.quantity = quantity
|
|
90
|
+
self.paymentIdentifier = paymentIdentifier
|
|
91
|
+
self.minter = minter
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
access(all) struct PhaseSummary {
|
|
96
|
+
access(all) let id: UInt64
|
|
97
|
+
access(all) let index: Int
|
|
98
|
+
|
|
99
|
+
access(all) let activeCheckerType: String
|
|
100
|
+
access(all) let pricerType: String
|
|
101
|
+
access(all) let addressVerifierType: String
|
|
102
|
+
|
|
103
|
+
access(all) let hasStarted: Bool
|
|
104
|
+
access(all) let hasEnded: Bool
|
|
105
|
+
access(all) let start: UInt64?
|
|
106
|
+
access(all) let end: UInt64?
|
|
107
|
+
|
|
108
|
+
access(all) let paymentTypes: [String]
|
|
109
|
+
|
|
110
|
+
access(all) let address: Address?
|
|
111
|
+
access(all) let remainingForAddress: Int?
|
|
112
|
+
|
|
113
|
+
access(all) let quote: Quote?
|
|
114
|
+
|
|
115
|
+
init(
|
|
116
|
+
index: Int,
|
|
117
|
+
phase: &{FlowtyDrops.PhasePublic},
|
|
118
|
+
address: Address?,
|
|
119
|
+
totalMinted: Int?,
|
|
120
|
+
minter: Address?,
|
|
121
|
+
quantity: Int?,
|
|
122
|
+
paymentIdentifier: String?
|
|
123
|
+
) {
|
|
124
|
+
self.index = index
|
|
125
|
+
self.id = phase.uuid
|
|
126
|
+
|
|
127
|
+
let d: FlowtyDrops.PhaseDetails = phase.getDetails()
|
|
128
|
+
self.activeCheckerType = d.activeChecker.getType().identifier
|
|
129
|
+
self.pricerType = d.pricer.getType().identifier
|
|
130
|
+
self.addressVerifierType = d.addressVerifier.getType().identifier
|
|
131
|
+
|
|
132
|
+
self.hasStarted = d.activeChecker.hasStarted()
|
|
133
|
+
self.hasEnded = d.activeChecker.hasEnded()
|
|
134
|
+
self.start = d.activeChecker.getStart()
|
|
135
|
+
self.end = d.activeChecker.getEnd()
|
|
136
|
+
|
|
137
|
+
self.paymentTypes = []
|
|
138
|
+
for pt in d.pricer.getPaymentTypes() {
|
|
139
|
+
self.paymentTypes.append(pt.identifier)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if let addr = address {
|
|
143
|
+
self.address = address
|
|
144
|
+
self.remainingForAddress = d.addressVerifier.remainingForAddress(addr: addr, totalMinted: totalMinted ?? 0)
|
|
145
|
+
} else {
|
|
146
|
+
self.address = nil
|
|
147
|
+
self.remainingForAddress = nil
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if paymentIdentifier != nil && quantity != nil {
|
|
151
|
+
let price = d.pricer.getPrice(num: quantity!, paymentTokenType: CompositeType(paymentIdentifier!)!, minter: minter)
|
|
152
|
+
|
|
153
|
+
self.quote = Quote(price: price, quantity: quantity!, paymentIdentifier: paymentIdentifier!, minter: minter)
|
|
154
|
+
} else {
|
|
155
|
+
self.quote = nil
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
access(all) fun getDropSummary(nftTypeIdentifier: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropSummary? {
|
|
161
|
+
let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
|
|
162
|
+
let segments = nftTypeIdentifier.split(separator: ".")
|
|
163
|
+
let contractAddress = AddressUtils.parseAddress(nftType)!
|
|
164
|
+
let contractName = segments[2]
|
|
165
|
+
|
|
166
|
+
let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName)
|
|
167
|
+
if resolver == nil {
|
|
168
|
+
return nil
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let dropResolver = resolver!.resolveContractView(resourceType: nftType, viewType: Type<FlowtyDrops.DropResolver>()) as! FlowtyDrops.DropResolver?
|
|
172
|
+
if dropResolver == nil {
|
|
173
|
+
return nil
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let container = dropResolver!.borrowContainer()
|
|
177
|
+
if container == nil {
|
|
178
|
+
return nil
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let drop = container!.borrowDropPublic(id: dropID)
|
|
182
|
+
if drop == nil {
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let dropDetails = drop!.getDetails()
|
|
187
|
+
|
|
188
|
+
let phaseSummaries: [PhaseSummary] = []
|
|
189
|
+
for index, phase in drop!.borrowAllPhases() {
|
|
190
|
+
let summary = PhaseSummary(
|
|
191
|
+
index: index,
|
|
192
|
+
phase: phase,
|
|
193
|
+
address: minter,
|
|
194
|
+
totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
|
|
195
|
+
minter: minter,
|
|
196
|
+
quantity: quantity,
|
|
197
|
+
paymentIdentifier: paymentIdentifier
|
|
198
|
+
)
|
|
199
|
+
phaseSummaries.append(summary)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let dropSummary = DropSummary(
|
|
203
|
+
id: drop!.uuid,
|
|
204
|
+
display: dropDetails.display,
|
|
205
|
+
medias: dropDetails.medias,
|
|
206
|
+
totalMinted: dropDetails.totalMinted,
|
|
207
|
+
minterCount: dropDetails.minters.keys.length,
|
|
208
|
+
mintedByAddress: minter != nil ? dropDetails.minters[minter!] : nil,
|
|
209
|
+
commissionRate: dropDetails.commissionRate,
|
|
210
|
+
nftType: CompositeType(dropDetails.nftType)!,
|
|
211
|
+
address: minter,
|
|
212
|
+
phases: phaseSummaries
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return dropSummary
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
access(all) fun getAllDropSummaries(nftTypeIdentifier: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropSummary] {
|
|
219
|
+
let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier")
|
|
220
|
+
let segments = nftTypeIdentifier.split(separator: ".")
|
|
221
|
+
let contractAddress = AddressUtils.parseAddress(nftType)!
|
|
222
|
+
let contractName = segments[2]
|
|
223
|
+
|
|
224
|
+
let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName)
|
|
225
|
+
if resolver == nil {
|
|
226
|
+
return []
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let dropResolver = resolver!.resolveContractView(resourceType: nftType, viewType: Type<FlowtyDrops.DropResolver>()) as! FlowtyDrops.DropResolver?
|
|
230
|
+
if dropResolver == nil {
|
|
231
|
+
return []
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let container = dropResolver!.borrowContainer()
|
|
235
|
+
if container == nil {
|
|
236
|
+
return []
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let summaries: [DropSummary] = []
|
|
240
|
+
for id in container!.getIDs() {
|
|
241
|
+
let drop = container!.borrowDropPublic(id: id)
|
|
242
|
+
if drop == nil {
|
|
243
|
+
continue
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let dropDetails = drop!.getDetails()
|
|
247
|
+
|
|
248
|
+
let phaseSummaries: [PhaseSummary] = []
|
|
249
|
+
for index, phase in drop!.borrowAllPhases() {
|
|
250
|
+
let summary = PhaseSummary(
|
|
251
|
+
index: index,
|
|
252
|
+
phase: phase,
|
|
253
|
+
address: minter,
|
|
254
|
+
totalMinted: minter != nil ? dropDetails.minters[minter!] : nil,
|
|
255
|
+
minter: minter,
|
|
256
|
+
quantity: quantity,
|
|
257
|
+
paymentIdentifier: paymentIdentifier
|
|
258
|
+
)
|
|
259
|
+
phaseSummaries.append(summary)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if CompositeType(dropDetails.nftType) == nil {
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
summaries.append(DropSummary(
|
|
267
|
+
id: drop!.uuid,
|
|
268
|
+
display: dropDetails.display,
|
|
269
|
+
medias: dropDetails.medias,
|
|
270
|
+
totalMinted: dropDetails.totalMinted,
|
|
271
|
+
minterCount: dropDetails.minters.keys.length,
|
|
272
|
+
mintedByAddress: minter != nil ? dropDetails.minters[minter!] : nil,
|
|
273
|
+
commissionRate: dropDetails.commissionRate,
|
|
274
|
+
nftType: CompositeType(dropDetails.nftType)!,
|
|
275
|
+
address: minter,
|
|
276
|
+
phases: phaseSummaries
|
|
277
|
+
))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return summaries
|
|
281
|
+
}
|
|
282
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import "FlowtyDrops"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This contract contains implementations for the FlowtyDrops.ActiveChecker struct interface.
|
|
5
|
+
You can use these implementations, or your own, to configure when a phase in a drop is active
|
|
6
|
+
*/
|
|
7
|
+
access(all) contract FlowtyActiveCheckers {
|
|
8
|
+
/*
|
|
9
|
+
The AlwaysOn ActiveChecker is always on and never ends.
|
|
10
|
+
*/
|
|
11
|
+
access(all) struct AlwaysOn: FlowtyDrops.ActiveChecker {
|
|
12
|
+
access(all) view fun hasStarted(): Bool {
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
access(all) view fun hasEnded(): Bool {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
access(all) view fun getStart(): UInt64? {
|
|
21
|
+
return nil
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
access(all) view fun getEnd(): UInt64? {
|
|
25
|
+
return nil
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
The manual checker is used to explicitly toggle a drop.
|
|
31
|
+
This version of checker allows a creator to turn on or off a drop at will
|
|
32
|
+
*/
|
|
33
|
+
access(all) struct ManualChecker: FlowtyDrops.ActiveChecker {
|
|
34
|
+
access(self) var started: Bool
|
|
35
|
+
access(self) var ended: Bool
|
|
36
|
+
|
|
37
|
+
access(all) view fun hasStarted(): Bool {
|
|
38
|
+
return self.started
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
access(all) view fun hasEnded(): Bool {
|
|
42
|
+
return self.ended
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
access(all) view fun getStart(): UInt64? {
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
access(all) view fun getEnd(): UInt64? {
|
|
50
|
+
return nil
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
access(Mutate) fun setStarted(_ b: Bool) {
|
|
54
|
+
self.started = b
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
access(Mutate) fun setEnded(_ b: Bool) {
|
|
58
|
+
self.ended = b
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
init() {
|
|
62
|
+
self.started = false
|
|
63
|
+
self.ended = false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
TimestampChecker uses block timestamps to determine if a phase or drop is live or not.
|
|
69
|
+
A timestamp checker has a start and an end time.
|
|
70
|
+
*/
|
|
71
|
+
access(all) struct TimestampChecker: FlowtyDrops.ActiveChecker {
|
|
72
|
+
access(all) var start: UInt64?
|
|
73
|
+
access(all) var end: UInt64?
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
access(all) view fun hasStarted(): Bool {
|
|
77
|
+
return self.start == nil || UInt64(getCurrentBlock().timestamp) >= self.start!
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
access(all) view fun hasEnded(): Bool {
|
|
81
|
+
if self.end == nil {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return UInt64(getCurrentBlock().timestamp) > self.end!
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
access(all) view fun getStart(): UInt64? {
|
|
89
|
+
return self.start
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
access(all) view fun getEnd(): UInt64? {
|
|
93
|
+
return self.end
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
access(Mutate) fun setStart(start: UInt64?) {
|
|
97
|
+
self.start = start
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
access(Mutate) fun setEnd(end: UInt64?) {
|
|
101
|
+
self.end = end
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
init(start: UInt64?, end: UInt64?) {
|
|
105
|
+
pre {
|
|
106
|
+
start == nil || end == nil || start! < end!: "start must be less than end"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
self.start = start
|
|
110
|
+
self.end = end
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "FlowtyDrops"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This contract contains implementations of the FlowtyDrops.AddressVerifier struct interface
|
|
5
|
+
*/
|
|
6
|
+
access(all) contract FlowtyAddressVerifiers {
|
|
7
|
+
/*
|
|
8
|
+
The AllowAll AddressVerifier allows any address to mint without any verification
|
|
9
|
+
*/
|
|
10
|
+
access(all) struct AllowAll: FlowtyDrops.AddressVerifier {
|
|
11
|
+
access(all) var maxPerMint: Int
|
|
12
|
+
|
|
13
|
+
access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
|
|
14
|
+
return num <= self.maxPerMint
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
access(Mutate) fun setMaxPerMint(_ value: Int) {
|
|
18
|
+
self.maxPerMint = value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
init(maxPerMint: Int) {
|
|
22
|
+
pre {
|
|
23
|
+
maxPerMint > 0: "maxPerMint must be greater than 0"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
self.maxPerMint = maxPerMint
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
The AllowList Verifier only lets a configured set of addresses participate in a drop phase. The number
|
|
32
|
+
of mints per address is specified to allow more granular control of what each address is permitted to do.
|
|
33
|
+
*/
|
|
34
|
+
access(all) struct AllowList: FlowtyDrops.AddressVerifier {
|
|
35
|
+
access(self) let allowedAddresses: {Address: Int}
|
|
36
|
+
|
|
37
|
+
access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool {
|
|
38
|
+
if let allowedMints = self.allowedAddresses[addr] {
|
|
39
|
+
return allowedMints >= num + totalMinted
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
access(all) view fun remainingForAddress(addr: Address, totalMinted: Int): Int? {
|
|
46
|
+
if let allowedMints = self.allowedAddresses[addr] {
|
|
47
|
+
return allowedMints - totalMinted
|
|
48
|
+
}
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
access(Mutate) fun setAddress(addr: Address, value: Int) {
|
|
53
|
+
self.allowedAddresses[addr] = value
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
access(Mutate) fun removeAddress(addr: Address) {
|
|
57
|
+
self.allowedAddresses.remove(key: addr)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
init(allowedAddresses: {Address: Int}) {
|
|
61
|
+
self.allowedAddresses = allowedAddresses
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|