diff --git a/contracts/flowty-drops/ContractManager.cdc b/contracts/flowty-drops/ContractManager.cdc index ca415af..3ddf351 100644 --- a/contracts/flowty-drops/ContractManager.cdc +++ b/contracts/flowty-drops/ContractManager.cdc @@ -1,5 +1,6 @@ import "FlowToken" import "FungibleToken" +import "FungibleTokenRouter" access(all) contract ContractManager { access(all) let StoragePath: StoragePath @@ -9,11 +10,24 @@ access(all) contract ContractManager { access(all) resource Manager { access(self) let acct: Capability + access(self) let routerCap: Capability + + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} access(Manage) fun borrowContractAccount(): auth(Contracts) &Account { return self.acct.borrow()! } + access(Manage) fun addOverride(type: Type, addr: Address) { + let router = self.routerCap.borrow() ?? panic("fungible token router is not valid") + router.addOverride(type: type, addr: addr) + } + + access(Manage) fun getSwitchboard(): auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router { + return self.routerCap.borrow()! + } + access(all) fun addFlowTokensToAccount(_ tokens: @FlowToken.Vault) { self.acct.borrow()!.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens) } @@ -22,7 +36,7 @@ access(all) contract ContractManager { return getAccount(self.acct.address) } - init(tokens: @FlowToken.Vault) { + init(tokens: @FlowToken.Vault, defaultRouterAddress: Address) { pre { tokens.balance >= 0.001: "minimum balance of 0.001 required for initialization" } @@ -32,11 +46,23 @@ access(all) contract ContractManager { assert(self.acct.check(), message: "failed to setup account capability") acct.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens) + + let router <- FungibleTokenRouter.createRouter(defaultAddress: defaultRouterAddress) + acct.storage.save(<-router, to: FungibleTokenRouter.StoragePath) + + let receiver = acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenRouter.StoragePath) + assert(receiver.check(), message: "invalid switchboard receiver capability") + acct.capabilities.publish(receiver, at: FungibleTokenRouter.PublicPath) + + self.routerCap = acct.capabilities.storage.issue(FungibleTokenRouter.StoragePath) + + self.data = {} + self.resources <- {} } } - access(all) fun createManager(tokens: @FlowToken.Vault): @Manager { - return <- create Manager(tokens: <- tokens) + access(all) fun createManager(tokens: @FlowToken.Vault, defaultRouterAddress: Address): @Manager { + return <- create Manager(tokens: <- tokens, defaultRouterAddress: defaultRouterAddress) } init() { diff --git a/contracts/flowty-drops/DropFactory.cdc b/contracts/flowty-drops/DropFactory.cdc index 822d8c3..0bf25fa 100644 --- a/contracts/flowty-drops/DropFactory.cdc +++ b/contracts/flowty-drops/DropFactory.cdc @@ -2,7 +2,7 @@ import "FungibleToken" import "MetadataViews" import "FlowtyDrops" -import "FlowtySwitchers" +import "FlowtyActiveCheckers" import "FlowtyAddressVerifiers" import "FlowtyPricers" @@ -22,7 +22,7 @@ access(all) contract DropFactory { } // This drop is always on and never ends. - let switcher = FlowtySwitchers.AlwaysOn() + let activeChecker = FlowtyActiveCheckers.AlwaysOn() // All addresses are allowed to participate let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10) @@ -30,7 +30,7 @@ access(all) contract DropFactory { // The cost of each mint is the same, and only permits one token type as payment let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType) - let phaseDetails = FlowtyDrops.PhaseDetails(switcher: switcher, display: nil, pricer: pricer, addressVerifier: addressVerifier) + let phaseDetails = FlowtyDrops.PhaseDetails(activeChecker: activeChecker, display: nil, pricer: pricer, addressVerifier: addressVerifier) let phase <- FlowtyDrops.createPhase(details: phaseDetails) @@ -54,8 +54,8 @@ access(all) contract DropFactory { paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken" } - // This switcher turns on at a set unix timestamp (or is on by default if nil), and ends at the specified end date if provided - let switcher = FlowtySwitchers.TimestampSwitch(start: startUnix, end: endUnix) + // 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 + let activeChecker = FlowtyActiveCheckers.TimestampChecker(start: startUnix, end: endUnix) // All addresses are allowed to participate let addressVerifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10) @@ -63,7 +63,7 @@ access(all) contract DropFactory { // The cost of each mint is the same, and only permits one token type as payment let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: paymentTokenType) - let phaseDetails = FlowtyDrops.PhaseDetails(switcher: switcher, display: nil, pricer: pricer, addressVerifier: addressVerifier) + let phaseDetails = FlowtyDrops.PhaseDetails(activeChecker: activeChecker, display: nil, pricer: pricer, addressVerifier: addressVerifier) let phase <- FlowtyDrops.createPhase(details: phaseDetails) let nftType = CompositeType(nftTypeIdentifier) ?? panic("invalid nft type identifier") diff --git a/contracts/flowty-drops/DropTypes.cdc b/contracts/flowty-drops/DropTypes.cdc index 4682ea7..ba37248 100644 --- a/contracts/flowty-drops/DropTypes.cdc +++ b/contracts/flowty-drops/DropTypes.cdc @@ -96,7 +96,7 @@ access(all) contract DropTypes { access(all) let id: UInt64 access(all) let index: Int - access(all) let switcherType: String + access(all) let activeCheckerType: String access(all) let pricerType: String access(all) let addressVerifierType: String @@ -124,15 +124,15 @@ access(all) contract DropTypes { self.index = index self.id = phase.uuid - let d = phase.getDetails() - self.switcherType = d.switcher.getType().identifier + let d: FlowtyDrops.PhaseDetails = phase.getDetails() + self.activeCheckerType = d.activeChecker.getType().identifier self.pricerType = d.pricer.getType().identifier self.addressVerifierType = d.addressVerifier.getType().identifier - self.hasStarted = d.switcher.hasStarted() - self.hasEnded = d.switcher.hasEnded() - self.start = d.switcher.getStart() - self.end = d.switcher.getEnd() + self.hasStarted = d.activeChecker.hasStarted() + self.hasEnded = d.activeChecker.hasEnded() + self.start = d.activeChecker.getStart() + self.end = d.activeChecker.getEnd() self.paymentTypes = [] for pt in d.pricer.getPaymentTypes() { @@ -259,6 +259,10 @@ access(all) contract DropTypes { phaseSummaries.append(summary) } + if CompositeType(dropDetails.nftType) == nil { + continue + } + summaries.append(DropSummary( id: drop!.uuid, display: dropDetails.display, diff --git a/contracts/flowty-drops/FlowtySwitchers.cdc b/contracts/flowty-drops/FlowtyActiveCheckers.cdc similarity index 73% rename from contracts/flowty-drops/FlowtySwitchers.cdc rename to contracts/flowty-drops/FlowtyActiveCheckers.cdc index 39e824b..14faba1 100644 --- a/contracts/flowty-drops/FlowtySwitchers.cdc +++ b/contracts/flowty-drops/FlowtyActiveCheckers.cdc @@ -1,14 +1,14 @@ import "FlowtyDrops" /* -This contract contains implementations for the FlowtyDrops.Switcher struct interface. -You can use these implementations, or your own, for switches when configuring a drop +This contract contains implementations for the FlowtyDrops.ActiveChecker struct interface. +You can use these implementations, or your own, to configure when a phase in a drop is active */ -access(all) contract FlowtySwitchers { +access(all) contract FlowtyActiveCheckers { /* - The AlwaysOn Switcher is always on and never ends. + The AlwaysOn ActiveChecker is always on and never ends. */ - access(all) struct AlwaysOn: FlowtyDrops.Switcher { + access(all) struct AlwaysOn: FlowtyDrops.ActiveChecker { access(all) view fun hasStarted(): Bool { return true } @@ -27,10 +27,10 @@ access(all) contract FlowtySwitchers { } /* - The manual switcher is used to explicitly toggle a drop. - This version of switcher allows a creator to turn on or off a drop at will + The manual checker is used to explicitly toggle a drop. + This version of checker allows a creator to turn on or off a drop at will */ - access(all) struct ManualSwitch: FlowtyDrops.Switcher { + access(all) struct ManualChecker: FlowtyDrops.ActiveChecker { access(self) var started: Bool access(self) var ended: Bool @@ -65,10 +65,10 @@ access(all) contract FlowtySwitchers { } /* - TimestampSwitch uses block timestamps to determine if a phase or drop is live or not. - A timestamp switcher has a start and an end time. + TimestampChecker uses block timestamps to determine if a phase or drop is live or not. + A timestamp checker has a start and an end time. */ - access(all) struct TimestampSwitch: FlowtyDrops.Switcher { + access(all) struct TimestampChecker: FlowtyDrops.ActiveChecker { access(all) var start: UInt64? access(all) var end: UInt64? diff --git a/contracts/flowty-drops/FlowtyDrops.cdc b/contracts/flowty-drops/FlowtyDrops.cdc index 08fa471..e5da834 100644 --- a/contracts/flowty-drops/FlowtyDrops.cdc +++ b/contracts/flowty-drops/FlowtyDrops.cdc @@ -2,14 +2,16 @@ import "NonFungibleToken" import "FungibleToken" import "MetadataViews" import "AddressUtils" +import "FungibleTokenMetadataViews" +import "FungibleTokenRouter" access(all) contract FlowtyDrops { access(all) let ContainerStoragePath: StoragePath access(all) let ContainerPublicPath: PublicPath - access(all) event DropAdded(address: Address, id: UInt64, name: String, description: String, imageUrl: String, start: UInt64?, end: UInt64?) + access(all) event DropAdded(address: Address, id: UInt64, name: String, description: String, imageUrl: String, start: UInt64?, end: UInt64?, nftType: String) access(all) event Minted(address: Address, dropID: UInt64, phaseID: UInt64, nftID: UInt64, nftType: String) - access(all) event PhaseAdded(dropID: UInt64, dropAddress: Address, id: UInt64, index: Int, switcherType: String, pricerType: String, addressVerifierType: String) + access(all) event PhaseAdded(dropID: UInt64, dropAddress: Address, id: UInt64, index: Int, activeCheckerType: String, pricerType: String, addressVerifierType: String) access(all) event PhaseRemoved(dropID: UInt64, dropAddress: Address, id: UInt64) access(all) entitlement Owner @@ -27,12 +29,52 @@ access(all) contract FlowtyDrops { phaseIndex: Int, expectedType: Type, receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>, - commissionReceiver: Capability<&{FungibleToken.Receiver}>, + commissionReceiver: Capability<&{FungibleToken.Receiver}>?, data: {String: AnyStruct} ): @{FungibleToken.Vault} access(all) fun getDetails(): DropDetails } + // A phase represents a stage of a drop. Some drops will only have one + // phase, while others could have many. For example, a drop with an allow list + // and a public mint would likely have two phases. + access(all) resource Phase: PhasePublic { + access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid) + + access(all) let details: PhaseDetails + + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} + + // returns whether this phase of a drop has started. + access(all) fun isActive(): Bool { + return self.details.activeChecker.hasStarted() && !self.details.activeChecker.hasEnded() + } + + access(all) fun getDetails(): PhaseDetails { + return self.details + } + + access(EditPhase) fun borrowActiveCheckerAuth(): auth(Mutate) &{ActiveChecker} { + return &self.details.activeChecker + } + + access(EditPhase) fun borrowPricerAuth(): auth(Mutate) &{Pricer} { + return &self.details.pricer + } + + access(EditPhase) fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} { + return &self.details.addressVerifier + } + + init(details: PhaseDetails) { + self.details = details + + self.data = {} + self.resources <- {} + } + } + access(all) resource Drop: DropPublic { access(all) event ResourceDestroyed( uuid: UInt64 = self.uuid, @@ -47,13 +89,16 @@ access(all) contract FlowtyDrops { access(self) let details: DropDetails access(self) let minterCap: Capability<&{Minter}> + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} + access(all) fun mint( payment: @{FungibleToken.Vault}, amount: Int, phaseIndex: Int, expectedType: Type, receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>, - commissionReceiver: Capability<&{FungibleToken.Receiver}>, + commissionReceiver: Capability<&{FungibleToken.Receiver}>?, data: {String: AnyStruct} ): @{FungibleToken.Vault} { pre { @@ -74,16 +119,18 @@ access(all) contract FlowtyDrops { let withdrawn <- payment.withdraw(amount: paymentAmount) // make sure that we have a fresh vault resource // take commission - let commission <- withdrawn.withdraw(amount: self.details.commissionRate * withdrawn.balance) - commissionReceiver.borrow()!.deposit(from: <-commission) + if commissionReceiver != nil && commissionReceiver!.check() { + let commission <- withdrawn.withdraw(amount: self.details.commissionRate * withdrawn.balance) + commissionReceiver!.borrow()!.deposit(from: <-commission) + } assert(phase.details.pricer.getPrice(num: amount, paymentTokenType: withdrawn.getType(), minter: receiverCap.address) * (1.0 - self.details.commissionRate) == withdrawn.balance, message: "incorrect payment amount") assert(phase.details.pricer.getPaymentTypes().contains(withdrawn.getType()), message: "unsupported payment type") // mint the nfts let minter = self.minterCap.borrow() ?? panic("minter capability could not be borrowed") - let mintedNFTs <- minter.mint(payment: <-withdrawn, amount: amount, phase: phase, data: data) - assert(phase.details.switcher.hasStarted() && !phase.details.switcher.hasEnded(), message: "phase is not active") + let mintedNFTs: @[{NonFungibleToken.NFT}] <- minter.mint(payment: <-withdrawn, amount: amount, phase: phase, data: data) + assert(phase.details.activeChecker.hasStarted() && !phase.details.activeChecker.hasEnded(), message: "phase is not active") assert(mintedNFTs.length == amount, message: "incorrect number of items returned") // distribute to receiver @@ -123,8 +170,8 @@ access(all) contract FlowtyDrops { var count = 0 while count < self.phases.length { let ref = self.borrowPhasePublic(index: count) - let switcher = ref.getDetails().switcher - if switcher.hasStarted() && !switcher.hasEnded() { + let activeChecker = ref.getDetails().activeChecker + if activeChecker.hasStarted() && !activeChecker.hasEnded() { arr.append(ref) } @@ -152,7 +199,7 @@ access(all) contract FlowtyDrops { dropAddress: self.owner!.address, id: phase.uuid, index: self.phases.length, - switcherType: phase.details.switcher.getType().identifier, + activeCheckerType: phase.details.activeChecker.getType().identifier, pricerType: phase.details.pricer.getType().identifier, addressVerifierType: phase.details.addressVerifier.getType().identifier ) @@ -182,6 +229,9 @@ access(all) contract FlowtyDrops { self.phases <- phases self.details = details self.minterCap = minterCap + + self.data = {} + self.resources <- {} } } @@ -193,6 +243,8 @@ access(all) contract FlowtyDrops { access(all) let commissionRate: UFix64 access(all) let nftType: String + access(all) let data: {String: AnyStruct} + access(contract) fun addMinted(num: Int, addr: Address) { self.totalMinted = self.totalMinted + num if self.minters[addr] == nil { @@ -203,22 +255,27 @@ access(all) contract FlowtyDrops { } init(display: MetadataViews.Display, medias: MetadataViews.Medias?, commissionRate: UFix64, nftType: String) { + pre { + nftType != "": "nftType should be a composite type identifier" + } + self.display = display self.medias = medias self.totalMinted = 0 self.commissionRate = commissionRate self.minters = {} self.nftType = nftType + self.data = {} } } - // A switcher represents a phase being on or off, and holds information + // An ActiveChecker represents a phase being on or off, and holds information // about whether a phase has started or not. - access(all) struct interface Switcher { - // Signal that a phase has started. If the phase has not ended, it means that this switcher's phase + access(all) struct interface ActiveChecker { + // Signal that a phase has started. If the phase has not ended, it means that this activeChecker's phase // is active access(all) view fun hasStarted(): Bool - // Signal that a phase has ended. If a switcher has ended, minting will not work. That could mean + // Signal that a phase has ended. If an ActiveChecker has ended, minting will not work. That could mean // the drop is over, or it could mean another phase has begun. access(all) view fun hasEnded(): Bool @@ -226,40 +283,6 @@ access(all) contract FlowtyDrops { access(all) view fun getEnd(): UInt64? } - // A phase represents a stage of a drop. Some drops will only have one - // phase, while others could have many. For example, a drop with an allow list - // and a public mint would likely have two phases. - access(all) resource Phase: PhasePublic { - access(all) event ResourceDestroyed(uuid: UInt64 = self.uuid) - - access(all) let details: PhaseDetails - - // returns whether this phase of a drop has started. - access(all) fun isActive(): Bool { - return self.details.switcher.hasStarted() && !self.details.switcher.hasEnded() - } - - access(all) fun getDetails(): PhaseDetails { - return self.details - } - - access(EditPhase) fun borrowSwitchAuth(): auth(Mutate) &{Switcher} { - return &self.details.switcher - } - - access(EditPhase) fun borrowPricerAuth(): auth(Mutate) &{Pricer} { - return &self.details.pricer - } - - access(EditPhase) fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} { - return &self.details.addressVerifier - } - - init(details: PhaseDetails) { - self.details = details - } - } - access(all) resource interface PhasePublic { // What does a phase need to be able to answer/manage? // - What are the details of the phase being interactive with? @@ -272,7 +295,7 @@ access(all) contract FlowtyDrops { access(all) struct PhaseDetails { // handles whether a phase is on or not - access(all) let switcher: {Switcher} + access(all) let activeChecker: {ActiveChecker} // display information about a phase access(all) let display: MetadataViews.Display? @@ -286,8 +309,8 @@ access(all) contract FlowtyDrops { // placecholder data dictionary to allow new fields to be accessed access(all) let data: {String: AnyStruct} - init(switcher: {Switcher}, display: MetadataViews.Display?, pricer: {Pricer}, addressVerifier: {AddressVerifier}) { - self.switcher = switcher + init(activeChecker: {ActiveChecker}, display: MetadataViews.Display?, pricer: {Pricer}, addressVerifier: {AddressVerifier}) { + self.activeChecker = activeChecker self.display = display self.pricer = pricer self.addressVerifier = addressVerifier @@ -314,8 +337,8 @@ access(all) contract FlowtyDrops { access(all) resource interface Minter { access(contract) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] { let resourceAddress = AddressUtils.parseAddress(self.getType())! - let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).borrow() - ?? panic("invalid flow token receiver") + let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(FungibleTokenRouter.PublicPath).borrow() + ?? panic("missing receiver at fungible token router path") receiver.deposit(from: <-payment) let nfts: @[{NonFungibleToken.NFT}] <- [] @@ -357,6 +380,9 @@ access(all) contract FlowtyDrops { access(all) resource Container: ContainerPublic { access(self) let drops: @{UInt64: Drop} + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} + access(Owner) fun addDrop(_ drop: @Drop) { let details = drop.getDetails() @@ -371,8 +397,9 @@ access(all) contract FlowtyDrops { name: details.display.name, description: details.display.description, imageUrl: details.display.thumbnail.uri(), - start: firstPhaseDetails.switcher.getStart(), - end: firstPhaseDetails.switcher.getEnd() + start: firstPhaseDetails.activeChecker.getStart(), + end: firstPhaseDetails.activeChecker.getEnd(), + nftType: details.nftType ) destroy self.drops.insert(key: drop.uuid, <-drop) } @@ -399,6 +426,9 @@ access(all) contract FlowtyDrops { init() { self.drops <- {} + + self.data = {} + self.resources <- {} } } @@ -416,7 +446,7 @@ access(all) contract FlowtyDrops { access(all) fun getMinterStoragePath(type: Type): StoragePath { let segments = type.identifier.split(separator: ".") - let identifier = "FlowtyDrops_Minter_".concat(segments[1]).concat(segments[2]) + let identifier = "FlowtyDrops_Minter_".concat(segments[1]).concat("_").concat(segments[2]) return StoragePath(identifier: identifier)! } diff --git a/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc b/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc index b5b856f..fd001d2 100644 --- a/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc +++ b/contracts/flowty-drops/initializers/OpenEditionInitializer.cdc @@ -1,6 +1,8 @@ import "ContractInitializer" import "NFTMetadata" import "FlowtyDrops" +import "NonFungibleToken" +import "UniversalCollection" access(all) contract OpenEditionInitializer: ContractInitializer { access(all) fun initialize(contractAcct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account, params: {String: AnyStruct}): NFTMetadata.InitializedCaps { @@ -9,18 +11,15 @@ access(all) contract OpenEditionInitializer: ContractInitializer { params["data"]!.getType() == Type(): "data param must be of type NFTMetadata.Metadata" params["collectionInfo"] != nil: "missing param collectionInfo" params["collectionInfo"]!.getType() == Type(): "collectionInfo param must be of type NFTMetadata.CollectionInfo" + params["type"] != nil: "missing param type" + params["type"]!.getType() == Type(): "type param must be of type Type" } let data = params["data"]! as! NFTMetadata.Metadata let collectionInfo = params["collectionInfo"]! as! NFTMetadata.CollectionInfo - let acct: auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account = Account(payer: contractAcct) - let cap = acct.capabilities.account.issue() - - let t = self.getType() - let contractName = t.identifier.split(separator: ".")[2] - - self.account.storage.save(cap, to: StoragePath(identifier: "metadataAuthAccount_".concat(contractName))!) + let nftType = params["type"]! as! Type + let contractName = nftType.identifier.split(separator: ".")[2] // do we have information to setup a drop as well? if params.containsKey("dropDetails") && params.containsKey("phaseDetails") && params.containsKey("minterController") { @@ -30,7 +29,7 @@ access(all) contract OpenEditionInitializer: ContractInitializer { let phaseDetails = params["phaseDetails"]! as! [FlowtyDrops.PhaseDetails] assert(minterCap.check(), message: "invalid minter capability") - + assert(CompositeType(dropDetails.nftType) != nil, message: "dropDetails.nftType must be a valid CompositeType") let phases: @[FlowtyDrops.Phase] <- [] for p in phaseDetails { @@ -38,21 +37,21 @@ access(all) contract OpenEditionInitializer: ContractInitializer { } let drop <- FlowtyDrops.createDrop(details: dropDetails, minterCap: minterCap, phases: <- phases) - if acct.storage.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { - acct.storage.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) + if contractAcct.storage.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { + contractAcct.storage.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) - acct.capabilities.unpublish(FlowtyDrops.ContainerPublicPath) - acct.capabilities.publish( - acct.capabilities.storage.issue<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerStoragePath), + contractAcct.capabilities.unpublish(FlowtyDrops.ContainerPublicPath) + contractAcct.capabilities.publish( + contractAcct.capabilities.storage.issue<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerStoragePath), at: FlowtyDrops.ContainerPublicPath ) } - let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) + let container = contractAcct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("drops container not found") container.addDrop(<- drop) } - return NFTMetadata.initialize(acct: acct, collectionInfo: collectionInfo, collectionType: self.getType()) + return NFTMetadata.initialize(acct: contractAcct, collectionInfo: collectionInfo, nftType: nftType) } } \ No newline at end of file diff --git a/contracts/flowty-drops/nft/BaseCollection.cdc b/contracts/flowty-drops/nft/BaseCollection.cdc index 61b1bbd..7a9203f 100644 --- a/contracts/flowty-drops/nft/BaseCollection.cdc +++ b/contracts/flowty-drops/nft/BaseCollection.cdc @@ -82,12 +82,12 @@ access(all) contract interface BaseCollection: ViewResolver { ) case Type(): let c = getAccount(addr).contracts.borrow<&{BaseCollection}>(name: segments[2])! - let md = c.MetadataCap.borrow() - if md == nil { + let tmp = c.MetadataCap.borrow() + if tmp == nil { return nil } - return md!.collectionInfo.collectionDisplay + return tmp!.collectionInfo.getDisplay() case Type(): return FlowtyDrops.DropResolver(cap: acct.capabilities.get<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath)) } diff --git a/contracts/flowty-drops/nft/NFTMetadata.cdc b/contracts/flowty-drops/nft/NFTMetadata.cdc index 03088b5..bef4635 100644 --- a/contracts/flowty-drops/nft/NFTMetadata.cdc +++ b/contracts/flowty-drops/nft/NFTMetadata.cdc @@ -9,8 +9,16 @@ access(all) contract NFTMetadata { access(all) struct CollectionInfo { access(all) var collectionDisplay: MetadataViews.NFTCollectionDisplay + access(all) let data: {String: AnyStruct} + + access(all) fun getDisplay(): MetadataViews.NFTCollectionDisplay { + return self.collectionDisplay + } + init(collectionDisplay: MetadataViews.NFTCollectionDisplay) { self.collectionDisplay = collectionDisplay + + self.data = {} } } @@ -24,8 +32,9 @@ access(all) contract NFTMetadata { access(all) let traits: MetadataViews.Traits? access(all) let editions: MetadataViews.Editions? access(all) let externalURL: MetadataViews.ExternalURL? + access(all) let royalties: MetadataViews.Royalties? - access(all) let data: {String: AnyStruct} // general-purpose data bucket + access(all) let data: {String: AnyStruct} init( name: String, @@ -34,6 +43,7 @@ access(all) contract NFTMetadata { traits: MetadataViews.Traits?, editions: MetadataViews.Editions?, externalURL: MetadataViews.ExternalURL?, + royalties: MetadataViews.Royalties?, data: {String: AnyStruct} ) { self.name = name @@ -43,6 +53,7 @@ access(all) contract NFTMetadata { self.traits = traits self.editions = editions self.externalURL = externalURL + self.royalties = royalties self.data = {} } @@ -53,6 +64,9 @@ access(all) contract NFTMetadata { access(all) let metadata: {UInt64: Metadata} access(all) var frozen: Bool + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} + access(all) fun borrowMetadata(id: UInt64): &Metadata? { return &self.metadata[id] } @@ -74,6 +88,9 @@ access(all) contract NFTMetadata { self.collectionInfo = collectionInfo self.metadata = {} self.frozen = false + + self.data = {} + self.resources <- {} } } @@ -81,9 +98,13 @@ access(all) contract NFTMetadata { access(all) let pubCap: Capability<&Container> access(all) let ownerCap: Capability + access(all) let data: {String: AnyStruct} + init(pubCap: Capability<&Container>, ownerCap: Capability) { self.pubCap = pubCap self.ownerCap = ownerCap + + self.data = {} } } @@ -91,8 +112,8 @@ access(all) contract NFTMetadata { return <- create Container(collectionInfo: collectionInfo) } - access(all) fun initialize(acct: auth(Storage, Capabilities) &Account, collectionInfo: CollectionInfo, collectionType: Type): InitializedCaps { - let storagePath = self.getCollectionStoragePath(type: collectionType) + access(all) fun initialize(acct: auth(Storage, Capabilities) &Account, collectionInfo: CollectionInfo, nftType: Type): InitializedCaps { + let storagePath = self.getCollectionStoragePath(type: nftType) let container <- self.createContainer(collectionInfo: collectionInfo) acct.storage.save(<-container, to: storagePath) let pubCap = acct.capabilities.storage.issue<&Container>(storagePath) diff --git a/contracts/flowty-drops/nft/OpenEditionNFT.cdc b/contracts/flowty-drops/nft/OpenEditionNFT.cdc index 4179815..450c759 100644 --- a/contracts/flowty-drops/nft/OpenEditionNFT.cdc +++ b/contracts/flowty-drops/nft/OpenEditionNFT.cdc @@ -35,6 +35,7 @@ access(all) contract OpenEditionNFT: BaseCollection { self.totalSupply = 0 self.account.storage.save(<- create NFTMinter(), to: FlowtyDrops.getMinterStoragePath(type: self.getType())) params["minterController"] = self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.getMinterStoragePath(type: self.getType())) + params["type"] = Type<@NFT>() self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap } diff --git a/contracts/flowty-drops/nft/OpenEditionTemplate.cdc b/contracts/flowty-drops/nft/OpenEditionTemplate.cdc index 393b20d..ef814f3 100644 --- a/contracts/flowty-drops/nft/OpenEditionTemplate.cdc +++ b/contracts/flowty-drops/nft/OpenEditionTemplate.cdc @@ -11,9 +11,10 @@ access(all) contract OpenEditionTemplate: ContractFactoryTemplate { "NFTMetadata", "UniversalCollection", "ContractBorrower", - "BaseCollection" + "BaseCollection", + "ViewResolver" ]).concat("\n\n") - .concat("access(all) contract ").concat(name).concat(": BaseCollection {\n") + .concat("access(all) contract ").concat(name).concat(": BaseCollection, ViewResolver {\n") .concat(" access(all) var MetadataCap: Capability<&NFTMetadata.Container>\n") .concat(" access(all) var totalSupply: UInt64\n") .concat("\n\n") @@ -42,6 +43,7 @@ access(all) contract OpenEditionTemplate: ContractFactoryTemplate { .concat(" let minter <- create NFTMinter()\n") .concat(" self.account.storage.save(<-minter, to: FlowtyDrops.getMinterStoragePath(type: self.getType()))\n") .concat(" params[\"minterController\"] = self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.getMinterStoragePath(type: self.getType()))\n") + .concat(" params[\"type\"] = Type<@NFT>()\n") .concat("\n\n") .concat(" self.MetadataCap = ContractBorrower.borrowInitializer(typeIdentifier: initializeIdentifier).initialize(contractAcct: self.account, params: params).pubCap\n") .concat(" }\n") diff --git a/contracts/flowty-drops/nft/UniversalCollection.cdc b/contracts/flowty-drops/nft/UniversalCollection.cdc index d263b1d..9f53021 100644 --- a/contracts/flowty-drops/nft/UniversalCollection.cdc +++ b/contracts/flowty-drops/nft/UniversalCollection.cdc @@ -7,6 +7,9 @@ access(all) contract UniversalCollection { access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} access(all) var nftType: Type + access(all) let data: {String: AnyStruct} + access(all) let resources: @{String: AnyResource} + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { return <- create Collection(nftType: self.nftType) } @@ -14,6 +17,9 @@ access(all) contract UniversalCollection { init (nftType: Type) { self.ownedNFTs <- {} self.nftType = nftType + + self.data = {} + self.resources <- {} } } diff --git a/flow.json b/flow.json index bac7f57..9b41f24 100644 --- a/flow.json +++ b/flow.json @@ -359,7 +359,7 @@ "source": "./contracts/flowty-drops/ContractManager.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -367,7 +367,7 @@ "source": "./contracts/flowty-drops/initializers/ContractInitializer.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -375,7 +375,7 @@ "source": "./contracts/flowty-drops/initializers/ContractBorrower.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -383,7 +383,7 @@ "source": "./contracts/flowty-drops/initializers/OpenEditionInitializer.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -391,7 +391,7 @@ "source": "./contracts/flowty-drops/nft/BaseCollection.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -399,7 +399,7 @@ "source": "./contracts/flowty-drops/nft/ContractFactoryTemplate.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -407,7 +407,7 @@ "source": "./contracts/flowty-drops/nft/ContractFactory.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -415,7 +415,7 @@ "source": "./contracts/flowty-drops/nft/OpenEditionTemplate.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -423,7 +423,7 @@ "source": "./contracts/flowty-drops/nft/UniversalCollection.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -431,7 +431,7 @@ "source": "./contracts/flowty-drops/nft/BaseNFT.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -439,7 +439,7 @@ "source": "./contracts/flowty-drops/nft/NFTMetadata.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -447,7 +447,7 @@ "source": "./contracts/flowty-drops/FlowtyDrops.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -455,15 +455,15 @@ "source": "./contracts/flowty-drops/DropFactory.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, - "FlowtySwitchers": { - "source": "./contracts/flowty-drops/FlowtySwitchers.cdc", + "FlowtyActiveCheckers": { + "source": "./contracts/flowty-drops/FlowtyActiveCheckers.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -471,7 +471,7 @@ "source": "./contracts/flowty-drops/FlowtyPricers.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -479,7 +479,7 @@ "source": "./contracts/flowty-drops/FlowtyAddressVerifiers.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0xd20430774404a7e8", + "testnet": "0x772a10c786851a1b", "emulator": "0xf8d6e0586b0a20c7" } }, @@ -487,7 +487,7 @@ "source": "./contracts/flowty-drops/DropTypes.cdc", "aliases": { "testing": "0x0000000000000006", - "testnet": "0x3d49bb33997bd91e", + "testnet": "0x934da91a977f1ac4", "emulator": "0xf8d6e0586b0a20c7" } }, diff --git a/package.json b/package.json index 4bb9584..68a3174 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flowtyio/flow-contracts", - "version": "0.1.0-beta.31", + "version": "0.1.0-beta.32", "main": "index.json", "description": "An NPM package for common flow contracts", "author": "flowtyio",