Skip to content

Commit

Permalink
Use FungibleTokenRouter instead of Switchboard (#31)
Browse files Browse the repository at this point in the history
* use fungible token router instead of switchboard

* generate new testnet addresses
  • Loading branch information
austinkline authored Aug 28, 2024
1 parent 44e5d49 commit 4251ef0
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 62 deletions.
38 changes: 17 additions & 21 deletions contracts/ContractManager.cdc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "FlowToken"
import "FungibleToken"
import "FungibleTokenSwitchboard"
import "FungibleTokenRouter"

access(all) contract ContractManager {
access(all) let StoragePath: StoragePath
Expand All @@ -10,7 +10,7 @@ access(all) contract ContractManager {

access(all) resource Manager {
access(self) let acct: Capability<auth(Storage, Contracts, Keys, Inbox, Capabilities) &Account>
access(self) let switchboardCap: Capability<auth(FungibleTokenSwitchboard.Owner) &FungibleTokenSwitchboard.Switchboard>
access(self) let routerCap: Capability<auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router>

access(all) let data: {String: AnyStruct}
access(all) let resources: @{String: AnyResource}
Expand All @@ -19,17 +19,13 @@ access(all) contract ContractManager {
return self.acct.borrow()!
}

access(Manage) fun addFungibleTokenReceiver(_ cap: Capability<&{FungibleToken.Receiver}>) {
pre {
cap.check(): "capability is not valid"
}

let switchboard = self.switchboardCap.borrow() ?? panic("fungible token switchboard is not valid")
switchboard.addNewVault(capability: cap)
access(Manage) fun addOverride(type: Type, addr: Address) {
let router = self.routerCap.borrow() ?? panic("fungible token router is not valid")
router.addOverride(type: type, addr: addr)
}

access(Manage) fun getSwitchboard(): auth(FungibleTokenSwitchboard.Owner) &FungibleTokenSwitchboard.Switchboard {
return self.switchboardCap.borrow()!
access(Manage) fun getSwitchboard(): auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router {
return self.routerCap.borrow()!
}

access(all) fun addFlowTokensToAccount(_ tokens: @FlowToken.Vault) {
Expand All @@ -40,7 +36,7 @@ access(all) contract ContractManager {
return getAccount(self.acct.address)
}

init(tokens: @FlowToken.Vault) {
init(tokens: @FlowToken.Vault, defaultRouterAddress: Address) {
pre {
tokens.balance >= 0.001: "minimum balance of 0.001 required for initialization"
}
Expand All @@ -51,27 +47,27 @@ access(all) contract ContractManager {

acct.storage.borrow<&{FungibleToken.Receiver}>(from: /storage/flowTokenVault)!.deposit(from: <-tokens)

let switchboard <- FungibleTokenSwitchboard.createSwitchboard()
acct.storage.save(<-switchboard, to: FungibleTokenSwitchboard.StoragePath)
let router <- FungibleTokenRouter.createRouter(defaultAddress: defaultRouterAddress)
acct.storage.save(<-router, to: FungibleTokenRouter.StoragePath)

let receiver = acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenSwitchboard.StoragePath)
let receiver = acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenRouter.StoragePath)
assert(receiver.check(), message: "invalid switchboard receiver capability")
acct.capabilities.publish(receiver, at: FungibleTokenSwitchboard.ReceiverPublicPath)
acct.capabilities.publish(receiver, at: FungibleTokenRouter.PublicPath)

acct.capabilities.publish(
acct.capabilities.storage.issue<&FungibleTokenSwitchboard.Switchboard>(FungibleTokenSwitchboard.StoragePath),
at: FungibleTokenSwitchboard.PublicPath
acct.capabilities.storage.issue<&FungibleTokenRouter.Router>(FungibleTokenRouter.StoragePath),
at: FungibleTokenRouter.PublicPath
)

self.switchboardCap = acct.capabilities.storage.issue<auth(FungibleTokenSwitchboard.Owner) &FungibleTokenSwitchboard.Switchboard>(FungibleTokenSwitchboard.StoragePath)
self.routerCap = acct.capabilities.storage.issue<auth(FungibleTokenRouter.Owner) &FungibleTokenRouter.Router>(FungibleTokenRouter.StoragePath)

self.data = {}
self.resources <- {}
}
}

access(all) fun createManager(tokens: @FlowToken.Vault): @Manager {
return <- create Manager(tokens: <- tokens)
access(all) fun createManager(tokens: @FlowToken.Vault, defaultRouterAddress: Address): @Manager {
return <- create Manager(tokens: <- tokens, defaultRouterAddress: defaultRouterAddress)
}

init() {
Expand Down
7 changes: 4 additions & 3 deletions contracts/FlowtyDrops.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import "NonFungibleToken"
import "FungibleToken"
import "MetadataViews"
import "AddressUtils"
import "FungibleTokenSwitchboard"
import "FungibleTokenMetadataViews"
import "FungibleTokenRouter"

access(all) contract FlowtyDrops {
access(all) let ContainerStoragePath: StoragePath
Expand Down Expand Up @@ -336,8 +337,8 @@ access(all) contract FlowtyDrops {
access(all) resource interface Minter {
access(contract) fun mint(payment: @{FungibleToken.Vault}, amount: Int, phase: &FlowtyDrops.Phase, data: {String: AnyStruct}): @[{NonFungibleToken.NFT}] {
let resourceAddress = AddressUtils.parseAddress(self.getType())!
let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(FungibleTokenSwitchboard.ReceiverPublicPath).borrow()
?? panic("invalid token receiver")
let receiver = getAccount(resourceAddress).capabilities.get<&{FungibleToken.Receiver}>(FungibleTokenRouter.PublicPath).borrow()
?? panic("missing receiver at fungible token router path")
receiver.deposit(from: <-payment)

let nfts: @[{NonFungibleToken.NFT}] <- []
Expand Down
17 changes: 9 additions & 8 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
}
},
"flowty-drops-testnet": {
"address": "0xd84b33493cc8c87e",
"address": "0x772a10c786851a1b",
"key": {
"type": "google-kms",
"index": 0,
Expand All @@ -42,7 +42,7 @@
}
},
"droptypes-testnet": {
"address": "0xce1f567aa5ccc21f",
"address": "0x934da91a977f1ac4",
"key": {
"type": "google-kms",
"index": 0,
Expand Down Expand Up @@ -254,12 +254,13 @@
"mainnet": "0xa340dc0a4ec828ab"
}
},
"FungibleTokenSwitchboard": {
"source": "./node_modules/@flowtyio/flow-contracts/contracts/FungibleTokenSwitchboard.cdc",
"FungibleTokenRouter": {
"source": "./node_modules/@flowtyio/flow-contracts/contracts/fungible-token-router/FungibleTokenRouter.cdc",
"aliases": {
"emulator": "0xee82856bf20e2aa6",
"testnet": "0x9a0766d93b6608b7",
"mainnet": "0xf233dcee88fe0abe"
"testing": "0x0000000000000007",
"emulator": "0xf8d6e0586b0a20c7",
"testnet": "0x83231f90a288bc35",
"mainnet": "0x707c0b39a8d689cb"
}
}
},
Expand Down Expand Up @@ -290,7 +291,7 @@
"ArrayUtils",
"StringUtils",
"AddressUtils",
"FungibleTokenSwitchboard"
"FungibleTokenRouter"
],
"emulator-ft": [
"FungibleToken",
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"@flowtyio/flow-contracts": "^0.1.0-beta.27"
"@flowtyio/flow-contracts": "0.1.0-beta.31"
}
}
1 change: 1 addition & 0 deletions tests/test_helpers.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ access(all) fun deployAll() {
deploy("ArrayUtils", "../node_modules/@flowtyio/flow-contracts/contracts/flow-utils/ArrayUtils.cdc", [])
deploy("StringUtils", "../node_modules/@flowtyio/flow-contracts/contracts/flow-utils/StringUtils.cdc", [])
deploy("AddressUtils", "../node_modules/@flowtyio/flow-contracts/contracts/flow-utils/AddressUtils.cdc", [])
deploy("FungibleTokenRouter", "../node_modules/@flowtyio/flow-contracts/contracts/fungible-token-router/FungibleTokenRouter.cdc", [])

deploy("FlowtyDrops", "../contracts/FlowtyDrops.cdc", [])
deploy("NFTMetadata", "../contracts/nft/NFTMetadata.cdc", [])
Expand Down
19 changes: 6 additions & 13 deletions transactions/drops/add_endless_open_edition.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "FlowtyDrops"
import "DropFactory"

import "MetadataViews"
import "FungibleTokenSwitchboard"
import "FungibleTokenRouter"
import "FungibleToken"

transaction(
Expand All @@ -26,20 +26,13 @@ transaction(
)
}

if acct.storage.borrow<&AnyResource>(from: FungibleTokenSwitchboard.StoragePath) == nil {
let switchboard <- FungibleTokenSwitchboard.createSwitchboard()
switchboard.addNewVault(capability: acct.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver))

acct.storage.save(<-switchboard, to: FungibleTokenSwitchboard.StoragePath)

acct.capabilities.publish(
acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenSwitchboard.StoragePath),
at: FungibleTokenSwitchboard.ReceiverPublicPath
)
if acct.storage.borrow<&AnyResource>(from: FungibleTokenRouter.StoragePath) == nil {
let switchboard <- FungibleTokenRouter.createRouter(defaultAddress: acct.address)
acct.storage.save(<-switchboard, to: FungibleTokenRouter.StoragePath)

acct.capabilities.publish(
acct.capabilities.storage.issue<&FungibleTokenSwitchboard.Switchboard>(FungibleTokenSwitchboard.StoragePath),
at: FungibleTokenSwitchboard.PublicPath
acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenRouter.StoragePath),
at: FungibleTokenRouter.PublicPath
)
}

Expand Down
18 changes: 6 additions & 12 deletions transactions/drops/add_time_based_open_edition.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "FlowtyDrops"
import "DropFactory"

import "MetadataViews"
import "FungibleTokenSwitchboard"
import "FungibleTokenRouter"
import "FungibleToken"

transaction(
Expand All @@ -28,20 +28,14 @@ transaction(
)
}

if acct.storage.borrow<&AnyResource>(from: FungibleTokenSwitchboard.StoragePath) == nil {
let switchboard <- FungibleTokenSwitchboard.createSwitchboard()
switchboard.addNewVault(capability: acct.capabilities.get<&{FungibleToken.Receiver}>(/public/flowTokenReceiver))
if acct.storage.borrow<&AnyResource>(from: FungibleTokenRouter.StoragePath) == nil {
let switchboard <- FungibleTokenRouter.createRouter(defaultAddress: acct.address)

acct.storage.save(<-switchboard, to: FungibleTokenSwitchboard.StoragePath)
acct.storage.save(<-switchboard, to: FungibleTokenRouter.StoragePath)

acct.capabilities.publish(
acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(FungibleTokenSwitchboard.StoragePath),
at: FungibleTokenSwitchboard.ReceiverPublicPath
)

acct.capabilities.publish(
acct.capabilities.storage.issue<&FungibleTokenSwitchboard.Switchboard>(FungibleTokenSwitchboard.StoragePath),
at: FungibleTokenSwitchboard.PublicPath
acct.capabilities.storage.issue<&FungibleTokenRouter.Router>(FungibleTokenRouter.StoragePath),
at: FungibleTokenRouter.PublicPath
)
}

Expand Down
110 changes: 110 additions & 0 deletions transactions/factory/create_open_edition_drop.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import "ContractFactoryTemplate"
import "ContractFactory"
import "OpenEditionTemplate"
import "MetadataViews"
import "OpenEditionInitializer"
import "ContractManager"
import "FungibleToken"
import "FlowToken"
import "FlowtyDrops"
import "NFTMetadata"
import "FlowtyActiveCheckers"
import "FlowtyPricers"
import "FlowtyAddressVerifiers"

transaction(contractName: String, managerInitialTokenBalance: UFix64, start: UInt64?, end: UInt64?, price: UFix64, paymentTokenType: String, phaseArgs: {String: String}, metadataArgs: {String: String}, collectionInfoArgs: {String: String}, dropDetailArgs: {String: String}) {
prepare(acct: auth(Storage, Capabilities) &Account) {
if acct.storage.borrow<&AnyResource>(from: ContractManager.StoragePath) == nil {
let v = acct.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault)!
let tokens <- v.withdraw(amount: managerInitialTokenBalance) as! @FlowToken.Vault

acct.storage.save(<- ContractManager.createManager(tokens: <-tokens), to: ContractManager.StoragePath)

acct.capabilities.publish(
acct.capabilities.storage.issue<&ContractManager.Manager>(ContractManager.StoragePath),
at: ContractManager.PublicPath
)
}

let manager: auth(ContractManager.Manage) &ContractManager.Manager = acct.storage.borrow<auth(ContractManager.Manage) &ContractManager.Manager>(from: ContractManager.StoragePath)
?? panic("manager was not borrowed successfully")

let data: {String: AnyStruct} = {}
let nftMetadata: NFTMetadata.Metadata = NFTMetadata.Metadata(
name: metadataArgs["name"]!,
description: metadataArgs["description"]!,
thumbnail: MetadataViews.IPFSFile(cid: metadataArgs["cid"]!, path: metadataArgs["path"]),
traits: nil,
editions: nil,
externalURL: metadataArgs["externalURL"] != nil ? MetadataViews.ExternalURL(metadataArgs["externalURL"]!) : nil,
royalties: nil,
data: data
)

let socials: {String: MetadataViews.ExternalURL} = {}
let keys = ["twitter", "x", "discord", "instagram"]
for k in keys {
if let v = collectionInfoArgs[k] {
socials[k] = MetadataViews.ExternalURL(v)
}
}

let collectionDisplay = MetadataViews.NFTCollectionDisplay(
name: collectionInfoArgs["name"]!,
description: collectionInfoArgs["description"]!,
externalURL: MetadataViews.ExternalURL(collectionInfoArgs["externalURL"]!),
squareImage: MetadataViews.Media(
file: MetadataViews.IPFSFile(cid: collectionInfoArgs["squareImageCid"]!, path: collectionInfoArgs["squareImagePath"]),
mediaType: collectionInfoArgs["squareImageMediaType"]!
),
bannerImage: MetadataViews.Media(
file: MetadataViews.IPFSFile(cid: collectionInfoArgs["bannerImageCid"]!, path: collectionInfoArgs["bannerImagePath"]),
mediaType: collectionInfoArgs["bannerImageMediaType"]!
),
socials: socials
)

let addrStr = manager.getAccount().address.toString()
let nftType = "A.".concat(addrStr.slice(from: 2, upTo: addrStr.length)).concat(".").concat(contractName).concat(".NFT")

let dropDetails = FlowtyDrops.DropDetails(
display: MetadataViews.Display(
name: dropDetailArgs["name"]!,
description: dropDetailArgs["description"]!,
thumbnail: MetadataViews.IPFSFile(cid: dropDetailArgs["thumbnailCid"]!, path: dropDetailArgs["thumbnailPath"])
),
medias: nil,
commissionRate: 0.05,
nftType: nftType
)

let phaseDetails = FlowtyDrops.PhaseDetails(
activeChecker: FlowtyActiveCheckers.TimestampChecker(start: start, end: end),
display: phaseArgs["displayName"] != nil ? MetadataViews.Display(
name: phaseArgs["displayName"]!,
description: phaseArgs["displayDescription"]!,
thumbnail: MetadataViews.IPFSFile(cid: phaseArgs["displayCid"]!, path: phaseArgs["displayPath"])
) : nil,
pricer: FlowtyPricers.FlatPrice(price: price, paymentTokenType: CompositeType(paymentTokenType)!),
addressVerifier: FlowtyAddressVerifiers.AllowAll(maxPerMint: 10)
)

// The Open edition initializer requires at least two keys:
// - data: NFTMetadata.Metadata
// - collectionInfo: NFTMetadata.CollectionInfo
//
// You can also specify some optional paramters:
// - dropDetails: FlowtyDrops.DropDetails
// - phaseDetails: [FlowtyDrops.PhaseDetails]
// - minterController: This is supplied in the initialization of the contract itself
let arr: [FlowtyDrops.PhaseDetails] = [phaseDetails]
let params: {String: AnyStruct} = {
"data": nftMetadata,
"collectionInfo": NFTMetadata.CollectionInfo(collectionDisplay: collectionDisplay),
"dropDetails": dropDetails,
"phaseDetails": arr
}

ContractFactory.createContract(templateType: Type<OpenEditionTemplate>(), acct: manager.borrowContractAccount(), name: contractName, params: params, initializeIdentifier: Type<OpenEditionInitializer>().identifier)
}
}

0 comments on commit 4251ef0

Please sign in to comment.