Skip to content

Commit

Permalink
Merge pull request #66 from dapperlabs/josh/marketupdates
Browse files Browse the repository at this point in the history
market updates and forwarding tests
  • Loading branch information
joshuahannan authored Jun 19, 2020
2 parents e754e4a + eea28fc commit 509a18d
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 58 deletions.
3 changes: 2 additions & 1 deletion contracts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module github.com/dapperlabs/nba-smart-contracts/contracts
go 1.14

require (
github.com/onflow/flow-ft/contracts v0.1.2 // indirect
github.com/onflow/flow-ft v0.1.2 // indirect
github.com/onflow/flow-ft/contracts v0.1.3 // indirect
github.com/onflow/flow-go-sdk v0.4.0
github.com/stretchr/testify v1.5.1
)
4 changes: 4 additions & 0 deletions contracts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,14 @@ github.com/onflow/cadence v0.4.0-beta1 h1:0f4CMnddT++5OYY53OPFuv5JTb85qDg/nygLV4
github.com/onflow/cadence v0.4.0-beta1/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I=
github.com/onflow/cadence v0.4.0 h1:oAKY/HclZZhc5wJgJwdPjWXJuC5IjuuHHVAAq3S7AHI=
github.com/onflow/cadence v0.4.0/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I=
github.com/onflow/flow-ft v0.1.2 h1:DlnffFsNNSuK6bTwmQwIeutFKds0XIf8JYQ1TDd6bc8=
github.com/onflow/flow-ft v0.1.2/go.mod h1:Sh/PKMT0mzGM5Zqb5tL/4wolfQis8KjXNiskkOSWdpU=
github.com/onflow/flow-ft/contracts v0.1.1 h1:lB68vWPHPk1J21SrdTIg9ZbMQ1bQmfdZAJqBANUwG6U=
github.com/onflow/flow-ft/contracts v0.1.1/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-ft/contracts v0.1.2 h1:gyETu/ZW8+tisoLG+/A0B8e10T122Aj4xBGxLubqI54=
github.com/onflow/flow-ft/contracts v0.1.2/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-ft/contracts v0.1.3 h1:KLszm8wQE1is92NYro76nGaQKfHw20G9H404cLn4uP4=
github.com/onflow/flow-ft/contracts v0.1.3/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-go-sdk v0.4.0 h1:ZNEE8HQ6xTyr4+RmlxZ2+U/BjKtQDsAB54I+D8AJpZA=
github.com/onflow/flow-go-sdk v0.4.0/go.mod h1:MHn8oQCkBNcl2rXdYSm9VYYK4ogwEpyrdM/XK/czdlM=
github.com/onflow/flow/protobuf/go/flow v0.1.5-0.20200601215056-34a11def1d6b/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
Expand Down
6 changes: 3 additions & 3 deletions contracts/internal/assets/assets.go

Large diffs are not rendered by default.

97 changes: 60 additions & 37 deletions src/contracts/MarketTopShot.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ pub contract Market {
// The fungible token vault of the seller
// so that when someone buys a token, the tokens are deposited
// to this Vault
access(self) let ownerCapability: Capability
access(self) var ownerCapability: Capability

// The capability that is used for depositing
// the beneficiary's cut of every sale
access(self) let beneficiaryCapability: Capability
access(self) var beneficiaryCapability: Capability

// the percentage that is taken from every purchase for the beneficiary
// This is a literal percentage
Expand All @@ -114,7 +114,7 @@ pub contract Market {
// for dapper utility coin
ownerCapability.borrow<&{FungibleToken.Receiver}>() != nil:
"Owner's Receiver Capability is invalid!"
beneficiaryCapability.borrow<&DapperUtilityCoin.Vault{FungibleToken.Receiver}>() != nil:
beneficiaryCapability.borrow<&{FungibleToken.Receiver}>() != nil:
"Beneficiary's Receiver Capability is invalid!"
}

Expand All @@ -125,27 +125,13 @@ pub contract Market {
self.cutPercentage = cutPercentage
}

// Withdraw removes a moment that was listed for sale
pub fun withdraw(tokenID: UInt64): @TopShot.NFT {

// remove and return the token
// will revert if the token doesn't exist
let token <- self.forSale.withdraw(withdrawID: tokenID) as! @TopShot.NFT

// Remove the price from the prices dictionary
self.prices.remove(key: tokenID)

// Emit the event for withdrawing a moment from the Sale
emit MomentWithdrawn(id: token.id, owner: self.owner?.address)

// Return the withdrawn token
return <-token
}

// listForSale lists an NFT for sale in this sale collection
// at the specified price
pub fun listForSale(token: @TopShot.NFT, price: UFix64) {

// get the ID of the token
let id = token.id

// Set the token's price
self.prices[token.id] = price

Expand All @@ -155,22 +141,24 @@ pub contract Market {
emit MomentListed(id: id, price: price, seller: self.owner?.address)
}

// changePrice changes the price of a token that is currently for sale
pub fun changePrice(tokenID: UInt64, newPrice: UFix64) {
pre {
self.prices[tokenID] != nil: "Cannot change the price for a token that is not for sale"
}
// set the new price
self.prices[tokenID] = newPrice
// Withdraw removes a moment that was listed for sale
pub fun withdraw(tokenID: UInt64): @TopShot.NFT {

emit MomentPriceChanged(id: tokenID, newPrice: newPrice, seller: self.owner?.address)
}
// remove and return the token
// will revert if the token doesn't exist
let token <- self.forSale.withdraw(withdrawID: tokenID) as! @TopShot.NFT

// changePercentage changes the cut percentage of the tokens that are for sale
pub fun changePercentage(newPercent: UFix64) {
self.cutPercentage = newPercent
// Remove the price from the prices dictionary
self.prices.remove(key: tokenID)

emit CutPercentageChanged(newPercent: newPercent, seller: self.owner?.address)
// set prices to nil for the withdrawn ID
self.prices[tokenID] = nil

// Emit the event for withdrawing a moment from the Sale
emit MomentWithdrawn(id: token.id, owner: self.owner?.address)

// Return the withdrawn token
return <-token
}

// purchase lets a user send tokens to purchase an NFT that is for sale
Expand Down Expand Up @@ -200,16 +188,51 @@ pub contract Market {
self.ownerCapability.borrow<&{FungibleToken.Receiver}>()!
.deposit(from: <-buyTokens)

emit TokenPurchased(id: tokenID, price: price, seller: self.owner?.address)
emit MomentPurchased(id: tokenID, price: price, seller: self.owner?.address)

// return the purchased token
return <-self.withdraw(tokenID: tokenID)
}

// idPrice returns the price of a specific token in the sale
// changePrice changes the price of a token that is currently for sale
pub fun changePrice(tokenID: UInt64, newPrice: UFix64) {
pre {
self.prices[tokenID] != nil: "Cannot change the price for a token that is not for sale"
}
// set the new price
self.prices[tokenID] = newPrice

emit MomentPriceChanged(id: tokenID, newPrice: newPrice, seller: self.owner?.address)
}

// changePercentage changes the cut percentage of the tokens that are for sale
pub fun changePercentage(_ newPercent: UFix64) {
self.cutPercentage = newPercent

emit CutPercentageChanged(newPercent: newPercent, seller: self.owner?.address)
}

// changeOwnerReceiver updates the capability for the sellers fungible token Vault
pub fun changeOwnerReceiver(_ newOwnerCapability: Capability) {
pre {
newOwnerCapability.borrow<&{FungibleToken.Receiver}>() != nil:
"Owner's Receiver Capability is invalid!"
}
self.ownerCapability = newOwnerCapability
}

// changeBeneficiaryReceiver updates the capability for the beneficiary of the cut of the sale
pub fun changeBeneficiaryReceiver(_ newBeneficiaryCapability: Capability) {
pre {
newBeneficiaryCapability.borrow<&DapperUtilityCoin.Vault{FungibleToken.Receiver}>() != nil:
"Beneficiary's Receiver Capability is invalid!"
}
self.beneficiaryCapability = newBeneficiaryCapability
}

// getPrice returns the price of a specific token in the sale
pub fun getPrice(tokenID: UInt64): UFix64? {
let price = self.prices[tokenID]
return price
return self.prices[tokenID]
}

// getIDs returns an array of token IDs that are for sale
Expand Down
77 changes: 72 additions & 5 deletions templates/market_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@ func GenerateCreateAndStartSaleScript(topshotAddr, marketAddr, beneficiaryAddr f
// borrow a reference to the sale
let topshotSaleCollection = acct.borrow<&Market.SaleCollection>(from: /storage/topshotSaleCollection)
?? panic("Could not borrow from sale in storage")
// set the new cut percentage
topshotSaleCollection.changePercentage(%[4]f)
// the the moment for sale
topshotSaleCollection.listForSale(token: <-token, price: %[6]d.0)
topshotSaleCollection.listForSale(token: <-token, price: UFix64(%[6]d))
}
}`
Expand Down Expand Up @@ -149,12 +152,32 @@ func GenerateChangePercentageScript(topshotAddr, marketAddr flow.Address, percen
let topshotSaleCollection = acct.borrow<&Market.SaleCollection>(from: /storage/topshotSaleCollection)
?? panic("Could not borrow from sale in storage")
topshotSaleCollection.changePercentage(newPercent: %[3]f)
topshotSaleCollection.changePercentage(%[3]f)
}
}`
return []byte(fmt.Sprintf(template, topshotAddr, marketAddr, percentage))
}

// GenerateChangeOwnerReceiverScript creates a cadence transaction
// that changes the sellers receiver capability
func GenerateChangeOwnerReceiverScript(topshotAddr, marketAddr flow.Address, receiverName string) []byte {
template := `
import FungibleToken from 0xee82856bf20e2aa6
import TopShot from 0x%[1]s
import Market from 0x%[2]s
transaction {
prepare(acct: AuthAccount) {
let topshotSaleCollection = acct.borrow<&Market.SaleCollection>(from: /storage/topshotSaleCollection)
?? panic("Could not borrow from sale in storage")
topshotSaleCollection.changeOwnerReceiver(acct.getCapability(/public/%[3]s)!)
}
}`
return []byte(fmt.Sprintf(template, topshotAddr, marketAddr, receiverName))
}

// GenerateBuySaleScript creates a cadence transaction that makes a purchase of
// an existing sale
func GenerateBuySaleScript(tokenAddr, topshotAddr, marketAddr, sellerAddr flow.Address, tokenName, tokenStorageName string, id, amount int) []byte {
Expand Down Expand Up @@ -188,9 +211,53 @@ func GenerateBuySaleScript(tokenAddr, topshotAddr, marketAddr, sellerAddr flow.A
return []byte(fmt.Sprintf(template, tokenName, tokenAddr, marketAddr, sellerAddr, tokenStorageName, amount, id, topshotAddr))
}

// GenerateMintTokensAndBuyScript creates a script that uses the admin resource
// from the admin accountto mint new tokens and use them to purchase a topshot
// moment from a market collection
func GenerateMintTokensAndBuyScript(tokenAddr, topshotAddr, marketAddr, sellerAddr, receiverAddr flow.Address, tokenName, storageName string, tokenID, amount int) []byte {
template := `
import FungibleToken from 0xee82856bf20e2aa6
import %[1]s from 0x%[2]s
import TopShot from 0x%[3]s
import Market from 0x%[4]s
transaction {
prepare(signer: AuthAccount) {
let tokenAdmin = signer
.borrow<&%[1]s.Administrator>(from: /storage/%[5]sAdmin)
?? panic("Signer is not the token admin")
let minter <- tokenAdmin.createNewMinter(allowedAmount: UFix64(%[6]d))
let mintedVault <- minter.mintTokens(amount: UFix64(%[6]d)) as! @%[1]s.Vault
destroy minter
let seller = getAccount(0x%[7]s)
let topshotSaleCollection = seller.getCapability(/public/topshotSaleCollection)!
.borrow<&{Market.SalePublic}>()
?? panic("Could not borrow public sale reference")
let boughtToken <- topshotSaleCollection.purchase(tokenID: %[8]d, buyTokens: <-mintedVault)
// get the recipient's public account object and borrow a reference to their moment receiver
let recipient = getAccount(0x%[9]s)
.getCapability(/public/MomentCollection)!.borrow<&{TopShot.MomentCollectionPublic}>()
?? panic("Could not borrow a reference to the moment collection")
// deposit the NFT in the receivers collection
recipient.deposit(token: <-boughtToken)
}
}
`

return []byte(fmt.Sprintf(template, tokenName, tokenAddr, topshotAddr, marketAddr, storageName, amount, sellerAddr, tokenID, receiverAddr))
}

// GenerateInspectSaleScript creates a script that retrieves a sale collection
// from storage and checks that the price is correct
func GenerateInspectSaleScript(saleCodeAddr, userAddr flow.Address, nftID int, price int) []byte {
func GenerateInspectSaleScript(saleCodeAddr, userAddr flow.Address, nftID int, expectedPrice int) []byte {
template := `
import Market from 0x%s
Expand All @@ -199,13 +266,13 @@ func GenerateInspectSaleScript(saleCodeAddr, userAddr flow.Address, nftID int, p
let collectionRef = acct.getCapability(/public/topshotSaleCollection)!.borrow<&{Market.SalePublic}>()
?? panic("Could not borrow capability from public collection")
if collectionRef.getPrice(tokenID: UInt64(%d)) != UFix64(%d) {
if collectionRef.getPrice(tokenID: UInt64(%d))! != UFix64(%d) {
panic("Price for token ID is not correct")
}
}
`

return []byte(fmt.Sprintf(template, saleCodeAddr, userAddr, nftID, price))
return []byte(fmt.Sprintf(template, saleCodeAddr, userAddr, nftID, expectedPrice))
}

// GenerateInspectSalePercentageScript creates a script that retrieves a sale collection
Expand Down
5 changes: 2 additions & 3 deletions test/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ require (
github.com/dapperlabs/nba-smart-contracts/contracts v0.1.7
github.com/dapperlabs/nba-smart-contracts/templates v0.1.6
github.com/onflow/cadence v0.4.0
github.com/onflow/flow-ft v0.1.0 // indirect
github.com/onflow/flow-ft/contracts v0.1.2
github.com/onflow/flow-ft/test v0.0.0-20200605203250-755c0ddcc598
github.com/onflow/flow-ft/contracts v0.1.3
github.com/onflow/flow-ft/test v0.0.0-20200619173914-64c953134397
github.com/onflow/flow-go-sdk v0.4.0
github.com/stretchr/testify v1.5.1
)
Expand Down
11 changes: 11 additions & 0 deletions test/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -471,24 +471,35 @@ github.com/onflow/cadence v0.4.0-beta1 h1:0f4CMnddT++5OYY53OPFuv5JTb85qDg/nygLV4
github.com/onflow/cadence v0.4.0-beta1/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I=
github.com/onflow/cadence v0.4.0 h1:oAKY/HclZZhc5wJgJwdPjWXJuC5IjuuHHVAAq3S7AHI=
github.com/onflow/cadence v0.4.0/go.mod h1:gaPtSctdMzT5NAoJgzsRuwUkdgRswVHsRXFNNmCTn3I=
github.com/onflow/cadence v0.4.1-0.20200604185918-21edaa9bfcdd h1:MYPhW5wojW1wlIncBN6Atsi+aC9FPUcCJcURADcmzsk=
github.com/onflow/cadence v0.4.1-0.20200604185918-21edaa9bfcdd/go.mod h1:dgj1JPlSDeY6ZSqD/yCW06reFSt+19d/IFgQ1eE8Bfg=
github.com/onflow/flow v0.1.4-0.20200601215056-34a11def1d6b/go.mod h1:lzyAYmbu1HfkZ9cfnL5/sjrrsnJiUU8fRL26CqLP7+c=
github.com/onflow/flow-ft v0.1.0 h1:VIBn0SPjlMbPxVXqzeKG+Gw0GlZRidmCWzkvRh8at5A=
github.com/onflow/flow-ft v0.1.0/go.mod h1:Sh/PKMT0mzGM5Zqb5tL/4wolfQis8KjXNiskkOSWdpU=
github.com/onflow/flow-ft v0.1.1 h1:sUiUccXDpyEOECGvfNdYIQ5+B1FIbvGj4NNU5IsrxCs=
github.com/onflow/flow-ft v0.1.2 h1:DlnffFsNNSuK6bTwmQwIeutFKds0XIf8JYQ1TDd6bc8=
github.com/onflow/flow-ft v0.1.2/go.mod h1:Sh/PKMT0mzGM5Zqb5tL/4wolfQis8KjXNiskkOSWdpU=
github.com/onflow/flow-ft/contracts v0.0.0-20200525235630-0e8024a483ce h1:I8SaLOFlQN7Jr+aU1mZB0mnsq2qlaaJPeji4PmkTWfA=
github.com/onflow/flow-ft/contracts v0.0.0-20200525235630-0e8024a483ce/go.mod h1:KwzrK5thiLObKHTHaHf9lrvOS3HLr9jntNEZUQ4yJgw=
github.com/onflow/flow-ft/contracts v0.1.1 h1:lB68vWPHPk1J21SrdTIg9ZbMQ1bQmfdZAJqBANUwG6U=
github.com/onflow/flow-ft/contracts v0.1.1/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-ft/contracts v0.1.2 h1:gyETu/ZW8+tisoLG+/A0B8e10T122Aj4xBGxLubqI54=
github.com/onflow/flow-ft/contracts v0.1.2/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-ft/contracts v0.1.3 h1:KLszm8wQE1is92NYro76nGaQKfHw20G9H404cLn4uP4=
github.com/onflow/flow-ft/contracts v0.1.3/go.mod h1:IKe3yEurEKpg/J15q5WBlHkuMmt1iRECSHgnIa1gvRw=
github.com/onflow/flow-ft/test v0.0.0-20200605203250-755c0ddcc598 h1:WJunAgDYt3d+29UDU+STpRnMiq8joeMj816f7hKgb8k=
github.com/onflow/flow-ft/test v0.0.0-20200605203250-755c0ddcc598/go.mod h1:YCClKQlh1+C4Cq67FU6ISvT/4uQ4m0ZGiODctaOieTk=
github.com/onflow/flow-ft/test v0.0.0-20200619173914-64c953134397 h1:W0fKypbuTWo/z3ccoGKzSu88mivU66E+zCTSTcWTaPg=
github.com/onflow/flow-ft/test v0.0.0-20200619173914-64c953134397/go.mod h1:YCClKQlh1+C4Cq67FU6ISvT/4uQ4m0ZGiODctaOieTk=
github.com/onflow/flow-go-sdk v0.3.0-beta1/go.mod h1:8v6vcYGh5/PtYcM3IXflLcCb00I91WDbnNF8SjirnlI=
github.com/onflow/flow-go-sdk v0.4.0 h1:ZNEE8HQ6xTyr4+RmlxZ2+U/BjKtQDsAB54I+D8AJpZA=
github.com/onflow/flow-go-sdk v0.4.0/go.mod h1:MHn8oQCkBNcl2rXdYSm9VYYK4ogwEpyrdM/XK/czdlM=
github.com/onflow/flow-go-sdk v0.5.0 h1:jAEI6aaCS53iynZ+S5U/6jGKOCLR9q4E3vbEyrz76Ik=
github.com/onflow/flow-go-sdk v0.5.0/go.mod h1:jTb6IjxQDcdoms1z277HUmgeVQ7JmhtCDhRKChiXclk=
github.com/onflow/flow-nft v0.0.0-20200528194639-18c29d3c8e3c/go.mod h1:Kj3iVWzbxYbFLO1CGKPZ8oPlW6qsoOuUSpyvFPPiyQc=
github.com/onflow/flow/protobuf/go/flow v0.1.4/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
github.com/onflow/flow/protobuf/go/flow v0.1.5-0.20200601215056-34a11def1d6b/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
github.com/onflow/flow/protobuf/go/flow v0.1.5-0.20200611205353-548107cc9aca/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
Expand Down
Loading

0 comments on commit 509a18d

Please sign in to comment.