Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nft v1.0.0 upgrade #252

Merged
merged 3 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions contracts/FastBreakV1.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
\/ \/ \/ \/ \/ \/

fast break game contract & oracle
micro coder: jer ahrens <jer.ahrens@dapperlabs.com>

*/

Expand Down Expand Up @@ -659,9 +658,9 @@ access(all) contract FastBreakV1: NonFungibleToken {
FastBreakNFTCollectionPublic
{

access(contract) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not find a fast break with the given ID in the Fast Break collection. Fast break Id: ".concat(withdrawID.toString()))

Expand Down
22 changes: 14 additions & 8 deletions contracts/TopShot.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS
import ViewResolver from 0xVIEWRESOLVERADDRESS

access(all) contract TopShot: NonFungibleToken {

access(all) entitlement NFTMinter
// -----------------------------------------------------------------------
// TopShot deployment variables
// -----------------------------------------------------------------------
Expand Down Expand Up @@ -1060,7 +1062,7 @@ access(all) contract TopShot: NonFungibleToken {
access(all) resource Collection: MomentCollectionPublic, NonFungibleToken.Collection {
// Dictionary of Moment conforming tokens
// NFT is a resource type with a UInt64 ID field
access(contract) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}
access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}}

init() {
self.ownedNFTs <- {}
Expand Down Expand Up @@ -1098,7 +1100,7 @@ access(all) contract TopShot: NonFungibleToken {
// that is to be removed from the Collection
//
// returns: @NonFungibleToken.NFT the token that was withdrawn
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {

// Borrow nft and check if locked
let nft = self.borrowNFT(withdrawID)
Expand All @@ -1124,7 +1126,7 @@ access(all) contract TopShot: NonFungibleToken {
// Returns: @NonFungibleToken.Collection: A collection that contains
// the withdrawn moments
//
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
// Create a new empty Collection
var batchCollection <- create Collection()

Expand Down Expand Up @@ -1181,11 +1183,13 @@ access(all) contract TopShot: NonFungibleToken {

// lock takes a token id and a duration in seconds and locks
// the moment for that duration
access(NonFungibleToken.Update | NonFungibleToken.Owner) fun lock(id: UInt64, duration: UFix64) {
access(NonFungibleToken.Update) fun lock(id: UInt64, duration: UFix64) {
// Remove the nft from the Collection
let token <- self.ownedNFTs.remove(key: id)
?? panic("Cannot lock: Moment does not exist in the collection")

TopShot.emitNFTUpdated(&token as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT})

// pass the token to the locking contract
// store it again after it comes back
let oldToken <- self.ownedNFTs[id] <- TopShotLocking.lockNFT(nft: <- token, duration: duration)
Expand All @@ -1195,7 +1199,7 @@ access(all) contract TopShot: NonFungibleToken {

// batchLock takes an array of token ids and a duration in seconds
// it iterates through the ids and locks each for the specified duration
access(NonFungibleToken.Update | NonFungibleToken.Owner) fun batchLock(ids: [UInt64], duration: UFix64) {
access(NonFungibleToken.Update) fun batchLock(ids: [UInt64], duration: UFix64) {
// Iterate through the ids and lock them
for id in ids {
self.lock(id: id, duration: duration)
Expand All @@ -1204,11 +1208,13 @@ access(all) contract TopShot: NonFungibleToken {

// unlock takes a token id and attempts to unlock it
// TopShotLocking.unlockNFT contains business logic around unlock eligibility
access(NonFungibleToken.Update | NonFungibleToken.Owner) fun unlock(id: UInt64) {
access(NonFungibleToken.Update) fun unlock(id: UInt64) {
// Remove the nft from the Collection
let token <- self.ownedNFTs.remove(key: id)
?? panic("Cannot lock: Moment does not exist in the collection")

TopShot.emitNFTUpdated(&token as auth(NonFungibleToken.Update) &{NonFungibleToken.NFT})

// Pass the token to the TopShotLocking contract then get it back
// Store it back to the ownedNFTs dictionary
let oldToken <- self.ownedNFTs[id] <- TopShotLocking.unlockNFT(nft: <- token)
Expand All @@ -1218,7 +1224,7 @@ access(all) contract TopShot: NonFungibleToken {

// batchUnlock takes an array of token ids
// it iterates through the ids and unlocks each if they are eligible
access(NonFungibleToken.Update | NonFungibleToken.Owner) fun batchUnlock(ids: [UInt64]) {
access(NonFungibleToken.Update) fun batchUnlock(ids: [UInt64]) {
// Iterate through the ids and unlocks them
for id in ids {
self.unlock(id: id)
Expand All @@ -1230,7 +1236,7 @@ access(all) contract TopShot: NonFungibleToken {
//
// Parameters: ids: An array of NFT IDs
// to be destroyed from the Collection
access(NonFungibleToken.Update | NonFungibleToken.Owner) fun destroyMoments(ids: [UInt64]) {
access(NonFungibleToken.Update) fun destroyMoments(ids: [UInt64]) {
let topShotLockingAdmin = TopShot.account.storage.borrow<&TopShotLocking.Admin>(from: TopShotLocking.AdminStoragePath())
?? panic("No TopShotLocking admin resource in storage")

Expand Down
12 changes: 10 additions & 2 deletions contracts/TopShotShardedCollection.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ access(all) contract TopShotShardedCollection {
// This makes storage more efficient and performant
access(all) let numBuckets: UInt64

access(all) fun forEachID(_ f: fun (UInt64): Bool): Void {
for key in self.collections.keys {
let col = &self.collections[key] as &TopShot.Collection?
col?.forEachID(f)
}
}


init(numBuckets: UInt64) {
self.collections <- {}
self.numBuckets = numBuckets
Expand All @@ -68,7 +76,7 @@ access(all) contract TopShotShardedCollection {

// withdraw removes a Moment from one of the Collections
// and moves it to the caller
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
post {
result.id == withdrawID: "The ID of the withdrawn NFT is incorrect"
}
Expand All @@ -87,7 +95,7 @@ access(all) contract TopShotShardedCollection {
//
// Returns: @NonFungibleToken.Collection a Collection containing the moments
// that were withdrawn
access(NonFungibleToken.Withdraw | NonFungibleToken.Owner) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
access(NonFungibleToken.Withdraw) fun batchWithdraw(ids: [UInt64]): @{NonFungibleToken.Collection} {
var batchCollection <- TopShot.createEmptyCollection(nftType: Type<@TopShot.NFT>())

// Iterate through the ids and withdraw them from the Collection
Expand Down
18 changes: 9 additions & 9 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

105 changes: 33 additions & 72 deletions lib/go/test/fast_break_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package test

import (
"context"
"testing"
"time"

"github.com/dapperlabs/nba-smart-contracts/lib/go/contracts"
"github.com/dapperlabs/nba-smart-contracts/lib/go/templates"
"github.com/onflow/cadence"
Expand All @@ -13,9 +16,7 @@ import (
"github.com/onflow/flow-go-sdk/test"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
)

// Tests all the main functionality of the TopShot Locking contract
Expand All @@ -35,43 +36,13 @@ func TestFastBreak(t *testing.T) {
logger := zerolog.Nop()
adapter := adapters.NewSDKAdapter(&logger, b)

viewResolverCode, _ := DownloadFile(ViewResolverContractsBaseURL + ViewResolverInterfaceFile)
viewResolverAddr, err := adapter.CreateAccount(context.Background(), nil, []sdktemplates.Contract{
{
Name: "ViewResolver",
Source: string(viewResolverCode),
},
})

assert.Nil(t, err)
viewResolverAddr := flow.HexToAddress("f8d6e0586b0a20c7")
env.ViewResolverAddress = viewResolverAddr.String()

// Should be able to deploy a contract as a new account with no keys.
nftCode, nftCodeErr := DownloadFile(NonFungibleTokenContractsBaseURL + NonFungibleTokenInterfaceFile)
parsedNFTContract := strings.Replace(string(nftCode), ViewResolverReplaceAddress, "0x"+viewResolverAddr.String(), 1)
assert.Nil(t, nftCodeErr)
nftAddr, nftAddrErr := adapter.CreateAccount(context.Background(), nil, []sdktemplates.Contract{
{
Name: "NonFungibleToken",
Source: parsedNFTContract,
},
})
assert.Nil(t, nftAddrErr)
nftAddr := flow.HexToAddress("f8d6e0586b0a20c7")
env.NFTAddress = nftAddr.String()

// Should be able to deploy a contract as a new account with no keys.
metadataViewsCode, metadataViewsCodeErr := DownloadFile(MetadataViewsContractsBaseURL + MetadataViewsInterfaceFile)
assert.Nil(t, metadataViewsCodeErr)
parsedMetadataContract := strings.Replace(string(metadataViewsCode), MetadataFTReplaceAddress, "0x"+emulatorFTAddress, 1)
parsedMetadataContract = strings.Replace(parsedMetadataContract, MetadataNFTReplaceAddress, "0x"+nftAddr.String(), 1)
parsedMetadataContract = strings.Replace(parsedMetadataContract, ViewResolverReplaceAddress, "0x"+viewResolverAddr.String(), 1)
metadataViewsAddr, metadataViewsAddrErr := adapter.CreateAccount(context.Background(), nil, []sdktemplates.Contract{
{
Name: "MetadataViews",
Source: parsedMetadataContract,
},
})
assert.Nil(t, metadataViewsAddrErr)
metadataViewsAddr := flow.HexToAddress("f8d6e0586b0a20c7")
env.MetadataViewsAddress = metadataViewsAddr.String()

// Deploy TopShot Locking contract
Expand All @@ -86,18 +57,8 @@ func TestFastBreak(t *testing.T) {
assert.Nil(t, topShotLockingAddrErr)
env.TopShotLockingAddress = topShotLockingAddr.String()

fungibleTokenSwitchboardCode, _ := DownloadFile(FungibleTokenSwitchboardContractsBaseURL + FungibleTokenSwitchboardInterfaceFile)
parsedFungibleSwitchboardContract := strings.Replace(string(fungibleTokenSwitchboardCode), MetadataFTReplaceAddress, "0x"+emulatorFTAddress, 1)
topShotRoyaltyAccountKey, _ := accountKeys.NewWithSigner()
topShotRoyaltyAddr, topShotRoyaltyAddrErr := adapter.CreateAccount(context.Background(), []*flow.AccountKey{topShotRoyaltyAccountKey}, []sdktemplates.Contract{
{
Name: "FungibleTokenSwitchboard",
Source: parsedFungibleSwitchboardContract,
},
})
assert.Nil(t, err)
topShotRoyaltyAddr := flow.HexToAddress("ee82856bf20e2aa6")
env.FTSwitchboardAddress = topShotRoyaltyAddr.String()
assert.Nil(t, topShotRoyaltyAddrErr)

// Deploy the topshot contract
topshotCode := contracts.GenerateTopShotContract(defaultfungibleTokenAddr, nftAddr.String(), metadataViewsAddr.String(), viewResolverAddr.String(), topShotLockingAddr.String(), topShotRoyaltyAddr.String(), Network)
Expand Down Expand Up @@ -125,14 +86,13 @@ func TestFastBreak(t *testing.T) {
Source: string(fastBreakCode),
},
})
assert.Nil(t, fastBreakAddrErr)
require.NoError(t, fastBreakAddrErr)
env.FastBreakAddress = fastBreakAddr.String()
assert.Nil(t, err)

// create a new user account
jerAccountKey, jerSigner := accountKeys.NewWithSigner()
jerAddress, jerAddressErr := adapter.CreateAccount(context.Background(), []*flow.AccountKey{jerAccountKey}, nil)
assert.Nil(t, jerAddressErr)
aliceAccountKey, aliceSigner := accountKeys.NewWithSigner()
aliceAddress, aliceAddressErr := adapter.CreateAccount(context.Background(), []*flow.AccountKey{aliceAccountKey}, nil)
require.NoError(t, aliceAddressErr)

firstName := CadenceString("FullName")
lebron := CadenceString("Lebron")
Expand Down Expand Up @@ -279,10 +239,10 @@ func TestFastBreak(t *testing.T) {

// Check that that main contract fields were initialized correctly
result := executeScriptAndCheck(t, b, templates.GenerateGetFastBreakScript(env), [][]byte{jsoncdc.MustEncode(cdcId)})
interfaceArray := result.ToGoValue().([]interface{})
resultId := interfaceArray[0].(string)
assert.NotNil(t, result)
assert.Equal(t, fastBreakID, resultId)

resultId := cadence.SearchFieldByName(result.(cadence.Optional).Value.(cadence.Struct), "id")
assert.Equal(t, cadence.String(fastBreakID), resultId)
})

t.Run("oracle should be able to add a stat to a fast break game", func(t *testing.T) {
Expand Down Expand Up @@ -316,22 +276,22 @@ func TestFastBreak(t *testing.T) {

// Check that that main contract fields were initialized correctly
result := executeScriptAndCheck(t, b, templates.GenerateGetFastBreakStatsScript(env), [][]byte{jsoncdc.MustEncode(cdcId)})
interfaceArray := result.ToGoValue().([]interface{})
assert.Equal(t, 1, len(interfaceArray))
interfaceArray := result.(cadence.Array)
assert.Len(t, interfaceArray.Values, 1)
assert.NotNil(t, result)
})

t.Run("player should be able to create a moment collection", func(t *testing.T) {
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateSetupAccountScript(env), jerAddress)
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateSetupAccountScript(env), aliceAddress)
signAndSubmit(
t, b, tx,
[]flow.Address{b.ServiceKey().Address, jerAddress}, []crypto.Signer{serviceKeySigner, jerSigner},
[]flow.Address{b.ServiceKey().Address, aliceAddress}, []crypto.Signer{serviceKeySigner, aliceSigner},
false,
)
})

t.Run("player should be able to setup game wallet", func(t *testing.T) {
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateFastBreakCreateAccountScript(env), jerAddress)
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateFastBreakCreateAccountScript(env), aliceAddress)
playerName, playerNameErr := cadence.NewString("houseofhufflepuff")
assert.Nil(t, playerNameErr)

Expand All @@ -340,16 +300,17 @@ func TestFastBreak(t *testing.T) {

signAndSubmit(
t, b, tx,
[]flow.Address{b.ServiceKey().Address, jerAddress}, []crypto.Signer{serviceKeySigner, jerSigner},
[]flow.Address{b.ServiceKey().Address, aliceAddress}, []crypto.Signer{serviceKeySigner, aliceSigner},
false,
)

result := executeScriptAndCheck(t, b, templates.GenerateCurrentPlayerScript(env), nil)
playerId = result.ToGoValue().(uint64)
require.NotNil(t, result)
playerId = uint64(result.(cadence.UInt64))
assert.Equal(t, cadence.NewUInt64(1), result)
})

// mint moment 1 to jer
// mint moment 1 to alice
{
tx := createTxWithTemplateAndAuthorizer(b, templates.GenerateMintMomentScript(env), topshotAddr)

Expand All @@ -359,7 +320,7 @@ func TestFastBreak(t *testing.T) {
arg1Err := tx.AddArgument(cadence.NewUInt32(1))
assert.Nil(t, arg1Err)

arg2Err := tx.AddArgument(cadence.NewAddress(jerAddress))
arg2Err := tx.AddArgument(cadence.NewAddress(aliceAddress))
assert.Nil(t, arg2Err)

signAndSubmit(
Expand All @@ -370,7 +331,7 @@ func TestFastBreak(t *testing.T) {
}

t.Run("player should not be able play fast break without top shots", func(t *testing.T) {
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), jerAddress)
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), aliceAddress)
cdcId, _ := cadence.NewString(fastBreakID)
ids := []cadence.Value{cadence.NewUInt64(2)}

Expand All @@ -382,7 +343,7 @@ func TestFastBreak(t *testing.T) {

signAndSubmit(
t, b, tx,
[]flow.Address{b.ServiceKey().Address, jerAddress}, []crypto.Signer{serviceKeySigner, jerSigner},
[]flow.Address{b.ServiceKey().Address, aliceAddress}, []crypto.Signer{serviceKeySigner, aliceSigner},
true,
)

Expand All @@ -391,7 +352,7 @@ func TestFastBreak(t *testing.T) {
})

t.Run("player should be able to play fast break", func(t *testing.T) {
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), jerAddress)
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), aliceAddress)
cdcId, _ := cadence.NewString(fastBreakID)
cdcTopShots := []cadence.Value{cadence.NewUInt64(1)}

Expand All @@ -403,7 +364,7 @@ func TestFastBreak(t *testing.T) {

signAndSubmit(
t, b, tx,
[]flow.Address{b.ServiceKey().Address, jerAddress}, []crypto.Signer{serviceKeySigner, jerSigner},
[]flow.Address{b.ServiceKey().Address, aliceAddress}, []crypto.Signer{serviceKeySigner, aliceSigner},
false,
)

Expand All @@ -412,7 +373,7 @@ func TestFastBreak(t *testing.T) {
})

t.Run("player should not be able to resubmit fast break", func(t *testing.T) {
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), jerAddress)
tx := createTxWithTemplateAndAuthorizer(b, templates.GeneratePlayFastBreakScript(env), aliceAddress)
cdcId, _ := cadence.NewString(fastBreakID)
ids := []cadence.Value{cadence.NewUInt64(1)}

Expand All @@ -424,7 +385,7 @@ func TestFastBreak(t *testing.T) {

signAndSubmit(
t, b, tx,
[]flow.Address{b.ServiceKey().Address, jerAddress}, []crypto.Signer{serviceKeySigner, jerSigner},
[]flow.Address{b.ServiceKey().Address, aliceAddress}, []crypto.Signer{serviceKeySigner, aliceSigner},
true,
)

Expand Down Expand Up @@ -462,7 +423,7 @@ func TestFastBreak(t *testing.T) {
arg0Err := tx.AddArgument(cdcId)
assert.Nil(t, arg0Err)

arg1Err := tx.AddArgument(cadence.NewAddress(jerAddress))
arg1Err := tx.AddArgument(cadence.NewAddress(aliceAddress))
assert.Nil(t, arg1Err)

arg2Err := tx.AddArgument(cadence.NewUInt64(100))
Expand All @@ -481,7 +442,7 @@ func TestFastBreak(t *testing.T) {
t,
b,
templates.GenerateGetPlayerScoreScript(env),
[][]byte{jsoncdc.MustEncode(cdcId), jsoncdc.MustEncode(cadence.NewAddress(jerAddress))},
[][]byte{jsoncdc.MustEncode(cdcId), jsoncdc.MustEncode(cadence.NewAddress(aliceAddress))},
)
assert.Equal(t, cadence.NewUInt64(100), result)
})
Expand Down
Loading
Loading