diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 8584492..7e247c0 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -26,7 +26,7 @@ jobs: - name: Install Flow dependencies run: npm i - name: Install Flow CLI - run: bash -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v1.17.0 + run: bash -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/feature/stable-cadence/install.sh)" - name: Run tests run: sh ./run-tests.sh - name: Upload coverage reports to Codecov diff --git a/contracts/DropFactory.cdc b/contracts/DropFactory.cdc index 244fe55..d883c68 100644 --- a/contracts/DropFactory.cdc +++ b/contracts/DropFactory.cdc @@ -9,8 +9,8 @@ import "FlowtyPricers" /* The DropFactory is a contract that helps create common types of drops */ -pub contract DropFactory { - pub fun createEndlessOpenEditionDrop( +access(all) contract DropFactory { + access(all) fun createEndlessOpenEditionDrop( price: UFix64, paymentTokenType: Type, dropDisplay: MetadataViews.Display, @@ -18,7 +18,7 @@ pub contract DropFactory { nftTypeIdentifier: String ): @FlowtyDrops.Drop { pre { - paymentTokenType.isSubtype(of: Type<@FungibleToken.Vault>()): "paymentTokenType must be a FungibleToken" + paymentTokenType.isSubtype(of: Type<@{FungibleToken.Vault}>()): "paymentTokenType must be a FungibleToken" } // This drop is always on and never ends. @@ -41,7 +41,7 @@ pub contract DropFactory { return <- drop } - pub fun createTimeBasedOpenEditionDrop( + access(all) fun createTimeBasedOpenEditionDrop( price: UFix64, paymentTokenType: Type, dropDisplay: MetadataViews.Display, @@ -51,7 +51,7 @@ pub contract DropFactory { nftTypeIdentifier: String ): @FlowtyDrops.Drop { pre { - paymentTokenType.isSubtype(of: Type<@FungibleToken.Vault>()): "paymentTokenType must be a FungibleToken" + 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 diff --git a/contracts/DropTypes.cdc b/contracts/DropTypes.cdc index 35637c7..00d8cd9 100644 --- a/contracts/DropTypes.cdc +++ b/contracts/DropTypes.cdc @@ -2,11 +2,11 @@ import "FlowtyDrops" import "MetadataViews" import "ViewResolver" -pub contract DropTypes { - pub struct Display { - pub let name: String - pub let description: String - pub let url: String +access(all) contract DropTypes { + access(all) struct Display { + access(all) let name: String + access(all) let description: String + access(all) let url: String init(_ display: MetadataViews.Display) { self.name = display.name @@ -15,9 +15,9 @@ pub contract DropTypes { } } - pub struct Media { - pub let url: String - pub let mediaType: String + access(all) struct Media { + access(all) let url: String + access(all) let mediaType: String init(_ media: MetadataViews.Media) { self.url = media.file.uri() @@ -25,22 +25,22 @@ pub contract DropTypes { } } - pub struct DropSummary { - pub let id: UInt64 - pub let display: Display - pub let medias: [Media] - pub let totalMinted: Int - pub let minterCount: Int - pub let commissionRate: UFix64 - pub let nftType: String + access(all) struct DropSummary { + access(all) let id: UInt64 + access(all) let display: Display + access(all) let medias: [Media] + access(all) let totalMinted: Int + access(all) let minterCount: Int + access(all) let commissionRate: UFix64 + access(all) let nftType: String - pub let address: Address? - pub let mintedByAddress: Int? + access(all) let address: Address? + access(all) let mintedByAddress: Int? - pub let phases: [PhaseSummary] + access(all) let phases: [PhaseSummary] - pub let blockTimestamp: UInt64 - pub let blockHeight: UInt64 + access(all) let blockTimestamp: UInt64 + access(all) let blockHeight: UInt64 init( id: UInt64, @@ -77,11 +77,11 @@ pub contract DropTypes { } } - pub struct Quote { - pub let price: UFix64 - pub let quantity: Int - pub let paymentIdentifier: String - pub let minter: Address? + access(all) struct Quote { + access(all) let price: UFix64 + access(all) let quantity: Int + access(all) let paymentIdentifier: String + access(all) let minter: Address? init(price: UFix64, quantity: Int, paymentIdentifier: String, minter: Address?) { self.price = price @@ -91,25 +91,25 @@ pub contract DropTypes { } } - pub struct PhaseSummary { - pub let id: UInt64 - pub let index: Int + access(all) struct PhaseSummary { + access(all) let id: UInt64 + access(all) let index: Int - pub let switcherType: String - pub let pricerType: String - pub let addressVerifierType: String + access(all) let switcherType: String + access(all) let pricerType: String + access(all) let addressVerifierType: String - pub let hasStarted: Bool - pub let hasEnded: Bool - pub let start: UInt64? - pub let end: UInt64? + access(all) let hasStarted: Bool + access(all) let hasEnded: Bool + access(all) let start: UInt64? + access(all) let end: UInt64? - pub let paymentTypes: [String] + access(all) let paymentTypes: [String] - pub let address: Address? - pub let remainingForAddress: Int? + access(all) let address: Address? + access(all) let remainingForAddress: Int? - pub let quote: Quote? + access(all) let quote: Quote? init( index: Int, @@ -156,13 +156,13 @@ pub contract DropTypes { } } - pub fun getDropSummary(contractAddress: Address, contractName: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropSummary? { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) + access(all) fun getDropSummary(contractAddress: Address, contractName: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropSummary? { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) if resolver == nil { return nil } - let dropResolver = resolver!.resolveView(Type()) as! FlowtyDrops.DropResolver? + let dropResolver = resolver!.resolveContractView(resourceType: nil, viewType: Type()) as! FlowtyDrops.DropResolver? if dropResolver == nil { return nil } @@ -209,13 +209,13 @@ pub contract DropTypes { return dropSummary } - pub fun getAllDropSummaries(contractAddress: Address, contractName: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropSummary] { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) + access(all) fun getAllDropSummaries(contractAddress: Address, contractName: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropSummary] { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) if resolver == nil { return [] } - let dropResolver = resolver!.resolveView(Type()) as! FlowtyDrops.DropResolver? + let dropResolver = resolver!.resolveContractView(resourceType: nil, viewType: Type()) as! FlowtyDrops.DropResolver? if dropResolver == nil { return [] } diff --git a/contracts/FlowtyAddressVerifiers.cdc b/contracts/FlowtyAddressVerifiers.cdc index 2a33f02..1fd6a3d 100644 --- a/contracts/FlowtyAddressVerifiers.cdc +++ b/contracts/FlowtyAddressVerifiers.cdc @@ -3,18 +3,18 @@ import "FlowtyDrops" /* This contract contains implementations of the FlowtyDrops.AddressVerifier struct interface */ -pub contract FlowtyAddressVerifiers { +access(all) contract FlowtyAddressVerifiers { /* The AllowAll AddressVerifier allows any address to mint without any verification */ - pub struct AllowAll: FlowtyDrops.AddressVerifier { - pub var maxPerMint: Int + access(all) struct AllowAll: FlowtyDrops.AddressVerifier { + access(all) var maxPerMint: Int - pub fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { + access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { return num <= self.maxPerMint } - pub fun setMaxPerMint(_ value: Int) { + access(Mutate) fun setMaxPerMint(_ value: Int) { self.maxPerMint = value } @@ -31,10 +31,10 @@ pub contract FlowtyAddressVerifiers { The AllowList Verifier only lets a configured set of addresses participate in a drop phase. The number of mints per address is specified to allow more granular control of what each address is permitted to do. */ - pub struct AllowList: FlowtyDrops.AddressVerifier { + access(all) struct AllowList: FlowtyDrops.AddressVerifier { access(self) let allowedAddresses: {Address: Int} - pub fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { + access(all) view fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { if let allowedMints = self.allowedAddresses[addr] { return allowedMints >= num + totalMinted } @@ -42,18 +42,18 @@ pub contract FlowtyAddressVerifiers { return false } - pub fun remainingForAddress(addr: Address, totalMinted: Int): Int? { + access(all) view fun remainingForAddress(addr: Address, totalMinted: Int): Int? { if let allowedMints = self.allowedAddresses[addr] { return allowedMints - totalMinted } return nil } - pub fun setAddress(addr: Address, value: Int) { + access(Mutate) fun setAddress(addr: Address, value: Int) { self.allowedAddresses[addr] = value } - pub fun removeAddress(addr: Address) { + access(Mutate) fun removeAddress(addr: Address) { self.allowedAddresses.remove(key: addr) } diff --git a/contracts/FlowtyDrops.cdc b/contracts/FlowtyDrops.cdc index c3bc1c1..30e633f 100644 --- a/contracts/FlowtyDrops.cdc +++ b/contracts/FlowtyDrops.cdc @@ -2,60 +2,69 @@ import "NonFungibleToken" import "FungibleToken" import "MetadataViews" -pub contract FlowtyDrops { - pub let ContainerStoragePath: StoragePath - pub let ContainerPublicPath: PublicPath +access(all) contract FlowtyDrops { + access(all) let ContainerStoragePath: StoragePath + access(all) let ContainerPublicPath: PublicPath - pub let MinterStoragePath: StoragePath - pub let MinterPrivatePath: PrivatePath + access(all) let MinterStoragePath: StoragePath - pub event DropAdded(address: Address, id: UInt64, name: String, description: String, imageUrl: String, start: UInt64?, end: UInt64?) - pub event Minted(address: Address, dropID: UInt64, phaseID: UInt64, nftID: UInt64, nftType: String) - pub event PhaseAdded(dropID: UInt64, dropAddress: Address, id: UInt64, index: Int, switcherType: String, pricerType: String, addressVerifierType: String) - pub event PhaseRemoved(dropID: UInt64, dropAddress: Address, id: UInt64) + access(all) event DropAdded(address: Address, id: UInt64, name: String, description: String, imageUrl: String, start: UInt64?, end: UInt64?) + 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 PhaseRemoved(dropID: UInt64, dropAddress: Address, id: UInt64) + + access(all) entitlement Owner + access(all) entitlement EditPhase // Interface to expose all the components necessary to participate in a drop // and to ask questions about a drop. - pub resource interface DropPublic { - pub fun borrowPhasePublic(index: Int): &{PhasePublic} - pub fun borrowActivePhases(): [&{PhasePublic}] - pub fun borrowAllPhases(): [&{PhasePublic}] - pub fun mint( - payment: @FungibleToken.Vault, + access(all) resource interface DropPublic { + access(all) fun borrowPhasePublic(index: Int): &{PhasePublic} + access(all) fun borrowActivePhases(): [&{PhasePublic}] + access(all) fun borrowAllPhases(): [&{PhasePublic}] + access(all) fun mint( + payment: @{FungibleToken.Vault}, amount: Int, phaseIndex: Int, expectedType: Type, receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>, commissionReceiver: Capability<&{FungibleToken.Receiver}>, data: {String: AnyStruct} - ): @FungibleToken.Vault - pub fun getDetails(): DropDetails + ): @{FungibleToken.Vault} + access(all) fun getDetails(): DropDetails } - pub resource Drop: DropPublic { + access(all) resource Drop: DropPublic { + access(all) event ResourceDestroyed( + uuid: UInt64 = self.uuid, + minterAddress: Address = self.minterCap.address, + nftType: String = self.details.nftType.identifier, + totalMinted: Int = self.details.totalMinted + ) + // phases represent the stages of a drop. For example, a drop might have an allowlist and a public mint phase. access(self) let phases: @[Phase] // the details of a drop. This includes things like display information and total number of mints access(self) let details: DropDetails access(self) let minterCap: Capability<&{Minter}> - pub fun mint( - payment: @FungibleToken.Vault, + access(all) fun mint( + payment: @{FungibleToken.Vault}, amount: Int, phaseIndex: Int, expectedType: Type, receiverCap: Capability<&{NonFungibleToken.CollectionPublic}>, commissionReceiver: Capability<&{FungibleToken.Receiver}>, data: {String: AnyStruct} - ): @FungibleToken.Vault { + ): @{FungibleToken.Vault} { pre { - expectedType.isSubtype(of: Type<@NonFungibleToken.NFT>()): "expected type must be an NFT" + expectedType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()): "expected type must be an NFT" self.phases.length > phaseIndex: "phase index is too high" receiverCap.check(): "receiver capability is not valid" } // validate the payment vault amount and type - let phase = &self.phases[phaseIndex] as! &Phase + let phase: &Phase = &self.phases[phaseIndex] assert( phase.details.addressVerifier.canMint(addr: receiverCap.address, num: amount, totalMinted: self.details.minters[receiverCap.address] ?? 0, data: {}), message: "receiver address has exceeded their mint capacity" @@ -98,16 +107,16 @@ pub contract FlowtyDrops { return <- payment } - pub fun borrowPhase(index: Int): &Phase { - return &self.phases[index] as! &Phase + access(Owner) fun borrowPhase(index: Int): auth(EditPhase) &Phase { + return &self.phases[index] } - pub fun borrowPhasePublic(index: Int): &{PhasePublic} { - return &self.phases[index] as! &{PhasePublic} + access(all) fun borrowPhasePublic(index: Int): &{PhasePublic} { + return &self.phases[index] } - pub fun borrowActivePhases(): [&{PhasePublic}] { + access(all) fun borrowActivePhases(): [&{PhasePublic}] { let arr: [&{PhasePublic}] = [] var count = 0 while count < self.phases.length { @@ -123,7 +132,7 @@ pub contract FlowtyDrops { return arr } - pub fun borrowAllPhases(): [&{PhasePublic}] { + access(all) fun borrowAllPhases(): [&{PhasePublic}] { let arr: [&{PhasePublic}] = [] var index = 0 while index < self.phases.length { @@ -135,7 +144,7 @@ pub contract FlowtyDrops { return arr } - pub fun addPhase(_ phase: @Phase) { + access(all) fun addPhase(_ phase: @Phase) { emit PhaseAdded( dropID: self.uuid, dropAddress: self.owner!.address, @@ -148,7 +157,7 @@ pub contract FlowtyDrops { self.phases.append(<-phase) } - pub fun removePhase(index: Int): @Phase { + access(all) fun removePhase(index: Int): @Phase { pre { self.phases.length > index: "index is greater than length of phases" } @@ -159,7 +168,7 @@ pub contract FlowtyDrops { return <- phase } - pub fun getDetails(): DropDetails { + access(all) fun getDetails(): DropDetails { return self.details } @@ -172,19 +181,15 @@ pub contract FlowtyDrops { self.details = details self.minterCap = minterCap } - - destroy () { - destroy self.phases - } } - pub struct DropDetails { - pub let display: MetadataViews.Display - pub let medias: MetadataViews.Medias? - pub var totalMinted: Int - pub var minters: {Address: Int} - pub let commissionRate: UFix64 - pub let nftType: Type + access(all) struct DropDetails { + access(all) let display: MetadataViews.Display + access(all) let medias: MetadataViews.Medias? + access(all) var totalMinted: Int + access(all) var minters: {Address: Int} + access(all) let commissionRate: UFix64 + access(all) let nftType: Type access(contract) fun addMinted(num: Int, addr: Address) { self.totalMinted = self.totalMinted + num @@ -207,43 +212,45 @@ pub contract FlowtyDrops { // A switcher represents a phase being on or off, and holds information // about whether a phase has started or not. - pub struct interface Switcher { + access(all) struct interface Switcher { // Signal that a phase has started. If the phase has not ended, it means that this switcher's phase // is active - pub fun hasStarted(): Bool + access(all) view fun hasStarted(): Bool // Signal that a phase has ended. If a switcher has ended, minting will not work. That could mean // the drop is over, or it could mean another phase has begun. - pub fun hasEnded(): Bool + access(all) view fun hasEnded(): Bool - pub fun getStart(): UInt64? - pub fun getEnd(): UInt64? + access(all) view fun getStart(): UInt64? + 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. - pub resource Phase: PhasePublic { - pub let details: PhaseDetails + 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. - pub fun isActive(): Bool { + access(all) fun isActive(): Bool { return self.details.switcher.hasStarted() && !self.details.switcher.hasEnded() } - pub fun getDetails(): PhaseDetails { + access(all) fun getDetails(): PhaseDetails { return self.details } - pub fun borrowSwitchAuth(): auth &{Switcher} { - return &self.details.switcher as! auth &{Switcher} + access(EditPhase) fun borrowSwitchAuth(): auth(Mutate) &{Switcher} { + return &self.details.switcher } - pub fun borrowPricerAuth(): auth &{Pricer} { - return &self.details.pricer as! auth &{Pricer} + access(EditPhase) fun borrowPricerAuth(): auth(Mutate) &{Pricer} { + return &self.details.pricer } - pub fun borrowAddressVerifierAuth(): auth &{AddressVerifier} { - return &self.details.addressVerifier as! auth &{AddressVerifier} + access(EditPhase) fun borrowAddressVerifierAuth(): auth(Mutate) &{AddressVerifier} { + return &self.details.addressVerifier } init(details: PhaseDetails) { @@ -251,31 +258,31 @@ pub contract FlowtyDrops { } } - pub resource interface PhasePublic { - pub fun getDetails(): PhaseDetails - pub fun isActive(): Bool + 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? // - How many items are left in the current phase? // - Can Address x mint on a phase? // - What is the cost to mint for the phase I am interested in (for address x)? + access(all) fun getDetails(): PhaseDetails + access(all) fun isActive(): Bool } - pub struct PhaseDetails { + access(all) struct PhaseDetails { // handles whether a phase is on or not - pub let switcher: {Switcher} + access(all) let switcher: {Switcher} // display information about a phase - pub let display: MetadataViews.Display? + access(all) let display: MetadataViews.Display? // handles the pricing of a phase - pub let pricer: {Pricer} + access(all) let pricer: {Pricer} // verifies whether an address is able to mint - pub let addressVerifier: {AddressVerifier} + access(all) let addressVerifier: {AddressVerifier} // placecholder data dictionary to allow new fields to be accessed - pub let data: {String: AnyStruct} + access(all) let data: {String: AnyStruct} init(switcher: {Switcher}, display: MetadataViews.Display?, pricer: {Pricer}, addressVerifier: {AddressVerifier}) { self.switcher = switcher @@ -287,23 +294,23 @@ pub contract FlowtyDrops { } } - pub struct interface AddressVerifier { - pub fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { + access(all) struct interface AddressVerifier { + access(all) fun canMint(addr: Address, num: Int, totalMinted: Int, data: {String: AnyStruct}): Bool { return true } - pub fun remainingForAddress(addr: Address, totalMinted: Int): Int? { + access(all) fun remainingForAddress(addr: Address, totalMinted: Int): Int? { return nil } } - pub struct interface Pricer { - pub fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 - pub fun getPaymentTypes(): [Type] + access(all) struct interface Pricer { + access(all) fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 + access(all) fun getPaymentTypes(): [Type] } - pub resource interface Minter { - pub fun mint(payment: @FungibleToken.Vault, amount: Int, phase: &Phase, data: {String: AnyStruct}): @[NonFungibleToken.NFT] { + access(all) resource interface Minter { + access(all) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] { post { phase.details.switcher.hasStarted() && !phase.details.switcher.hasEnded(): "phase is not active" result.length == amount: "incorrect number of items returned" @@ -311,10 +318,10 @@ pub contract FlowtyDrops { } } - pub struct DropResolver { + access(all) struct DropResolver { access(self) let cap: Capability<&{ContainerPublic}> - pub fun borrowContainer(): &{ContainerPublic}? { + access(all) fun borrowContainer(): &{ContainerPublic}? { return self.cap.borrow() } @@ -327,16 +334,16 @@ pub contract FlowtyDrops { } } - pub resource interface ContainerPublic { - pub fun borrowDropPublic(id: UInt64): &{DropPublic}? - pub fun getIDs(): [UInt64] + access(all) resource interface ContainerPublic { + access(all) fun borrowDropPublic(id: UInt64): &{DropPublic}? + access(all) fun getIDs(): [UInt64] } // Contains drops. - pub resource Container: ContainerPublic { - pub let drops: @{UInt64: Drop} + access(all) resource Container: ContainerPublic { + access(self) let drops: @{UInt64: Drop} - pub fun addDrop(_ drop: @Drop) { + access(Owner) fun addDrop(_ drop: @Drop) { let details = drop.getDetails() let phases = drop.borrowAllPhases() @@ -356,7 +363,7 @@ pub contract FlowtyDrops { destroy self.drops.insert(key: drop.uuid, <-drop) } - pub fun removeDrop(id: UInt64): @Drop { + access(Owner) fun removeDrop(id: UInt64): @Drop { pre { self.drops.containsKey(id): "drop was not found" } @@ -364,36 +371,32 @@ pub contract FlowtyDrops { return <- self.drops.remove(key: id)! } - pub fun borrowDrop(id: UInt64): &Drop? { - return &self.drops[id] as &Drop? + access(Owner) fun borrowDrop(id: UInt64): auth(Owner) &Drop? { + return &self.drops[id] } - pub fun borrowDropPublic(id: UInt64): &{DropPublic}? { - return &self.drops[id] as &{DropPublic}? + access(all) fun borrowDropPublic(id: UInt64): &{DropPublic}? { + return &self.drops[id] } - pub fun getIDs(): [UInt64] { + access(all) fun getIDs(): [UInt64] { return self.drops.keys } init() { self.drops <- {} } - - destroy () { - destroy self.drops - } } - pub fun createPhase(details: PhaseDetails): @Phase { + access(all) fun createPhase(details: PhaseDetails): @Phase { return <- create Phase(details: details) } - pub fun createDrop(details: DropDetails, minterCap: Capability<&{Minter}>, phases: @[Phase]): @Drop { + access(all) fun createDrop(details: DropDetails, minterCap: Capability<&{Minter}>, phases: @[Phase]): @Drop { return <- create Drop(details: details, minterCap: minterCap, phases: <- phases) } - pub fun createContainer(): @Container { + access(all) fun createContainer(): @Container { return <- create Container() } @@ -405,7 +408,6 @@ pub contract FlowtyDrops { self.ContainerStoragePath = StoragePath(identifier: containerIdentifier)! self.ContainerPublicPath = PublicPath(identifier: containerIdentifier)! - self.MinterPrivatePath = PrivatePath(identifier: minterIdentifier)! self.MinterStoragePath = StoragePath(identifier: minterIdentifier)! } } \ No newline at end of file diff --git a/contracts/FlowtyPricers.cdc b/contracts/FlowtyPricers.cdc index 08cdfc4..a8b0b7d 100644 --- a/contracts/FlowtyPricers.cdc +++ b/contracts/FlowtyPricers.cdc @@ -5,25 +5,25 @@ import "FlowToken" This contract contains implementations of the FlowtyDrops.Pricer interface. You can use these, or any custom implementation for the phases of your drop. */ -pub contract FlowtyPricers { +access(all) contract FlowtyPricers { /* The FlatPrice Pricer implementation has a set price and token type. Every mint is the same cost regardless of the number minter, or what address is minting */ - pub struct FlatPrice: FlowtyDrops.Pricer { - pub var price: UFix64 - pub let paymentTokenType: Type + access(all) struct FlatPrice: FlowtyDrops.Pricer { + access(all) var price: UFix64 + access(all) let paymentTokenType: Type - pub fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 { + access(all) view fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 { return self.price * UFix64(num) } - pub fun getPaymentTypes(): [Type] { + access(all) view fun getPaymentTypes(): [Type] { return [self.paymentTokenType] } - pub fun setPrice(price: UFix64) { + access(Mutate) fun setPrice(price: UFix64) { self.price = price } @@ -36,12 +36,12 @@ pub contract FlowtyPricers { /* The Free Pricer can be used for a free mint, it has no price and always marks its payment type as @FlowToken.Vault */ - pub struct Free: FlowtyDrops.Pricer { - pub fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 { + access(all) struct Free: FlowtyDrops.Pricer { + access(all) fun getPrice(num: Int, paymentTokenType: Type, minter: Address?): UFix64 { return 0.0 } - pub fun getPaymentTypes(): [Type] { + access(all) fun getPaymentTypes(): [Type] { return [Type<@FlowToken.Vault>()] } } diff --git a/contracts/FlowtySwitchers.cdc b/contracts/FlowtySwitchers.cdc index fc0178b..39e824b 100644 --- a/contracts/FlowtySwitchers.cdc +++ b/contracts/FlowtySwitchers.cdc @@ -4,24 +4,24 @@ 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 */ -pub contract FlowtySwitchers { +access(all) contract FlowtySwitchers { /* The AlwaysOn Switcher is always on and never ends. */ - pub struct AlwaysOn: FlowtyDrops.Switcher { - pub fun hasStarted(): Bool { + access(all) struct AlwaysOn: FlowtyDrops.Switcher { + access(all) view fun hasStarted(): Bool { return true } - pub fun hasEnded(): Bool { + access(all) view fun hasEnded(): Bool { return false } - pub fun getStart(): UInt64? { + access(all) view fun getStart(): UInt64? { return nil } - pub fun getEnd(): UInt64? { + access(all) view fun getEnd(): UInt64? { return nil } } @@ -30,31 +30,31 @@ pub 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 */ - pub struct ManualSwitch: FlowtyDrops.Switcher { + access(all) struct ManualSwitch: FlowtyDrops.Switcher { access(self) var started: Bool access(self) var ended: Bool - pub fun hasStarted(): Bool { + access(all) view fun hasStarted(): Bool { return self.started } - pub fun hasEnded(): Bool { + access(all) view fun hasEnded(): Bool { return self.ended } - pub fun getStart(): UInt64? { + access(all) view fun getStart(): UInt64? { return nil } - pub fun getEnd(): UInt64? { + access(all) view fun getEnd(): UInt64? { return nil } - pub fun setStarted(_ b: Bool) { + access(Mutate) fun setStarted(_ b: Bool) { self.started = b } - pub fun setEnded(_ b: Bool) { + access(Mutate) fun setEnded(_ b: Bool) { self.ended = b } @@ -68,16 +68,16 @@ pub 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. */ - pub struct TimestampSwitch: FlowtyDrops.Switcher { - pub var start: UInt64? - pub var end: UInt64? + access(all) struct TimestampSwitch: FlowtyDrops.Switcher { + access(all) var start: UInt64? + access(all) var end: UInt64? - pub fun hasStarted(): Bool { + access(all) view fun hasStarted(): Bool { return self.start == nil || UInt64(getCurrentBlock().timestamp) >= self.start! } - pub fun hasEnded(): Bool { + access(all) view fun hasEnded(): Bool { if self.end == nil { return false } @@ -85,19 +85,19 @@ pub contract FlowtySwitchers { return UInt64(getCurrentBlock().timestamp) > self.end! } - pub fun getStart(): UInt64? { + access(all) view fun getStart(): UInt64? { return self.start } - pub fun getEnd(): UInt64? { + access(all) view fun getEnd(): UInt64? { return self.end } - pub fun setStart(start: UInt64?) { + access(Mutate) fun setStart(start: UInt64?) { self.start = start } - pub fun setEnd(end: UInt64?) { + access(Mutate) fun setEnd(end: UInt64?) { self.end = end } diff --git a/contracts/nft/OpenEditionNFT.cdc b/contracts/nft/OpenEditionNFT.cdc index 5a51c8d..512e64c 100644 --- a/contracts/nft/OpenEditionNFT.cdc +++ b/contracts/nft/OpenEditionNFT.cdc @@ -14,7 +14,6 @@ import "MetadataViews" import "ViewResolver" import "FungibleToken" import "FlowToken" -import "ExampleToken" import "FlowtyDrops" import "FlowtySwitchers" @@ -22,31 +21,31 @@ import "FlowtyAddressVerifiers" import "FlowtyPricers" import "DropFactory" -pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { +access(all) contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// Total supply of ExampleNFTs in existence - pub var totalSupply: UInt64 + access(all) var totalSupply: UInt64 /// The event that is emitted when the contract is created - pub event ContractInitialized() + access(all) event ContractInitialized() /// The event that is emitted when an NFT is withdrawn from a Collection - pub event Withdraw(id: UInt64, from: Address?) + access(all) event Withdraw(id: UInt64, from: Address?) /// The event that is emitted when an NFT is deposited to a Collection - pub event Deposit(id: UInt64, to: Address?) + access(all) event Deposit(id: UInt64, to: Address?) /// Storage and Public Paths - pub let CollectionStoragePath: StoragePath - pub let CollectionPublicPath: PublicPath + access(all) let CollectionStoragePath: StoragePath + access(all) let CollectionPublicPath: PublicPath /// The core resource that represents a Non Fungible Token. /// New instances will be created using the NFTMinter resource /// and stored in the Collection resource /// - pub resource NFT: NonFungibleToken.INFT, MetadataViews.Resolver { - pub let id: UInt64 - pub let display: MetadataViews.Display + access(all) resource NFT: NonFungibleToken.NFT { + access(all) let id: UInt64 + access(all) let display: MetadataViews.Display init() { OpenEditionNFT.totalSupply = OpenEditionNFT.totalSupply + 1 @@ -64,7 +63,7 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @return An array of Types defining the implemented views. This value will be used by /// developers to know which parameter to pass to the resolveView() method. /// - pub fun getViews(): [Type] { + access(all) view fun getViews(): [Type] { return [ Type(), Type(), @@ -79,7 +78,7 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @param view: The Type of the desired view. /// @return A structure representing the requested view. /// - pub fun resolveView(_ view: Type): AnyStruct? { + access(all) fun resolveView(_ view: Type): AnyStruct? { switch view { case Type(): return self.display @@ -90,22 +89,27 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { case Type(): return MetadataViews.ExternalURL("https://flowty.io/asset/".concat(OpenEditionNFT.account.address.toString()).concat("/OpenEditionNFT/").concat(self.id.toString())) case Type(): - return OpenEditionNFT.resolveView(view) + return OpenEditionNFT.resolveContractView(resourceType: self.getType(), viewType: view) case Type(): - return OpenEditionNFT.resolveView(view) + return OpenEditionNFT.resolveContractView(resourceType: self.getType(), viewType: view) } + return nil } + + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- create Collection() + } } /// The resource that will be holding the NFTs inside any account. /// In order to be able to manage NFTs any account will need to create /// an empty collection first /// - pub resource Collection: NonFungibleToken.Provider, NonFungibleToken.Receiver, NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection { + access(all) resource Collection: NonFungibleToken.Collection { // dictionary of NFT conforming tokens // NFT is a resource type with an `UInt64` ID field - pub var ownedNFTs: @{UInt64: NonFungibleToken.NFT} + access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} init () { self.ownedNFTs <- {} @@ -116,7 +120,7 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @param withdrawID: The ID of the NFT that wants to be withdrawn /// @return The NFT resource that has been taken out of the collection /// - pub fun withdraw(withdrawID: UInt64): @NonFungibleToken.NFT { + access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { let token <- self.ownedNFTs.remove(key: withdrawID) ?? panic("missing NFT") emit Withdraw(id: token.id, from: self.owner?.address) @@ -128,8 +132,8 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// /// @param token: The NFT resource to be included in the collection /// - pub fun deposit(token: @NonFungibleToken.NFT) { - let token <- token as! @OpenEditionNFT.NFT + access(all) fun deposit(token: @{NonFungibleToken.NFT}) { + assert(token.getType() == Type<@NFT>(), message: "invalid deposited nft type") let id: UInt64 = token.id @@ -141,11 +145,19 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { destroy oldToken } + access(all) view fun getSupportedNFTTypes(): {Type: Bool} { + return {Type<@NFT>(): true} + } + + access(all) view fun isSupportedNFTType(type: Type): Bool { + return type == Type<@NFT>() + } + /// Helper method for getting the collection IDs /// /// @return An array containing the IDs of the NFTs in the collection /// - pub fun getIDs(): [UInt64] { + access(all) view fun getIDs(): [UInt64] { return self.ownedNFTs.keys } @@ -155,24 +167,8 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @param id: The ID of the wanted NFT /// @return A reference to the wanted NFT resource /// - pub fun borrowNFT(id: UInt64): &NonFungibleToken.NFT { - return (&self.ownedNFTs[id] as &NonFungibleToken.NFT?)! - } - - /// Gets a reference to an NFT in the collection so that - /// the caller can read its metadata and call its methods - /// - /// @param id: The ID of the wanted NFT - /// @return A reference to the wanted NFT resource - /// - pub fun borrowExampleNFT(id: UInt64): &OpenEditionNFT.NFT? { - if self.ownedNFTs[id] != nil { - // Create an authorized reference to allow downcasting - let ref = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! - return ref as! &OpenEditionNFT.NFT - } - - return nil + access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { + return &self.ownedNFTs[id] } /// Gets a reference to the NFT only conforming to the `{MetadataViews.Resolver}` @@ -182,14 +178,14 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @param id: The ID of the wanted NFT /// @return The resource reference conforming to the Resolver interface /// - pub fun borrowViewResolver(id: UInt64): &{MetadataViews.Resolver} { - let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)! - let OpenEditionNFT = nft as! &OpenEditionNFT.NFT - return OpenEditionNFT + access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver} { + let tmp = (&self.ownedNFTs[id] as &{NonFungibleToken.NFT}?)! + let nft = tmp as! &OpenEditionNFT.NFT + return tmp } - destroy() { - destroy self.ownedNFTs + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- create Collection() } } @@ -197,27 +193,24 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// /// @return The new Collection resource /// - pub fun createEmptyCollection(): @NonFungibleToken.Collection { + access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { return <- create Collection() } /// Resource that an admin or something similar would own to be /// able to mint new NFTs /// - pub resource NFTMinter: FlowtyDrops.Minter { - pub fun mint(payment: @FungibleToken.Vault, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[NonFungibleToken.NFT] { + access(all) resource NFTMinter: FlowtyDrops.Minter { + access(all) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] { switch(payment.getType()) { case Type<@FlowToken.Vault>(): - OpenEditionNFT.account.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-payment) - break - case Type<@ExampleToken.Vault>(): - OpenEditionNFT.account.borrow<&{FungibleToken.Receiver}>(from: /storage/exampleTokenVault)!.deposit(from: <-payment) + OpenEditionNFT.account.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-payment) break default: panic("unsupported payment token type") } - let nfts: @[NonFungibleToken.NFT] <- [] + let nfts: @[{NonFungibleToken.NFT}] <- [] var count = 0 while count < amount { @@ -234,18 +227,16 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @param view: The Type of the desired view. /// @return A structure representing the requested view. /// - pub fun resolveView(_ view: Type): AnyStruct? { - switch view { + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { case Type(): return MetadataViews.NFTCollectionData( storagePath: OpenEditionNFT.CollectionStoragePath, publicPath: OpenEditionNFT.CollectionPublicPath, - providerPath: /private/openEditionNFT, - publicCollection: Type<&OpenEditionNFT.Collection{NonFungibleToken.CollectionPublic}>(), - publicLinkedType: Type<&OpenEditionNFT.Collection{NonFungibleToken.CollectionPublic,NonFungibleToken.Receiver,MetadataViews.ResolverCollection}>(), - providerLinkedType: Type<&OpenEditionNFT.Collection{NonFungibleToken.CollectionPublic,NonFungibleToken.Provider,MetadataViews.ResolverCollection}>(), - createEmptyCollectionFunction: (fun (): @NonFungibleToken.Collection { - return <-OpenEditionNFT.createEmptyCollection() + publicCollection: Type<&{NonFungibleToken.Collection}>(), + publicLinkedType: Type<&{NonFungibleToken.Collection}>(), + createEmptyCollectionFunction: (fun (): @{NonFungibleToken.Collection} { + return <-OpenEditionNFT.createEmptyCollection(nftType: resourceType ?? Type<@NFT>()) }) ) case Type(): @@ -276,7 +267,7 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { } ) case Type(): - return FlowtyDrops.DropResolver(cap: OpenEditionNFT.account.getCapability<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath)) + return FlowtyDrops.DropResolver(cap: OpenEditionNFT.account.capabilities.get<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath)) } return nil } @@ -286,7 +277,7 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { /// @return An array of Types defining the implemented views. This value will be used by /// developers to know which parameter to pass to the resolveView() method. /// - pub fun getViews(): [Type] { + access(all) view fun getContractViews(resourceType: Type?): [Type] { return [ Type(), Type() @@ -303,19 +294,18 @@ pub contract OpenEditionNFT: NonFungibleToken, ViewResolver { // Create a Collection resource and save it to storage let collection <- create Collection() - self.account.save(<-collection, to: self.CollectionStoragePath) + self.account.storage.save(<-collection, to: self.CollectionStoragePath) // create a public capability for the collection - self.account.link<&OpenEditionNFT.Collection{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>( - self.CollectionPublicPath, - target: self.CollectionStoragePath + self.account.capabilities.publish( + self.account.capabilities.storage.issue<&{NonFungibleToken.Collection}>(self.CollectionStoragePath), + at: self.CollectionPublicPath ) // Create a Minter resource and save it to storage let minter <- create NFTMinter() - self.account.save(<-minter, to: FlowtyDrops.MinterStoragePath) - let minterCap = self.account.link<&NFTMinter{FlowtyDrops.Minter}>(FlowtyDrops.MinterPrivatePath, target: FlowtyDrops.MinterStoragePath) - ?? panic("unable to link minter capability") + self.account.storage.save(<-minter, to: FlowtyDrops.MinterStoragePath) + self.account.capabilities.storage.issue<&{FlowtyDrops.Minter}>(FlowtyDrops.MinterStoragePath) emit ContractInitialized() } diff --git a/contracts/standard/ExampleToken.cdc b/contracts/standard/ExampleToken.cdc deleted file mode 100644 index d45ec7e..0000000 --- a/contracts/standard/ExampleToken.cdc +++ /dev/null @@ -1,217 +0,0 @@ -import "FungibleToken" - -pub contract ExampleToken: FungibleToken { - - /// Total supply of ExampleTokens in existence - pub var totalSupply: UFix64 - - /// TokensInitialized - /// - /// The event that is emitted when the contract is created - pub event TokensInitialized(initialSupply: UFix64) - - /// TokensWithdrawn - /// - /// The event that is emitted when tokens are withdrawn from a Vault - pub event TokensWithdrawn(amount: UFix64, from: Address?) - - /// TokensDeposited - /// - /// The event that is emitted when tokens are deposited to a Vault - pub event TokensDeposited(amount: UFix64, to: Address?) - - /// TokensMinted - /// - /// The event that is emitted when new tokens are minted - pub event TokensMinted(amount: UFix64) - - /// TokensBurned - /// - /// The event that is emitted when tokens are destroyed - pub event TokensBurned(amount: UFix64) - - /// MinterCreated - /// - /// The event that is emitted when a new minter resource is created - pub event MinterCreated(allowedAmount: UFix64) - - /// BurnerCreated - /// - /// The event that is emitted when a new burner resource is created - pub event BurnerCreated() - - /// Vault - /// - /// Each user stores an instance of only the Vault in their storage - /// The functions in the Vault and governed by the pre and post conditions - /// in FungibleToken when they are called. - /// The checks happen at runtime whenever a function is called. - /// - /// Resources can only be created in the context of the contract that they - /// are defined in, so there is no way for a malicious user to create Vaults - /// out of thin air. A special Minter resource needs to be defined to mint - /// new tokens. - /// - pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance { - - /// The total balance of this vault - pub var balance: UFix64 - - // initialize the balance at resource creation time - init(balance: UFix64) { - self.balance = balance - } - - /// withdraw - /// - /// Function that takes an amount as an argument - /// and withdraws that amount from the Vault. - /// - /// It creates a new temporary Vault that is used to hold - /// the money that is being transferred. It returns the newly - /// created Vault to the context that called so it can be deposited - /// elsewhere. - /// - pub fun withdraw(amount: UFix64): @FungibleToken.Vault { - self.balance = self.balance - amount - emit TokensWithdrawn(amount: amount, from: self.owner?.address) - return <-create Vault(balance: amount) - } - - /// deposit - /// - /// Function that takes a Vault object as an argument and adds - /// its balance to the balance of the owners Vault. - /// - /// It is allowed to destroy the sent Vault because the Vault - /// was a temporary holder of the tokens. The Vault's balance has - /// been consumed and therefore can be destroyed. - /// - pub fun deposit(from: @FungibleToken.Vault) { - let vault <- from as! @ExampleToken.Vault - self.balance = self.balance + vault.balance - emit TokensDeposited(amount: vault.balance, to: self.owner?.address) - vault.balance = 0.0 - destroy vault - } - - destroy() { - ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance - } - } - - /// createEmptyVault - /// - /// Function that creates a new Vault with a balance of zero - /// and returns it to the calling context. A user must call this function - /// and store the returned Vault in their storage in order to allow their - /// account to be able to receive deposits of this token type. - /// - pub fun createEmptyVault(): @Vault { - return <-create Vault(balance: 0.0) - } - - pub resource Administrator { - - /// createNewMinter - /// - /// Function that creates and returns a new minter resource - /// - pub fun createNewMinter(allowedAmount: UFix64): @Minter { - emit MinterCreated(allowedAmount: allowedAmount) - return <-create Minter(allowedAmount: allowedAmount) - } - - /// createNewBurner - /// - /// Function that creates and returns a new burner resource - /// - pub fun createNewBurner(): @Burner { - emit BurnerCreated() - return <-create Burner() - } - } - - /// Minter - /// - /// Resource object that token admin accounts can hold to mint new tokens. - /// - pub resource Minter { - - /// The amount of tokens that the minter is allowed to mint - pub var allowedAmount: UFix64 - - /// mintTokens - /// - /// Function that mints new tokens, adds them to the total supply, - /// and returns them to the calling context. - /// - pub fun mintTokens(amount: UFix64): @ExampleToken.Vault { - pre { - amount > 0.0: "Amount minted must be greater than zero" - amount <= self.allowedAmount: "Amount minted must be less than the allowed amount" - } - ExampleToken.totalSupply = ExampleToken.totalSupply + amount - self.allowedAmount = self.allowedAmount - amount - emit TokensMinted(amount: amount) - return <-create Vault(balance: amount) - } - - init(allowedAmount: UFix64) { - self.allowedAmount = allowedAmount - } - } - - /// Burner - /// - /// Resource object that token admin accounts can hold to burn tokens. - /// - pub resource Burner { - - /// burnTokens - /// - /// Function that destroys a Vault instance, effectively burning the tokens. - /// - /// Note: the burned tokens are automatically subtracted from the - /// total supply in the Vault destructor. - /// - pub fun burnTokens(from: @FungibleToken.Vault) { - let vault <- from as! @ExampleToken.Vault - let amount = vault.balance - destroy vault - emit TokensBurned(amount: amount) - } - } - - init() { - self.totalSupply = 1000.0 - - // Create the Vault with the total supply of tokens and save it in storage - // - let vault <- create Vault(balance: self.totalSupply) - self.account.save(<-vault, to: /storage/exampleTokenVault) - - // Create a public capability to the stored Vault that only exposes - // the `deposit` method through the `Receiver` interface - // - self.account.link<&{FungibleToken.Receiver}>( - /public/exampleTokenReceiver, - target: /storage/exampleTokenVault - ) - - // Create a public capability to the stored Vault that only exposes - // the `balance` field through the `Balance` interface - // - self.account.link<&ExampleToken.Vault{FungibleToken.Balance}>( - /public/exampleTokenBalance, - target: /storage/exampleTokenVault - ) - - let admin <- create Administrator() - self.account.save(<-admin, to: /storage/exampleTokenAdmin) - - // Emit an event that shows that the contract was initialized - // - emit TokensInitialized(initialSupply: self.totalSupply) - } -} \ No newline at end of file diff --git a/flow.json b/flow.json index a41e0f4..4182d7d 100644 --- a/flow.json +++ b/flow.json @@ -154,6 +154,14 @@ "testnet": "0x7e60df042a9c0868", "mainnet": "0x1654653399040a61" } + }, + "Burner": { + "source": "./node_modules/@flowtyio/flow-contracts/contracts/Burner.cdc", + "aliases": { + "emulator": "0xf8d6e0586b0a20c7", + "testnet": "0x9a0766d93b6608b7", + "mainnet": "0xf233dcee88fe0abe" + } } }, "deployments": { @@ -169,7 +177,8 @@ "NonFungibleToken", "ViewResolver", "MetadataViews", - "ExampleToken" + "ExampleToken", + "Burner" ], "emulator-ft": [ "FungibleToken", diff --git a/package-lock.json b/package-lock.json index 69052c9..8af5fab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,13 +9,13 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@flowtyio/flow-contracts": "github:flowtyio/flow-contracts" + "@flowtyio/flow-contracts": "^0.1.0-beta.21" } }, "node_modules/@flowtyio/flow-contracts": { - "version": "0.0.18", - "resolved": "git+ssh://git@github.com/flowtyio/flow-contracts.git#9db39e2404b9761fc9f51292ae25b060d2ed7283", - "license": "MIT", + "version": "0.1.0-beta.21", + "resolved": "https://registry.npmjs.org/@flowtyio/flow-contracts/-/flow-contracts-0.1.0-beta.21.tgz", + "integrity": "sha512-VHmwlxvjoJw/mNnmg2/BzndngkiIF8LsFNqR2CJh1Zoo5OVII+FqUoZkyOFmZ9ZiB7g9bWH9CCMelNxFhGnLWw==", "dependencies": { "commander": "^11.0.0" }, diff --git a/package.json b/package.json index 664e95d..99c979e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,6 @@ "author": "", "license": "ISC", "dependencies": { - "@flowtyio/flow-contracts": "github:flowtyio/flow-contracts" + "@flowtyio/flow-contracts": "^0.1.0-beta.21" } } diff --git a/run-tests.sh b/run-tests.sh index 1170378..a093fec 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -2,4 +2,4 @@ set -e -flow test --cover --covercode="contracts" --coverprofile="coverage.lcov" tests/*_tests.cdc \ No newline at end of file +flow-c1 test --cover --covercode="contracts" --coverprofile="coverage.lcov" tests/*_tests.cdc diff --git a/scripts/can_mint_at_phase.cdc b/scripts/can_mint_at_phase.cdc index 560f5b4..2f1eac4 100644 --- a/scripts/can_mint_at_phase.cdc +++ b/scripts/can_mint_at_phase.cdc @@ -1,13 +1,13 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, totalMinted: Int, paymentIdentifier: String): Bool { +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, totalMinted: Int, paymentIdentifier: String): Bool { let paymentTokenType = CompositeType(paymentIdentifier)! - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/get_active_phases.cdc b/scripts/get_active_phases.cdc index 67b3729..a575543 100644 --- a/scripts/get_active_phases.cdc +++ b/scripts/get_active_phases.cdc @@ -1,11 +1,11 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64): [UInt64] { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64): [UInt64] { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/get_drop_details.cdc b/scripts/get_drop_details.cdc index 7125342..c96fc5b 100644 --- a/scripts/get_drop_details.cdc +++ b/scripts/get_drop_details.cdc @@ -1,11 +1,11 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64): FlowtyDrops.DropDetails { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64): FlowtyDrops.DropDetails { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/get_drop_ids.cdc b/scripts/get_drop_ids.cdc index ff887d4..a5ed42c 100644 --- a/scripts/get_drop_ids.cdc +++ b/scripts/get_drop_ids.cdc @@ -1,13 +1,13 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String): [UInt64] { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) +access(all) fun main(contractAddress: Address, contractName: String): [UInt64] { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) if resolver == nil { return [] } - let tmp = resolver!.resolveView(Type()) + let tmp = resolver!.resolveContractView(resourceType: nil, viewType: Type()) if tmp == nil { return [] } diff --git a/scripts/get_drop_summaries.cdc b/scripts/get_drop_summaries.cdc index c6d242b..bca8820 100644 --- a/scripts/get_drop_summaries.cdc +++ b/scripts/get_drop_summaries.cdc @@ -1,5 +1,5 @@ import "DropTypes" -pub fun main(contractAddress: Address, contractName: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropTypes.DropSummary] { +access(all) fun main(contractAddress: Address, contractName: String, minter: Address?, quantity: Int?, paymentIdentifier: String?): [DropTypes.DropSummary] { return DropTypes.getAllDropSummaries(contractAddress: contractAddress, contractName: contractName, minter: minter, quantity: quantity, paymentIdentifier: paymentIdentifier) } \ No newline at end of file diff --git a/scripts/get_drop_summary.cdc b/scripts/get_drop_summary.cdc index 77fe539..0c59918 100644 --- a/scripts/get_drop_summary.cdc +++ b/scripts/get_drop_summary.cdc @@ -1,5 +1,5 @@ import "DropTypes" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropTypes.DropSummary? { +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64, minter: Address?, quantity: Int?, paymentIdentifier: String?): DropTypes.DropSummary? { return DropTypes.getDropSummary(contractAddress: contractAddress, contractName: contractName, dropID: dropID, minter: minter, quantity: quantity, paymentIdentifier: paymentIdentifier) } \ No newline at end of file diff --git a/scripts/get_price_at_phase.cdc b/scripts/get_price_at_phase.cdc index fa22dad..060b08f 100644 --- a/scripts/get_price_at_phase.cdc +++ b/scripts/get_price_at_phase.cdc @@ -1,13 +1,13 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, paymentIdentifier: String): UFix64? { +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, paymentIdentifier: String): UFix64? { let paymentTokenType = CompositeType(paymentIdentifier)! - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/has_phase_ended.cdc b/scripts/has_phase_ended.cdc index 20a81c3..0083c6a 100644 --- a/scripts/has_phase_ended.cdc +++ b/scripts/has_phase_ended.cdc @@ -1,11 +1,11 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/has_phase_started.cdc b/scripts/has_phase_started.cdc index 74506b8..d738c7c 100644 --- a/scripts/has_phase_started.cdc +++ b/scripts/has_phase_started.cdc @@ -1,11 +1,11 @@ import "FlowtyDrops" import "ViewResolver" -pub fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) +access(all) fun main(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("contract does not implement ViewResolver interface") - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let container = dropResolver.borrowContainer() ?? panic("drop container not found") diff --git a/scripts/import_all.cdc b/scripts/import_all.cdc index e0e5b11..3c6204b 100644 --- a/scripts/import_all.cdc +++ b/scripts/import_all.cdc @@ -4,6 +4,6 @@ import "FlowtySwitchers" import "FlowtyPricers" import "OpenEditionNFT" -pub fun main(): Bool { +access(all) fun main(): Bool { return true } \ No newline at end of file diff --git a/scripts/util/get_current_time.cdc b/scripts/util/get_current_time.cdc index bded294..0de07e7 100644 --- a/scripts/util/get_current_time.cdc +++ b/scripts/util/get_current_time.cdc @@ -1,3 +1,3 @@ -pub fun main(): UFix64 { +access(all) fun main(): UFix64 { return getCurrentBlock().timestamp } \ No newline at end of file diff --git a/scripts/util/get_minter_controller_id.cdc b/scripts/util/get_minter_controller_id.cdc new file mode 100644 index 0000000..42fb0b3 --- /dev/null +++ b/scripts/util/get_minter_controller_id.cdc @@ -0,0 +1,14 @@ +import "FlowtyDrops" + +access(all) fun main(addr: Address): UInt64? { + let acct = getAuthAccount(addr) + let controllers = acct.capabilities.storage.getControllers(forPath: FlowtyDrops.MinterStoragePath) + + for c in controllers { + if c.borrowType == Type<&{FlowtyDrops.Minter}>() { + return c.capabilityID + } + } + + return nil +} \ No newline at end of file diff --git a/tests/FlowtyAddressVerifiers_tests.cdc b/tests/FlowtyAddressVerifiers_tests.cdc index 5728539..c5e7790 100644 --- a/tests/FlowtyAddressVerifiers_tests.cdc +++ b/tests/FlowtyAddressVerifiers_tests.cdc @@ -2,19 +2,19 @@ import Test import "test_helpers.cdc" import "FlowtyAddressVerifiers" -pub let alice = Test.createAccount() +access(all) let alice = Test.createAccount() -pub fun setup() { +access(all) fun setup() { deployAll() } -pub fun test_FlowtyAddressVerifiers_AllowAll() { +access(all) fun test_FlowtyAddressVerifiers_AllowAll() { let v = FlowtyAddressVerifiers.AllowAll(maxPerMint: 10) Test.assertEqual(true, v.canMint(addr: alice.address, num: 10, totalMinted: 10, data: {})) Test.assertEqual(nil, v.remainingForAddress(addr: alice.address, totalMinted: 10)) } -pub fun test_FlowtyAddressVerifiers_AllowList_InAllowList() { +access(all) fun test_FlowtyAddressVerifiers_AllowList_InAllowList() { let mintNum = 5 let addresses: {Address: Int} = {alice.address: mintNum} let v = FlowtyAddressVerifiers.AllowList(allowedAddresses: addresses) @@ -32,7 +32,7 @@ pub fun test_FlowtyAddressVerifiers_AllowList_InAllowList() { Test.assertEqual(true, v.canMint(addr: alice.address, num: mintNum, totalMinted: 0, data: {})) } -pub fun test_FlowtyAddressVerifiers_AllowList_NotInAllowList() { +access(all) fun test_FlowtyAddressVerifiers_AllowList_NotInAllowList() { let mintNum = 5 let addresses: {Address: Int} = {Test.createAccount().address: mintNum} diff --git a/tests/FlowtyDrops_tests.cdc b/tests/FlowtyDrops_tests.cdc index 287352f..f393ccc 100644 --- a/tests/FlowtyDrops_tests.cdc +++ b/tests/FlowtyDrops_tests.cdc @@ -3,24 +3,23 @@ import "test_helpers.cdc" import "FlowToken" import "FlowtyDrops" import "OpenEditionNFT" -import "ExampleToken" import "DropTypes" -pub let defaultEndlessOpenEditionName = "Default Endless Open Edition" +access(all) let defaultEndlessOpenEditionName = "Default Endless Open Edition" -pub fun setup() { +access(all) fun setup() { deployAll() } -pub fun afterEach() { +access(all) fun afterEach() { txExecutor("drops/remove_all_drops.cdc", [openEditionAccount], [], nil, nil) } -pub fun testImports() { +access(all) fun testImports() { Test.assert(scriptExecutor("import_all.cdc", [])! as! Bool, message: "failed to import all") } -pub fun test_OpenEditionNFT_getPrice() { +access(all) fun test_OpenEditionNFT_getPrice() { let minter = Test.createAccount() let dropID = createDefaultEndlessOpenEditionDrop() @@ -35,19 +34,18 @@ pub fun test_OpenEditionNFT_getPrice() { Test.assertEqual(10.0, priceMultiple) } -pub fun test_OpenEditionNFT_getDetails() { +access(all) fun test_OpenEditionNFT_getDetails() { let dropID = createDefaultEndlessOpenEditionDrop() let details = getDropDetails(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID) Test.assertEqual(details.display.name, defaultEndlessOpenEditionName) } -pub fun test_OpenEditionNFT_mint() { +access(all) fun test_OpenEditionNFT_mint() { let dropID = createDefaultEndlessOpenEditionDrop() let details = getDropDetails(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID) let minter = Test.createAccount() - setupExampleToken(minter) - mintExampleTokens(minter, 100.0) + mintFlowTokens(minter, 100.0) let costPerItem = 1.0 let numToMint = 2 @@ -59,9 +57,9 @@ pub fun test_OpenEditionNFT_mint() { contractName: "OpenEditionNFT", numToMint: numToMint, totalCost: total, - paymentIdentifier: exampleTokenIdentifier(), - paymentStoragePath: exampleTokenStoragePath, - paymentReceiverPath: exampleTokenReceiverPath, + paymentIdentifier: flowTokenIdentifier(), + paymentStoragePath: flowTokenStoragePath, + paymentReceiverPath: flowTokenReceiverPath, dropID: dropID, dropPhaseIndex: 0, nftIdentifier: Type<@OpenEditionNFT.NFT>().identifier, @@ -74,9 +72,9 @@ pub fun test_OpenEditionNFT_mint() { let commissionDepositAmount = total * details.commissionRate let minterDepositAmount = total - commissionDepositAmount - let tokenDeposits = Test.eventsOfType(Type()) - let minterFee = tokenDeposits.removeLast() as! ExampleToken.TokensDeposited - let commissionEvent = tokenDeposits.removeLast() as! ExampleToken.TokensDeposited + let tokenDeposits = Test.eventsOfType(Type()) + let minterFee = tokenDeposits.removeLast() as! FlowToken.TokensDeposited + let commissionEvent = tokenDeposits.removeLast() as! FlowToken.TokensDeposited Test.assertEqual(commissionDepositAmount, commissionEvent.amount) Test.assertEqual(flowtyDropsAccount.address, commissionEvent.to!) @@ -85,7 +83,7 @@ pub fun test_OpenEditionNFT_mint() { Test.assertEqual(openEditionAccount.address, minterFee.to!) } -pub fun test_OpenEditionNFT_EditPhaseDetails() { +access(all) fun test_OpenEditionNFT_EditPhaseDetails() { let dropID = createDefaultTimeBasedOpenEditionDrop() Test.assertEqual(true, hasDropPhaseStarted(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0)) Test.assertEqual(false, hasDropPhaseEnded(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0)) @@ -94,7 +92,7 @@ pub fun test_OpenEditionNFT_EditPhaseDetails() { let currentTime = getCurrentTime() let newStart = UInt64(currentTime + 5.0) - txExecutor("drops/edit_timebased_phase_start_and_end.cdc", [openEditionAccount], [dropID, 0, newStart, newStart + UInt64(5)], nil, nil) + txExecutor("drops/edit_timebased_phase_start_and_end.cdc", [openEditionAccount], [dropID, 0, newStart, newStart + 5], nil, nil) Test.assertEqual(false, hasDropPhaseStarted(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0)) Test.assertEqual(false, hasDropPhaseEnded(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0)) @@ -108,33 +106,33 @@ pub fun test_OpenEditionNFT_EditPhaseDetails() { Test.assertEqual(true, hasDropPhaseEnded(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0)) } -pub fun test_OpenEditionNFT_EditPrice() { +access(all) fun test_OpenEditionNFT_EditPrice() { let dropID = createDefaultTimeBasedOpenEditionDrop() - Test.assertEqual(1.0, getPriceAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 1, paymentIdentifier: exampleTokenIdentifier())) + Test.assertEqual(1.0, getPriceAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 1, paymentIdentifier: flowTokenIdentifier())) txExecutor("drops/edit_flat_price.cdc", [openEditionAccount], [dropID, 0, 2.0], nil, nil) - Test.assertEqual(2.0, getPriceAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 1, paymentIdentifier: exampleTokenIdentifier())) + Test.assertEqual(2.0, getPriceAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 1, paymentIdentifier: flowTokenIdentifier())) } -pub fun test_OpenEditionNFT_EditMaxPerMint() { +access(all) fun test_OpenEditionNFT_EditMaxPerMint() { let dropID = createDefaultTimeBasedOpenEditionDrop() - Test.assertEqual(true, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 10, totalMinted: 0, paymentIdentifier: exampleTokenIdentifier())) + Test.assertEqual(true, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 10, totalMinted: 0, paymentIdentifier: flowTokenIdentifier())) txExecutor("drops/edit_address_verifier_max_per_mint.cdc", [openEditionAccount], [dropID, 0, 5], nil, nil) - Test.assertEqual(false, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 10, totalMinted: 0, paymentIdentifier: exampleTokenIdentifier())) - Test.assertEqual(true, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 5, totalMinted: 0, paymentIdentifier: exampleTokenIdentifier())) + Test.assertEqual(false, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 10, totalMinted: 0, paymentIdentifier: flowTokenIdentifier())) + Test.assertEqual(true, canMintAtPhase(contractAddress: openEditionAccount.address, contractName: "OpenEditionNFT", dropID: dropID, phaseIndex: 0, minter: openEditionAccount.address, numToMint: 5, totalMinted: 0, paymentIdentifier: flowTokenIdentifier())) } -pub fun test_OpenEditionNFT_GetActivePhases() { +access(all) fun test_OpenEditionNFT_GetActivePhases() { let dropID = createDefaultTimeBasedOpenEditionDrop() let activePhaseIDs = scriptExecutor("get_active_phases.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID])! as! [UInt64] Test.assert(activePhaseIDs.length == 1, message: "unexpected active phase length") } -pub fun test_OpenEditionNFT_addPhase() { +access(all) fun test_OpenEditionNFT_addPhase() { let dropID = createDefaultTimeBasedOpenEditionDrop() - txExecutor("drops/add_free_phase.cdc", [openEditionAccount], [dropID, nil, nil], nil, nil) + txExecutor("drops/add_free_phase.cdc", [openEditionAccount], [dropID, nil, nil, "https://example.com/fake_image.jpg"], nil, nil) let phaseEvent = Test.eventsOfType(Type()).removeLast() as! FlowtyDrops.PhaseAdded var activePhaseIDs = scriptExecutor("get_active_phases.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID])! as! [UInt64] @@ -145,14 +143,13 @@ pub fun test_OpenEditionNFT_addPhase() { Test.assert(!activePhaseIDs.contains(phaseEvent.id), message: "unexpected active phase length") } -pub fun test_OpenEditionNFT_getDropSummary() { +access(all) fun test_OpenEditionNFT_getDropSummary() { let dropID = createDefaultTimeBasedOpenEditionDrop() let minter = Test.createAccount() - setupExampleToken(minter) - mintExampleTokens(minter, 100.0) + mintFlowTokens(minter, 100.0) - let summary = scriptExecutor("get_drop_summary.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID, minter.address, 1, exampleTokenIdentifier()])! as! DropTypes.DropSummary + let summary = scriptExecutor("get_drop_summary.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID, minter.address, 1, flowTokenIdentifier()])! as! DropTypes.DropSummary Test.assertEqual(minter.address, summary.address!) let numToMint = 5 @@ -164,40 +161,43 @@ pub fun test_OpenEditionNFT_getDropSummary() { contractName: "OpenEditionNFT", numToMint: numToMint, totalCost: totalCost, - paymentIdentifier: exampleTokenIdentifier(), - paymentStoragePath: exampleTokenStoragePath, - paymentReceiverPath: exampleTokenReceiverPath, + paymentIdentifier: flowTokenIdentifier(), + paymentStoragePath: flowTokenStoragePath, + paymentReceiverPath: flowTokenReceiverPath, dropID: dropID, dropPhaseIndex: 0, nftIdentifier: Type<@OpenEditionNFT.NFT>().identifier, commissionAddress: flowtyDropsAccount.address ) - let summaryAfter = scriptExecutor("get_drop_summary.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID, minter.address, 1, exampleTokenIdentifier()])! as! DropTypes.DropSummary + let summaryAfter = scriptExecutor("get_drop_summary.cdc", [openEditionAccount.address, "OpenEditionNFT", dropID, minter.address, 1, flowTokenIdentifier()])! as! DropTypes.DropSummary Test.assertEqual(summaryAfter.totalMinted, numToMint) Test.assertEqual(summaryAfter.mintedByAddress!, numToMint) Test.assertEqual(1, summaryAfter.phases.length) - let summaries = scriptExecutor("get_drop_summaries.cdc", [openEditionAccount.address, "OpenEditionNFT", minter.address, 1, exampleTokenIdentifier()])! as! [DropTypes.DropSummary] + let summaries = scriptExecutor("get_drop_summaries.cdc", [openEditionAccount.address, "OpenEditionNFT", minter.address, 1, flowTokenIdentifier()])! as! [DropTypes.DropSummary] Test.assertEqual(1, summaries.length) Test.assertEqual(summaries[0].totalMinted, numToMint) Test.assertEqual(summaries[0].mintedByAddress!, numToMint) Test.assertEqual(1, summaries[0].phases.length) } -pub fun test_OpenEditionNFT_getDropSummary_noMinter() { +access(all) fun test_OpenEditionNFT_getDropSummary_noMinter() { let dropID = createDefaultTimeBasedOpenEditionDrop() - let summaries = scriptExecutor("get_drop_summaries.cdc", [openEditionAccount.address, "OpenEditionNFT", nil, 1, exampleTokenIdentifier()])! as! [DropTypes.DropSummary] + let summaries = scriptExecutor("get_drop_summaries.cdc", [openEditionAccount.address, "OpenEditionNFT", nil, 1, flowTokenIdentifier()])! as! [DropTypes.DropSummary] Test.assertEqual(1, summaries.length) Test.assertEqual(summaries[0].totalMinted, 0) Test.assertEqual(summaries[0].mintedByAddress, nil) Test.assertEqual(1, summaries[0].phases.length) } + // ------------------------------------------------------------------------ // Helper functions section -pub fun createDefaultEndlessOpenEditionDrop(): UInt64 { +access(all) fun createDefaultEndlessOpenEditionDrop(): UInt64 { + let controllerID = getMinterControllerID(acct: openEditionAccount)! + return createEndlessOpenEditionDrop( acct: openEditionAccount, name: "Default Endless Open Edition", @@ -205,15 +205,17 @@ pub fun createDefaultEndlessOpenEditionDrop(): UInt64 { ipfsCid: "1234", ipfsPath: nil, price: 1.0, - paymentIdentifier: exampleTokenIdentifier(), - minterPrivatePath: FlowtyDrops.MinterPrivatePath, + paymentIdentifier: flowTokenIdentifier(), + minterControllerID: controllerID, nftTypeIdentifier: openEditionNftIdentifier() ) } -pub fun createDefaultTimeBasedOpenEditionDrop(): UInt64 { +access(all) fun createDefaultTimeBasedOpenEditionDrop(): UInt64 { let currentTime = getCurrentTime() + let controllerID = getMinterControllerID(acct: openEditionAccount)! + return createTimebasedOpenEditionDrop( acct: openEditionAccount, name: "Default Time-based Open Edition", @@ -221,24 +223,24 @@ pub fun createDefaultTimeBasedOpenEditionDrop(): UInt64 { ipfsCid: "1234", ipfsPath: nil, price: 1.0, - paymentIdentifier: exampleTokenIdentifier(), + paymentIdentifier: flowTokenIdentifier(), startUnix: UInt64(getCurrentTime()), endUnix: UInt64(currentTime + 5.0), - minterPrivatePath: FlowtyDrops.MinterPrivatePath, + minterControllerID: controllerID, nftTypeIdentifier: openEditionNftIdentifier() ) } -pub fun getPriceAtPhase(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, paymentIdentifier: String): UFix64 { +access(all) fun getPriceAtPhase(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, paymentIdentifier: String): UFix64 { return scriptExecutor("get_price_at_phase.cdc", [contractAddress, contractName, dropID, phaseIndex, minter, numToMint, paymentIdentifier])! as! UFix64 } -pub fun getDropDetails(contractAddress: Address, contractName: String, dropID: UInt64): FlowtyDrops.DropDetails { +access(all) fun getDropDetails(contractAddress: Address, contractName: String, dropID: UInt64): FlowtyDrops.DropDetails { return scriptExecutor("get_drop_details.cdc", [contractAddress, contractName, dropID])! as! FlowtyDrops.DropDetails } -pub fun mintDrop( - minter: Test.Account, +access(all) fun mintDrop( + minter: Test.TestAccount, contractAddress: Address, contractName: String, numToMint: Int, @@ -260,8 +262,8 @@ pub fun mintDrop( var count = 0 while count < numToMint { count = count + 1 - let event = events.removeFirst() as! FlowtyDrops.Minted - ids.append(event.nftID) + let evt = events.removeFirst() as! FlowtyDrops.Minted + ids.append(evt.nftID) } return ids diff --git a/tests/FlowtyPricers_tests.cdc b/tests/FlowtyPricers_tests.cdc index a4742f8..f5be0ab 100644 --- a/tests/FlowtyPricers_tests.cdc +++ b/tests/FlowtyPricers_tests.cdc @@ -4,17 +4,17 @@ import "FlowtyPricers" import "FlowToken" -pub let placeholder = Test.createAccount() +access(all) let placeholder = Test.createAccount() -pub fun setup() { +access(all) fun setup() { deployAll() } -pub fun test_importAll() { +access(all) fun test_importAll() { scriptExecutor("import_all.cdc", []) } -pub fun test_FlowtyPricers_FlatPrice() { +access(all) fun test_FlowtyPricers_FlatPrice() { let price = 1.1 let pricer = FlowtyPricers.FlatPrice(price: price, paymentTokenType: Type<@FlowToken.Vault>()) @@ -25,7 +25,7 @@ pub fun test_FlowtyPricers_FlatPrice() { Test.assertEqual(cost, price * UFix64(num)) } -pub fun test_FlowtyPricers_Free() { +access(all) fun test_FlowtyPricers_Free() { let pricer = FlowtyPricers.Free() Test.assertEqual(0.0, pricer.getPrice(num: 5, paymentTokenType: Type<@FlowToken.Vault>(), minter: placeholder.address)) } \ No newline at end of file diff --git a/tests/FlowtySwitches_tests.cdc b/tests/FlowtySwitches_tests.cdc index ca19c52..3f9b352 100644 --- a/tests/FlowtySwitches_tests.cdc +++ b/tests/FlowtySwitches_tests.cdc @@ -2,11 +2,11 @@ import Test import "test_helpers.cdc" import "FlowtySwitchers" -pub fun setup() { +access(all) fun setup() { deployAll() } -pub fun test_FlowtySwitchers_AlwaysOn() { +access(all) fun test_FlowtySwitchers_AlwaysOn() { let s = FlowtySwitchers.AlwaysOn() Test.assert(s.hasStarted(), message: "AlwaysOn switcher should always be started") Test.assert(!s.hasEnded(), message: "AlwaysOn switcher never ends") @@ -15,7 +15,7 @@ pub fun test_FlowtySwitchers_AlwaysOn() { Test.assertEqual(nil, s.getEnd()) } -pub fun test_FlowtySwitchers_ManualSwitch() { +access(all) fun test_FlowtySwitchers_ManualSwitch() { let s = FlowtySwitchers.ManualSwitch() Test.assertEqual(false, s.hasStarted()) @@ -33,7 +33,7 @@ pub fun test_FlowtySwitchers_ManualSwitch() { Test.assertEqual(true, s.hasEnded()) } -pub fun test_FlowtySwitchers_TimestampSwitch_StartIsNil() { +access(all) fun test_FlowtySwitchers_TimestampSwitch_StartIsNil() { let end = UInt64(getCurrentBlock().timestamp) + 10 let s = FlowtySwitchers.TimestampSwitch(start: nil, end: end) @@ -44,7 +44,7 @@ pub fun test_FlowtySwitchers_TimestampSwitch_StartIsNil() { Test.assertEqual(end, s.getEnd()!) } -pub fun test_FlowtySwitchers_TimestampSwitch_StartAfterNow() { +access(all) fun test_FlowtySwitchers_TimestampSwitch_StartAfterNow() { let start = UInt64(getCurrentBlock().timestamp) + 1 let end = UInt64(getCurrentBlock().timestamp) + 10 let s = FlowtySwitchers.TimestampSwitch(start: start, end: end) @@ -56,7 +56,7 @@ pub fun test_FlowtySwitchers_TimestampSwitch_StartAfterNow() { Test.assertEqual(end, s.getEnd()!) } -pub fun test_FlowtySwitchers_TimestampSwitch_StartBeforeNow() { +access(all) fun test_FlowtySwitchers_TimestampSwitch_StartBeforeNow() { let start = UInt64(getCurrentBlock().timestamp) - 1 let end = UInt64(getCurrentBlock().timestamp) + 10 let s = FlowtySwitchers.TimestampSwitch(start: start, end: end) @@ -68,7 +68,7 @@ pub fun test_FlowtySwitchers_TimestampSwitch_StartBeforeNow() { Test.assertEqual(end, s.getEnd()!) } -pub fun test_FlowtySwitchers_TimestampSwitch_Ended() { +access(all) fun test_FlowtySwitchers_TimestampSwitch_Ended() { let start = UInt64(getCurrentBlock().timestamp) - 10 let end = UInt64(getCurrentBlock().timestamp) - 1 let s = FlowtySwitchers.TimestampSwitch(start: start, end: end) @@ -77,7 +77,7 @@ pub fun test_FlowtySwitchers_TimestampSwitch_Ended() { Test.assertEqual(true, s.hasEnded()) } -pub fun test_FlowtySwitchers_TimestampSwitch_InvalidStartEnd() { +access(all) fun test_FlowtySwitchers_TimestampSwitch_InvalidStartEnd() { let start: UInt64 = 10 let end: UInt64 = 9 diff --git a/tests/test_helpers.cdc b/tests/test_helpers.cdc index 4acd92d..4a3972c 100644 --- a/tests/test_helpers.cdc +++ b/tests/test_helpers.cdc @@ -3,7 +3,6 @@ import Test import "NonFungibleToken" import "FlowToken" import "FlowtyDrops" -import "ExampleToken" import "OpenEditionNFT" // Helper functions. All of the following were taken from @@ -11,9 +10,8 @@ import "OpenEditionNFT" // - deploy // - scriptExecutor // - txExecutor -// - getErrorMessagePointer -pub fun scriptExecutor(_ scriptName: String, _ arguments: [AnyStruct]): AnyStruct? { +access(all) fun scriptExecutor(_ scriptName: String, _ arguments: [AnyStruct]): AnyStruct? { let scriptCode = loadCode(scriptName, "scripts") let scriptResult = Test.executeScript(scriptCode, arguments) @@ -26,7 +24,7 @@ pub fun scriptExecutor(_ scriptName: String, _ arguments: [AnyStruct]): AnyStruc return scriptResult.returnValue } -pub fun expectScriptFailure(_ scriptName: String, _ arguments: [AnyStruct]): String { +access(all) fun expectScriptFailure(_ scriptName: String, _ arguments: [AnyStruct]): String { let scriptCode = loadCode(scriptName, "scripts") let scriptResult = Test.executeScript(scriptCode, arguments) @@ -34,7 +32,7 @@ pub fun expectScriptFailure(_ scriptName: String, _ arguments: [AnyStruct]): Str return scriptResult.error!.message } -pub fun txExecutor(_ txName: String, _ signers: [Test.Account], _ arguments: [AnyStruct], _ expectedError: String?, _ expectedErrorType: ErrorType?): Test.TransactionResult { +access(all) fun txExecutor(_ txName: String, _ signers: [Test.TestAccount], _ arguments: [AnyStruct], _ expectedError: String?, _ expectedErrorType: ErrorType?): Test.TransactionResult { let txCode = loadCode(txName, "transactions") let authorizers: [Address] = [] @@ -50,121 +48,45 @@ pub fun txExecutor(_ txName: String, _ signers: [Test.Account], _ arguments: [An ) let txResult = Test.executeTransaction(tx) - if let err = txResult.error { - if let expectedErrorMessage = expectedError { - let ptr = getErrorMessagePointer(errorType: expectedErrorType!) - let errMessage = err.message - let hasEmittedCorrectMessage = contains(errMessage, expectedErrorMessage) - let failureMessage = "Expecting - " - .concat(expectedErrorMessage) - .concat("\n") - .concat("But received - ") - .concat(err.message) - assert(hasEmittedCorrectMessage, message: failureMessage) - } - panic(err.message) - } else { - if let expectedErrorMessage = expectedError { - panic("Expecting error - ".concat(expectedErrorMessage).concat(". While no error triggered")) - } + if txResult.error != nil { + panic(txResult.error!.message) } return txResult } -pub fun loadCode(_ fileName: String, _ baseDirectory: String): String { +access(all) fun loadCode(_ fileName: String, _ baseDirectory: String): String { return Test.readFile("../".concat(baseDirectory).concat("/").concat(fileName)) } -pub enum ErrorType: UInt8 { - pub case TX_PANIC - pub case TX_ASSERT - pub case TX_PRE +access(all) enum ErrorType: UInt8 { + access(all) case TX_PANIC + access(all) case TX_ASSERT + access(all) case TX_PRE } -pub fun getErrorMessagePointer(errorType: ErrorType): Int { - switch errorType { - case ErrorType.TX_PANIC: return 159 - case ErrorType.TX_ASSERT: return 170 - case ErrorType.TX_PRE: return 174 - default: panic("Invalid error type") - } - - return 0 -} - -// Copied functions from flow-utils so we can assert on error conditions -// https://github.com/green-goo-dao/flow-utils/blob/main/cadence/contracts/StringUtils.cdc -pub fun contains(_ s: String, _ substr: String): Bool { - if let index = index(s, substr, 0) { - return true - } - return false -} - -// https://github.com/green-goo-dao/flow-utils/blob/main/cadence/contracts/StringUtils.cdc -pub fun index(_ s: String, _ substr: String, _ startIndex: Int): Int? { - for i in range(startIndex, s.length - substr.length + 1) { - if s[i] == substr[0] && s.slice(from: i, upTo: i + substr.length) == substr { - return i - } - } - return nil -} - -// https://github.com/green-goo-dao/flow-utils/blob/main/cadence/contracts/ArrayUtils.cdc -pub fun rangeFunc(_ start: Int, _ end: Int, _ f: ((Int): Void)) { - var current = start - while current < end { - f(current) - current = current + 1 - } -} - -pub fun range(_ start: Int, _ end: Int): [Int] { - let res: [Int] = [] - rangeFunc(start, end, fun (i: Int) { - res.append(i) - }) - return res -} - - // the cadence testing framework allocates 4 addresses for system acounts, // and 10 pre-created accounts for us to use for deployments: -pub let Account0x1 = Address(0x0000000000000001) -pub let Account0x2 = Address(0x0000000000000002) -pub let Account0x3 = Address(0x0000000000000003) -pub let Account0x4 = Address(0x0000000000000004) -pub let Account0x5 = Address(0x0000000000000005) -pub let Account0x6 = Address(0x0000000000000006) -pub let Account0x7 = Address(0x0000000000000007) -pub let Account0x8 = Address(0x0000000000000008) -pub let Account0x9 = Address(0x0000000000000009) -pub let Account0xa = Address(0x000000000000000a) -pub let Account0xb = Address(0x000000000000000b) -pub let Account0xc = Address(0x000000000000000c) -pub let Account0xd = Address(0x000000000000000d) -pub let Account0xe = Address(0x000000000000000e) - -// Example Token constants -pub let exampleTokenStoragePath = /storage/exampleTokenVault -pub let exampleTokenReceiverPath = /public/exampleTokenReceiver -pub let exampleTokenProviderPath = /private/exampleTokenProvider -pub let exampleTokenBalancePath = /public/exampleTokenBalance - -pub let serviceAccount = Test.getAccount(Account0x5) -pub let flowtyDropsAccount = Test.getAccount(Account0x6) -pub let openEditionAccount = Test.getAccount(Account0x7) -pub let exampleTokenAccount = Test.getAccount(Account0x8) +access(all) let Account0x6 = Address(0x0000000000000006) +access(all) let Account0x7 = Address(0x0000000000000007) +access(all) let Account0x8 = Address(0x0000000000000008) +access(all) let Account0x9 = Address(0x0000000000000009) +access(all) let Account0xa = Address(0x000000000000000a) +access(all) let Account0xb = Address(0x000000000000000b) +access(all) let Account0xc = Address(0x000000000000000c) +access(all) let Account0xd = Address(0x000000000000000d) +access(all) let Account0xe = Address(0x000000000000000e) // Flow Token constants -pub let flowTokenStoragePath = /storage/flowTokenVault -pub let flowTokenReceiverPath = /public/flowTokenReceiver +access(all) let flowTokenStoragePath = /storage/flowTokenVault +access(all) let flowTokenReceiverPath = /public/flowTokenReceiver -pub fun deployAll() { - deploy("ExampleToken", "../contracts/standard/ExampleToken.cdc", []) +access(all) let serviceAccount = Test.serviceAccount() +access(all) let flowtyDropsAccount = Test.getAccount(Account0x6) +access(all) let openEditionAccount = Test.getAccount(Account0x7) +access(all) let exampleTokenAccount = Test.getAccount(Account0x8) +access(all) fun deployAll() { // 0x6 deploy("FlowtyDrops", "../contracts/FlowtyDrops.cdc", []) deploy("FlowtySwitchers", "../contracts/FlowtySwitchers.cdc", []) @@ -177,27 +99,23 @@ pub fun deployAll() { // 0x7 deploy("OpenEditionNFT", "../contracts/nft/OpenEditionNFT.cdc", []) - - - setupExampleToken(flowtyDropsAccount) - setupExampleToken(openEditionAccount) } -pub fun deploy(_ name: String, _ path: String, _ arguments: [AnyStruct]) { +access(all) fun deploy(_ name: String, _ path: String, _ arguments: [AnyStruct]) { let err = Test.deployContract(name: name, path: path, arguments: arguments) Test.expect(err, Test.beNil()) } -pub fun heartbeat() { +access(all) fun heartbeat() { txExecutor("util/heartbeat.cdc", [serviceAccount], [], nil, nil) } -pub fun getCurrentTime(): UFix64 { +access(all) fun getCurrentTime(): UFix64 { return scriptExecutor("util/get_current_time.cdc", [])! as! UFix64 } -pub fun mintFromDrop( - minter: Test.Account, +access(all) fun mintFromDrop( + minter: Test.TestAccount, contractAddress: Address, contractName: String, numToMint: Int, @@ -226,34 +144,34 @@ pub fun mintFromDrop( txExecutor("drops/mint.cdc", [minter], args, nil, nil) } -pub fun getDropIDs( +access(all) fun getDropIDs( contractAddress: Address, contractName: String ): [UInt64] { return scriptExecutor("get_drop_ids.cdc", [contractAddress, contractName])! as! [UInt64] } -pub fun createEndlessOpenEditionDrop( - acct: Test.Account, +access(all) fun createEndlessOpenEditionDrop( + acct: Test.TestAccount, name: String, description: String, ipfsCid: String, ipfsPath: String?, price: UFix64, paymentIdentifier: String, - minterPrivatePath: PrivatePath, + minterControllerID: UInt64, nftTypeIdentifier: String ): UInt64 { txExecutor("drops/add_endless_open_edition.cdc", [acct], [ - name, description, ipfsCid, ipfsPath, price, paymentIdentifier, minterPrivatePath, nftTypeIdentifier + name, description, ipfsCid, ipfsPath, price, paymentIdentifier, minterControllerID, nftTypeIdentifier ], nil, nil) let e = Test.eventsOfType(Type()).removeLast() as! FlowtyDrops.DropAdded return e.id } -pub fun createTimebasedOpenEditionDrop( - acct: Test.Account, +access(all) fun createTimebasedOpenEditionDrop( + acct: Test.TestAccount, name: String, description: String, ipfsCid: String, @@ -262,47 +180,47 @@ pub fun createTimebasedOpenEditionDrop( paymentIdentifier: String, startUnix: UInt64?, endUnix: UInt64?, - minterPrivatePath: PrivatePath, + minterControllerID: UInt64, nftTypeIdentifier: String ): UInt64 { txExecutor("drops/add_time_based_open_edition.cdc", [acct], [ - name, description, ipfsCid, ipfsPath, price, paymentIdentifier, startUnix, endUnix, minterPrivatePath, nftTypeIdentifier + name, description, ipfsCid, ipfsPath, price, paymentIdentifier, startUnix, endUnix, minterControllerID, nftTypeIdentifier ], nil, nil) let e = Test.eventsOfType(Type()).removeLast() as! FlowtyDrops.DropAdded return e.id } -pub fun sendFlowTokens(fromAccount: Test.Account, toAccount: Test.Account, amount: UFix64) { +access(all) fun sendFlowTokens(fromAccount: Test.TestAccount, toAccount: Test.TestAccount, amount: UFix64) { txExecutor("util/send_flow_tokens.cdc", [fromAccount], [toAccount.address, amount], nil, nil) } -pub fun setupExampleToken(_ acct: Test.Account) { - txExecutor("example-token/setup.cdc", [acct], [], nil, nil) -} - -pub fun mintExampleTokens(_ acct: Test.Account, _ amount: UFix64) { - txExecutor("example-token/mint.cdc", [exampleTokenAccount], [acct.address, amount], nil, nil) +access(all) fun mintFlowTokens(_ acct: Test.TestAccount, _ amount: UFix64) { + txExecutor("flow-token/mint.cdc", [serviceAccount], [acct.address, amount], nil, nil) } -pub fun exampleTokenIdentifier(): String { - return Type<@ExampleToken.Vault>().identifier +access(all) fun flowTokenIdentifier(): String { + return Type<@FlowToken.Vault>().identifier } -pub fun openEditionNftIdentifier(): String { +access(all) fun openEditionNftIdentifier(): String { return Type<@OpenEditionNFT.NFT>().identifier } -pub fun hasDropPhaseStarted(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { +access(all) fun hasDropPhaseStarted(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { return scriptExecutor("has_phase_started.cdc", [contractAddress, contractName, dropID, phaseIndex])! as! Bool } -pub fun hasDropPhaseEnded(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { +access(all) fun hasDropPhaseEnded(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int): Bool { return scriptExecutor("has_phase_ended.cdc", [contractAddress, contractName, dropID, phaseIndex])! as! Bool } -pub fun canMintAtPhase(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, totalMinted: Int, paymentIdentifier: String): Bool { +access(all) fun canMintAtPhase(contractAddress: Address, contractName: String, dropID: UInt64, phaseIndex: Int, minter: Address, numToMint: Int, totalMinted: Int, paymentIdentifier: String): Bool { return scriptExecutor("can_mint_at_phase.cdc", [ contractAddress, contractName, dropID, phaseIndex, minter, numToMint, totalMinted, paymentIdentifier ])! as! Bool +} + +access(all) fun getMinterControllerID(acct: Test.TestAccount): UInt64? { + return scriptExecutor("util/get_minter_controller_id.cdc", [acct.address]) as! UInt64? } \ No newline at end of file diff --git a/transactions/drops/add_endless_open_edition.cdc b/transactions/drops/add_endless_open_edition.cdc index ad7da59..801e7ab 100644 --- a/transactions/drops/add_endless_open_edition.cdc +++ b/transactions/drops/add_endless_open_edition.cdc @@ -10,21 +10,27 @@ transaction( ipfsPath: String?, price: UFix64, paymentIdentifier: String, - minterPrivatePath: PrivatePath, + minterControllerID: UInt64, nftTypeIdentifier: String ) { - prepare(acct: AuthAccount) { - if acct.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { - acct.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { + acct.storage.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) - acct.unlink(FlowtyDrops.ContainerPublicPath) - acct.link<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath, target: FlowtyDrops.ContainerStoragePath) + acct.capabilities.unpublish(FlowtyDrops.ContainerPublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerStoragePath), + at: FlowtyDrops.ContainerPublicPath + ) } - let minter = acct.getCapability<&{FlowtyDrops.Minter}>(minterPrivatePath) + let controller = acct.capabilities.storage.getController(byCapabilityID: minterControllerID) + ?? panic("minter capability not found") + + let minter = controller.capability as! Capability<&{FlowtyDrops.Minter}> assert(minter.check(), message: "minter capability is not valid") - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("drops container not found") let paymentType = CompositeType(paymentIdentifier) ?? panic("invalid payment identifier") diff --git a/transactions/drops/add_free_phase.cdc b/transactions/drops/add_free_phase.cdc index 8e6502b..c66f637 100644 --- a/transactions/drops/add_free_phase.cdc +++ b/transactions/drops/add_free_phase.cdc @@ -1,14 +1,31 @@ import "FlowtyDrops" import "FlowtyPricers" +import "FlowtySwitchers" +import "FlowtyAddressVerifiers" -transaction(dropID: UInt64, start: UInt64?, end: UInt64?) { - prepare(acct: AuthAccount) { - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) +import "MetadataViews" + +transaction(dropID: UInt64, start: UInt64?, end: UInt64?, displayURL: String) { + prepare(acct: auth(BorrowValue) &Account) { + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("container not found") let drop = container.borrowDrop(id: dropID) ?? panic("drop not found") let firstPhase = drop.borrowPhase(index: 0) - let details = FlowtyDrops.PhaseDetails(switcher: firstPhase.details.switcher, display: firstPhase.details.display, pricer: FlowtyPricers.Free(), addressVerifier: firstPhase.details.addressVerifier) + let switcher = FlowtySwitchers.TimestampSwitch(start: start, end: end) + + var display: MetadataViews.Display? = nil + if let tmpDisplay = firstPhase.details.display { + display = MetadataViews.Display( + name: tmpDisplay.name, + description: tmpDisplay.description, + thumbnail: MetadataViews.HTTPFile(url: displayURL) + ) + } + + let verifier = FlowtyAddressVerifiers.AllowAll(maxPerMint: 1000) + + let details = FlowtyDrops.PhaseDetails(switcher: switcher, display: display, pricer: FlowtyPricers.Free(), addressVerifier: verifier) let phase <- FlowtyDrops.createPhase(details: details) drop.addPhase(<- phase) diff --git a/transactions/drops/add_time_based_open_edition.cdc b/transactions/drops/add_time_based_open_edition.cdc index 74de7df..63df5f6 100644 --- a/transactions/drops/add_time_based_open_edition.cdc +++ b/transactions/drops/add_time_based_open_edition.cdc @@ -12,21 +12,27 @@ transaction( paymentIdentifier: String, startUnix: UInt64?, endUnix: UInt64?, - minterPrivatePath: PrivatePath, + minterControllerID: UInt64, nftTypeIdentifier: String ) { - prepare(acct: AuthAccount) { - if acct.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { - acct.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) - - acct.unlink(FlowtyDrops.ContainerPublicPath) - acct.link<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerPublicPath, target: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(Storage, Capabilities) &Account) { + if acct.storage.borrow<&AnyResource>(from: FlowtyDrops.ContainerStoragePath) == nil { + acct.storage.save(<- FlowtyDrops.createContainer(), to: FlowtyDrops.ContainerStoragePath) + + acct.capabilities.unpublish(FlowtyDrops.ContainerPublicPath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{FlowtyDrops.ContainerPublic}>(FlowtyDrops.ContainerStoragePath), + at: FlowtyDrops.ContainerPublicPath + ) } - let minter = acct.getCapability<&{FlowtyDrops.Minter}>(minterPrivatePath) + let controller = acct.capabilities.storage.getController(byCapabilityID: minterControllerID) + ?? panic("minter capability not found") + + let minter = controller.capability as! Capability<&{FlowtyDrops.Minter}> assert(minter.check(), message: "minter capability is not valid") - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("drops container not found") let paymentType = CompositeType(paymentIdentifier) ?? panic("invalid payment identifier") diff --git a/transactions/drops/destroy_container.cdc b/transactions/drops/destroy_container.cdc index 9ff2903..17bba18 100644 --- a/transactions/drops/destroy_container.cdc +++ b/transactions/drops/destroy_container.cdc @@ -1,7 +1,7 @@ import "FlowtyDrops" transaction { - prepare(acct: AuthAccount) { - destroy acct.load<@AnyResource>(from: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(LoadValue) &Account) { + destroy acct.storage.load<@AnyResource>(from: FlowtyDrops.ContainerStoragePath) } } \ No newline at end of file diff --git a/transactions/drops/edit_address_verifier_max_per_mint.cdc b/transactions/drops/edit_address_verifier_max_per_mint.cdc index 826b789..65dff4e 100644 --- a/transactions/drops/edit_address_verifier_max_per_mint.cdc +++ b/transactions/drops/edit_address_verifier_max_per_mint.cdc @@ -2,12 +2,12 @@ import "FlowtyDrops" import "FlowtyAddressVerifiers" transaction(dropID: UInt64, phaseIndex: Int, maxPerMint: Int) { - prepare(acct: AuthAccount) { - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(BorrowValue) &Account) { + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("container not found") let drop = container.borrowDrop(id: dropID) ?? panic("drop not found") let phase = drop.borrowPhase(index: phaseIndex) - let v = phase.borrowAddressVerifierAuth() as! auth &FlowtyAddressVerifiers.AllowAll + let v = phase.borrowAddressVerifierAuth() as! auth(Mutate) &FlowtyAddressVerifiers.AllowAll v.setMaxPerMint(maxPerMint) } } \ No newline at end of file diff --git a/transactions/drops/edit_flat_price.cdc b/transactions/drops/edit_flat_price.cdc index a62ef2b..d3f85bb 100644 --- a/transactions/drops/edit_flat_price.cdc +++ b/transactions/drops/edit_flat_price.cdc @@ -2,12 +2,12 @@ import "FlowtyDrops" import "FlowtyPricers" transaction(dropID: UInt64, phaseIndex: Int, price: UFix64) { - prepare(acct: AuthAccount) { - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(BorrowValue) &Account) { + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("container not found") let drop = container.borrowDrop(id: dropID) ?? panic("drop not found") let phase = drop.borrowPhase(index: phaseIndex) - let pricer = phase.borrowPricerAuth() as! auth &FlowtyPricers.FlatPrice + let pricer = phase.borrowPricerAuth() as! auth(Mutate) &FlowtyPricers.FlatPrice pricer.setPrice(price: price) - } + } } \ No newline at end of file diff --git a/transactions/drops/edit_timebased_phase_start_and_end.cdc b/transactions/drops/edit_timebased_phase_start_and_end.cdc index 66ff1c4..879273d 100644 --- a/transactions/drops/edit_timebased_phase_start_and_end.cdc +++ b/transactions/drops/edit_timebased_phase_start_and_end.cdc @@ -2,12 +2,14 @@ import "FlowtyDrops" import "FlowtySwitchers" transaction(dropID: UInt64, phaseIndex: Int, start: UInt64?, end: UInt64?) { - prepare(acct: AuthAccount) { - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(BorrowValue) &Account) { + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("container not found") let drop = container.borrowDrop(id: dropID) ?? panic("drop not found") let phase = drop.borrowPhase(index: phaseIndex) - let s = phase.borrowSwitchAuth() as! auth &FlowtySwitchers.TimestampSwitch + + let tmp = phase.borrowSwitchAuth() + let s = tmp as! auth(Mutate) &FlowtySwitchers.TimestampSwitch s.setStart(start: start) s.setEnd(end: end) diff --git a/transactions/drops/mint.cdc b/transactions/drops/mint.cdc index 6e8bb4b..3600445 100644 --- a/transactions/drops/mint.cdc +++ b/transactions/drops/mint.cdc @@ -18,33 +18,38 @@ transaction( nftIdentifier: String, commissionAddress: Address ) { - prepare(acct: AuthAccount) { - let resolver = getAccount(contractAddress).contracts.borrow<&ViewResolver>(name: contractName) + prepare(acct: auth(Capabilities, Storage) &Account) { + let resolver = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName) ?? panic("ViewResolver contract interface not found on contract address + name") - let collectionData = resolver.resolveView(Type())! as! MetadataViews.NFTCollectionData - if acct.borrow<&AnyResource>(from: collectionData.storagePath) == nil { - acct.save(<- collectionData.createEmptyCollection(), to: collectionData.storagePath) + let collectionData = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! MetadataViews.NFTCollectionData + if acct.storage.borrow<&AnyResource>(from: collectionData.storagePath) == nil { + acct.storage.save(<- collectionData.createEmptyCollection(), to: collectionData.storagePath) - acct.link<&{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection}>(collectionData.publicPath, target: collectionData.storagePath) - acct.link<&{NonFungibleToken.CollectionPublic, MetadataViews.ResolverCollection, NonFungibleToken.Provider}>(collectionData.providerPath, target: collectionData.storagePath) + acct.capabilities.publish( + acct.capabilities.storage.issue<&{NonFungibleToken.Collection}>(collectionData.storagePath), + at: collectionData.publicPath + ) + + acct.capabilities.storage.issue(collectionData.storagePath) } - let receiverCap = acct.getCapability<&{NonFungibleToken.CollectionPublic}>(collectionData.publicPath) + let receiverCap = acct.capabilities.get<&{NonFungibleToken.CollectionPublic}>(collectionData.publicPath) + assert(receiverCap.check(), message: "invalid receiver capability") let expectedNftType = CompositeType(nftIdentifier) ?? panic("invalid nft identifier") - let vault = acct.borrow<&{FungibleToken.Provider}>(from: paymentStoragePath) + let vault = acct.storage.borrow(from: paymentStoragePath) ?? panic("could not borrow token provider") let paymentVault <- vault.withdraw(amount: totalCost) - let dropResolver = resolver.resolveView(Type())! as! FlowtyDrops.DropResolver + let dropResolver = resolver.resolveContractView(resourceType: nil, viewType: Type())! as! FlowtyDrops.DropResolver let dropContainer = dropResolver.borrowContainer() ?? panic("unable to borrow drop container") let drop = dropContainer.borrowDropPublic(id: dropID) ?? panic("drop not found") - let commissionReceiver = getAccount(commissionAddress).getCapability<&{FungibleToken.Receiver}>(paymentReceiverPath) + let commissionReceiver = getAccount(commissionAddress).capabilities.get<&{FungibleToken.Receiver}>(paymentReceiverPath) let remainder <- drop.mint( payment: <-paymentVault, amount: numToMint, @@ -56,7 +61,7 @@ transaction( ) if remainder.balance > 0.0 { - acct.borrow<&{FungibleToken.Receiver}>(from: paymentStoragePath)!.deposit(from: <-remainder) + acct.storage.borrow<&{FungibleToken.Receiver}>(from: paymentStoragePath)!.deposit(from: <-remainder) } else { destroy remainder } diff --git a/transactions/drops/remove_all_drops.cdc b/transactions/drops/remove_all_drops.cdc index 7d27b8b..c231141 100644 --- a/transactions/drops/remove_all_drops.cdc +++ b/transactions/drops/remove_all_drops.cdc @@ -1,8 +1,8 @@ import "FlowtyDrops" transaction { - prepare(acct: AuthAccount) { - if let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) { + prepare(acct: auth(BorrowValue) &Account) { + if let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) { for id in container.getIDs() { destroy container.removeDrop(id: id) } diff --git a/transactions/drops/remove_last_phase.cdc b/transactions/drops/remove_last_phase.cdc index 13d194d..8ff66c2 100644 --- a/transactions/drops/remove_last_phase.cdc +++ b/transactions/drops/remove_last_phase.cdc @@ -2,14 +2,12 @@ import "FlowtyDrops" import "FlowtyPricers" transaction(dropID: UInt64, start: UInt64?, end: UInt64?) { - prepare(acct: AuthAccount) { - let container = acct.borrow<&FlowtyDrops.Container>(from: FlowtyDrops.ContainerStoragePath) + prepare(acct: auth(BorrowValue) &Account) { + let container = acct.storage.borrow(from: FlowtyDrops.ContainerStoragePath) ?? panic("container not found") let drop = container.borrowDrop(id: dropID) ?? panic("drop not found") let firstPhase = drop.borrowPhase(index: 0) - let details = FlowtyDrops.PhaseDetails(switcher: firstPhase.details.switcher, display: firstPhase.details.display, pricer: FlowtyPricers.Free(), addressVerifier: firstPhase.details.addressVerifier) - let phases = drop.borrowAllPhases() destroy drop.removePhase(index: phases.length - 1) } diff --git a/transactions/example-token/mint.cdc b/transactions/example-token/mint.cdc deleted file mode 100644 index 4b7d1a0..0000000 --- a/transactions/example-token/mint.cdc +++ /dev/null @@ -1,28 +0,0 @@ -import "FungibleToken" -import "ExampleToken" - -transaction(recipient: Address, amount: UFix64) { - let tokenAdmin: &ExampleToken.Administrator - let tokenReceiver: &{FungibleToken.Receiver} - - prepare(acct: AuthAccount) { - - self.tokenAdmin = acct - .borrow<&ExampleToken.Administrator>(from: /storage/exampleTokenAdmin) - ?? panic("Signer is not the token admin") - - self.tokenReceiver = getAccount(recipient) - .getCapability(/public/exampleTokenReceiver) - .borrow<&{FungibleToken.Receiver}>() - ?? panic("Unable to borrow receiver reference") - } - - execute { - let minter <- self.tokenAdmin.createNewMinter(allowedAmount: amount) - let mintedVault <- minter.mintTokens(amount: amount) - - self.tokenReceiver.deposit(from: <-mintedVault) - - destroy minter - } -} \ No newline at end of file diff --git a/transactions/example-token/setup.cdc b/transactions/example-token/setup.cdc deleted file mode 100644 index 337e035..0000000 --- a/transactions/example-token/setup.cdc +++ /dev/null @@ -1,27 +0,0 @@ -import "FungibleToken" -import "ExampleToken" - -transaction { - prepare(acct: AuthAccount) { - if acct.borrow<&ExampleToken.Vault>(from: /storage/exampleTokenVault) == nil { - acct.save(<-ExampleToken.createEmptyVault(), to: /storage/exampleTokenVault) - } - - acct.unlink(/public/exampleTokenReceiver) - acct.unlink(/public/exampleTokenBalance) - acct.unlink(/private/exampleTokenBalance) - - acct.link<&{FungibleToken.Receiver}>( - /public/exampleTokenReceiver, - target: /storage/exampleTokenVault - ) - - acct.link<&{FungibleToken.Balance}>( - /public/exampleTokenBalance, - target: /storage/exampleTokenVault - ) - - acct.link<&{FungibleToken.Provider, FungibleToken.Balance, FungibleToken.Receiver}>(/private/exampleTokenBalance, target: /storage/exampleTokenVault) - } -} - \ No newline at end of file diff --git a/transactions/flow-token/mint.cdc b/transactions/flow-token/mint.cdc new file mode 100644 index 0000000..462b514 --- /dev/null +++ b/transactions/flow-token/mint.cdc @@ -0,0 +1,15 @@ +import "FlowToken" +import "FungibleToken" + +transaction(receiver: Address, amount: UFix64) { + prepare(acct: auth(BorrowValue) &Account) { + let admin = acct.storage.borrow<&FlowToken.Administrator>(from: /storage/flowTokenAdmin) + ?? panic("flow token admin not found") + + let minter <- admin.createNewMinter(allowedAmount: amount) + let tokens <- minter.mintTokens(amount: amount) + + getAccount(receiver).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver).borrow()!.deposit(from: <-tokens) + destroy minter + } +} \ No newline at end of file diff --git a/transactions/util/heartbeat.cdc b/transactions/util/heartbeat.cdc index eb9040a..95edcc3 100644 --- a/transactions/util/heartbeat.cdc +++ b/transactions/util/heartbeat.cdc @@ -1,3 +1,3 @@ transaction { - prepare(acct: AuthAccount) {} + prepare(acct: &Account) {} } \ No newline at end of file diff --git a/transactions/util/send_flow_tokens.cdc b/transactions/util/send_flow_tokens.cdc index d3ff408..c13e5f6 100644 --- a/transactions/util/send_flow_tokens.cdc +++ b/transactions/util/send_flow_tokens.cdc @@ -1,11 +1,11 @@ import "FungibleToken" transaction(to: Address, amount: UFix64) { - prepare(acct: AuthAccount) { - let receiver = getAccount(to).getCapability<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) + prepare(acct: auth(BorrowValue) &Account) { + let receiver = getAccount(to).capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) .borrow() ?? panic("receiver not found") - let provider = acct.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)! + let provider = acct.storage.borrow(from: /storage/flowTokenVault)! let tokens <- provider.withdraw(amount: amount) receiver.deposit(from: <-tokens) }