├── evm-bridging ├── cadence │ ├── transactions │ │ ├── .gitignore │ │ ├── utils │ │ │ ├── unwrap_nfts_args.json │ │ │ └── wrap_nfts_args.json │ │ ├── bridge_nfts_from_evm_args.json │ │ ├── admin │ │ │ ├── set_up_royalty_management_args.json │ │ │ ├── deploy │ │ │ │ ├── deploy_contract.cdc │ │ │ │ └── create_new_account_with_coa_and_flow_deposit.cdc │ │ │ └── set_up_royalty_management.cdc │ │ ├── transfer_erc721s_to_evm_address_args.json │ │ ├── bridge_nfts_to_evm_args.json │ │ ├── transfer_flow_to_evm_address.cdc │ │ └── tests │ │ │ └── test_uint_array_encoding.cdc │ └── scripts │ │ ├── get_evm_address_string.cdc │ │ └── is_erc721_wrapped.cdc ├── .gitignore ├── remappings.txt ├── src │ ├── interfaces │ │ ├── ICrossVM.sol │ │ ├── IBridgePermissions.sol │ │ ├── ITransferValidator.sol │ │ ├── ICreatorToken.sol │ │ ├── ICrossVMBridgeCallable.sol │ │ └── ICrossVMBridgeERC721Fulfillment.sol │ ├── test-contracts │ │ └── TestContract.sol │ └── lib │ │ ├── ERC721TransferValidator.sol │ │ └── CrossVMBridgeCallableUpgradeable.sol ├── .env.flowevm.testnet.example ├── foundry.toml └── script │ └── InitialTestingDeploy.s.sol ├── .github ├── CODEOWNERS ├── workflows │ └── ci.yml └── ISSUE_TEMPLATE │ ├── pull_request_template.md │ ├── bug_report.md │ └── feature-request.md ├── .gitignore ├── Makefile ├── NOTICE ├── transactions ├── fastbreak │ ├── scripts │ │ ├── get_current_player.cdc │ │ ├── get_token_count.cdc │ │ ├── get_fast_break.cdc │ │ ├── get_fast_break_submission_deadline.cdc │ │ ├── get_fast_break_stats.cdc │ │ ├── get_player_win_count_for_run.cdc │ │ └── get_player_score.cdc │ ├── player │ │ ├── update_submission.cdc │ │ ├── play.cdc │ │ └── create_player.cdc │ └── oracle │ │ ├── update_fast_break_game.cdc │ │ ├── update_submission_deadline.cdc │ │ ├── add_stat_to_game.cdc │ │ ├── create_run.cdc │ │ ├── create_game.cdc │ │ └── score_fast_break_submission.cdc ├── scripts │ ├── subeditions │ │ ├── get_nft_subedition.cdc │ │ ├── get_all_subeditions.cdc │ │ ├── get_nextSubeditionID.cdc │ │ └── get_subedition_by_id.cdc │ ├── collections │ │ ├── get_locked_nfts_length.cdc │ │ ├── get_moment_isLocked.cdc │ │ ├── get_id_in_Collection.cdc │ │ ├── get_collection_ids.cdc │ │ ├── get_moment_lockExpiry.cdc │ │ ├── get_moment_serialNum.cdc │ │ ├── get_moment_playID.cdc │ │ ├── get_moment_series.cdc │ │ ├── borrow_nft_safe.cdc │ │ ├── get_moment_setID.cdc │ │ ├── get_moment_setName.cdc │ │ ├── get_metadata.cdc │ │ ├── get_metadata_field.cdc │ │ └── get_setplays_are_owned.cdc │ ├── get_currentSeries.cdc │ ├── plays │ │ ├── get_all_plays.cdc │ │ ├── get_nextPlayID.cdc │ │ ├── get_play_metadata.cdc │ │ └── get_play_metadata_field.cdc │ ├── get_totalSupply.cdc │ ├── sets │ │ ├── get_nextSetID.cdc │ │ ├── get_plays_in_set.cdc │ │ ├── get_setSeries.cdc │ │ ├── get_set_data.cdc │ │ ├── get_setName.cdc │ │ ├── get_setIDs_by_name.cdc │ │ ├── get_set_locked.cdc │ │ ├── get_edition_retired.cdc │ │ └── get_numMoments_in_edition.cdc │ ├── users │ │ └── is_account_all_set_up.cdc │ ├── get_topshot_metadata.cdc │ └── setup_sharded_locker_room.cdc ├── admin │ ├── create_set_and_play_struct.cdc │ ├── unlock_all_moments.cdc │ ├── set_nfts_lock_expiry.cdc │ ├── grant_topshot_locking_admin.cdc │ ├── transfer_admin.cdc │ ├── create_new_subedition_admin_resource.cdc │ ├── set_nft_subedition.cdc │ ├── add_plays_to_set.cdc │ ├── retireAll_plays_from_set.cdc │ ├── start_new_series.cdc │ ├── lock_set.cdc │ ├── retire_all.cdc │ ├── mark_moment_unlockable.cdc │ ├── create_set.cdc │ ├── add_play_to_set.cdc │ ├── create_play.cdc │ ├── create_subedition.cdc │ ├── retire_play_from_set.cdc │ ├── update_tagline.cdc │ ├── mint_moment.cdc │ ├── mint_moment_with_subedition.cdc │ ├── batch_mint_moment.cdc │ ├── fulfill_pack.cdc │ └── batch_mint_moment_with_subedition.cdc ├── marketV3 │ ├── scripts │ │ ├── get_sale_len.cdc │ │ ├── get_sale_percentage.cdc │ │ ├── get_sale_set_id.cdc │ │ └── get_sale_price.cdc │ ├── change_receiver.cdc │ ├── change_price.cdc │ ├── start_sale.cdc │ ├── mint_and_purchase.cdc │ ├── stop_sale.cdc │ ├── upgrade_sale.cdc │ ├── create_sale.cdc │ ├── purchase_moment.cdc │ └── purchase_both_markets.cdc ├── user │ ├── batch_unlock_moments.cdc │ ├── unlock_moment.cdc │ ├── lock_moment.cdc │ ├── lock_fake_nft.cdc │ ├── batch_lock_moments.cdc │ ├── setup_collection.cdc │ ├── batch_transfer.cdc │ ├── destroy_moments_v2.cdc │ ├── destroy_moments.cdc │ ├── transfer_moment.cdc │ ├── transfer_moment_v3_sale.cdc │ ├── setup_up_all_collections.cdc │ └── setup_switchboard_account.cdc ├── market │ ├── scripts │ │ ├── get_sale_len.cdc │ │ ├── get_sale_percentage.cdc │ │ ├── get_sale_price.cdc │ │ └── get_sale_set_id.cdc │ ├── change_price.cdc │ ├── change_receiver.cdc │ ├── change_percentage.cdc │ ├── create_sale.cdc │ ├── stop_sale.cdc │ ├── start_sale.cdc │ ├── mint_and_purchase.cdc │ └── purchase_moment.cdc └── shardedCollection │ ├── transfer_from_sharded.cdc │ ├── batch_from_sharded.cdc │ └── setup_sharded_collection.cdc ├── lib └── go │ ├── events │ ├── test_utils.go │ ├── set_locked.go │ ├── moment_unlocked.go │ ├── set.go │ ├── moment_destroy.go │ ├── play.go │ ├── set_play.go │ ├── set_locked_test.go │ ├── moment_destroy_test.go │ ├── subedition_added_to_moment.go │ ├── withdraw.go │ ├── moment_locked.go │ ├── set_play_retired.go │ ├── set_test.go │ ├── revealed.go │ ├── set_play_test.go │ ├── deposit_test.go │ ├── subedition.go │ ├── withdraw_test.go │ ├── deposit.go │ ├── subedition_added_to_moment_test.go │ ├── set_play_retired_test.go │ ├── moment_minted.go │ ├── revealed_test.go │ ├── play_test.go │ ├── subedition_test.go │ ├── moment_minted_test.go │ └── go.mod │ ├── templates │ ├── go.sum │ ├── go.mod │ ├── Makefile │ ├── data │ │ └── set_metadata.go │ ├── fastbreak_user_templates.go │ ├── topshot_sharded_collection_templates.go │ ├── fastbreak_oracle_templates.go │ └── fastbreak_script_templates.go │ ├── test │ └── Makefile │ ├── contracts │ ├── Makefile │ ├── go.mod │ └── contracts_test.go │ ├── Makefile │ └── README.md ├── go.mod ├── embed_test.go ├── .gitmodules ├── go.sum ├── LICENSE └── contracts └── TopshotAdminReceiver.cdc /evm-bridging/cadence/transactions/.gitignore: -------------------------------------------------------------------------------- 1 | *.json -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | *.cdc @dapperlabs/flow-smart-contracts 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | flow.json 3 | /imports 4 | .vscode 5 | .env 6 | **/vendor/ 7 | *.pkey -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | $(MAKE) generate -C lib/go 4 | $(MAKE) test -C lib/go 5 | 6 | .PHONY: ci 7 | ci: 8 | $(MAKE) ci -C lib/go 9 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | NBA Top Shot 2 | Copyright 2019-2020 Dapper Labs, Inc. 3 | 4 | This product includes software developed at Dapper Labs, Inc. (https://www.dapperlabs.com/). -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_current_player.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(): UInt64 { 4 | 5 | return FastBreakV1.nextPlayerId 6 | } -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_token_count.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(): UInt64 { 4 | 5 | return FastBreakV1.totalSupply 6 | } -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_fast_break.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(id: String): &FastBreakV1.FastBreakGame? { 4 | return FastBreakV1.getFastBreakGame(id: id) 5 | } -------------------------------------------------------------------------------- /evm-bridging/.gitignore: -------------------------------------------------------------------------------- 1 | # foundry - compiler files 2 | /out 3 | /cache 4 | 5 | # foundry - broadcast logs 6 | /broadcast 7 | 8 | # keys 9 | *.pkey 10 | *.pem 11 | 12 | # dotenv file 13 | *.env 14 | 15 | # macOS 16 | .DS_Store -------------------------------------------------------------------------------- /lib/go/events/test_utils.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/onflow/cadence" 5 | ) 6 | 7 | func NewCadenceString(str string) cadence.String { 8 | res, _ := cadence.NewString(str) 9 | return res 10 | } 11 | -------------------------------------------------------------------------------- /lib/go/templates/go.sum: -------------------------------------------------------------------------------- 1 | github.com/kevinburke/go-bindata v3.22.0+incompatible h1:/JmqEhIWQ7GRScV0WjX/0tqBrC5D21ALg0H0U/KZ/ts= 2 | github.com/kevinburke/go-bindata v3.22.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= 3 | -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_fast_break_submission_deadline.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(id: String): UInt64? { 4 | return FastBreakV1.getFastBreakGame(id: id)?.submissionDeadline 5 | } -------------------------------------------------------------------------------- /evm-bridging/remappings.txt: -------------------------------------------------------------------------------- 1 | @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ 2 | @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ 3 | openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/ -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/ICrossVM.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.24; 2 | 3 | interface ICrossVM { 4 | function getCadenceAddress() external view returns (string memory); 5 | function getCadenceIdentifier() external view returns (string memory); 6 | } 7 | -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_fast_break_stats.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(fastBreakGameID: String): &[FastBreakV1.FastBreakStat]? { 4 | return FastBreakV1.getFastBreakGameStats(id: fastBreakGameID) 5 | } -------------------------------------------------------------------------------- /evm-bridging/.env.flowevm.testnet.example: -------------------------------------------------------------------------------- 1 | 2 | RPC_URL=https://testnet.evm.nodes.onflow.org 3 | VERIFIER_URL=https://evm-testnet.flowscan.io/api 4 | VERIFIER_PROVIDER=blockscout 5 | 6 | DEPLOYED_PROXY_CONTRACT_ADDRESS= 7 | 8 | DEPLOYER_ADDRESS= 9 | DEPLOYER_PRIVATE_KEY= -------------------------------------------------------------------------------- /lib/go/templates/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dapperlabs/nba-smart-contracts/lib/go/templates 2 | 3 | go 1.16 4 | 5 | replace github.com/dapperlabs/nba-smart-contracts/lib/go/templates => ../templates 6 | 7 | require github.com/kevinburke/go-bindata v3.22.0+incompatible 8 | -------------------------------------------------------------------------------- /lib/go/test/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | go test ./... 4 | 5 | .PHONY: generate 6 | generate: 7 | go generate 8 | 9 | .PHONY: check-tidy 10 | check-tidy: generate 11 | go mod tidy 12 | git diff --exit-code 13 | 14 | .PHONY: ci 15 | ci: check-tidy test 16 | -------------------------------------------------------------------------------- /lib/go/contracts/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | go test ./... 4 | 5 | .PHONY: generate 6 | generate: 7 | go generate 8 | 9 | .PHONY: check-tidy 10 | check-tidy: generate 11 | go mod tidy 12 | git diff --exit-code 13 | 14 | .PHONY: ci 15 | ci: check-tidy test 16 | -------------------------------------------------------------------------------- /lib/go/templates/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | go test ./... 4 | 5 | .PHONY: generate 6 | generate: 7 | go generate 8 | 9 | .PHONY: check-tidy 10 | check-tidy: generate 11 | go mod tidy 12 | git diff --exit-code 13 | 14 | .PHONY: ci 15 | ci: check-tidy test 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-go@v1 11 | with: 12 | go-version: '1.22' 13 | - run: make ci 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dapperlabs/nba-smart-contracts 2 | 3 | go 1.22.5 4 | 5 | require github.com/stretchr/testify v1.9.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /evm-bridging/src/test-contracts/TestContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | contract TestContract { 5 | function testArrayEncoding(uint256[] calldata values) external pure 6 | returns (uint256[] memory) 7 | { 8 | return values; 9 | } 10 | } -------------------------------------------------------------------------------- /transactions/scripts/subeditions/get_nft_subedition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | access(all) fun main(nftID: UInt64): UInt32 { 4 | 5 | let subedition = TopShot.getMomentsSubedition(nftID: nftID) 6 | ?? panic("Could not find the specified moment") 7 | return subedition 8 | } -------------------------------------------------------------------------------- /lib/go/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | $(MAKE) test -C contracts 4 | $(MAKE) test -C test 5 | 6 | .PHONY: generate 7 | generate: 8 | $(MAKE) generate -C contracts 9 | $(MAKE) generate -C templates 10 | 11 | .PHONY: ci 12 | ci: 13 | $(MAKE) ci -C contracts 14 | $(MAKE) ci -C templates 15 | $(MAKE) ci -C test -------------------------------------------------------------------------------- /embed_test.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | import "os" 4 | 5 | import ( 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func TestEmbed(t *testing.T) { 11 | content, err := os.ReadFile("transactions/admin/fulfill_pack.cdc") 12 | assert.NoError(t, err) 13 | assert.Equal(t, AdminFulfillPack, content) 14 | } 15 | -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/utils/unwrap_nfts_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "Array", 8 | "value": [ 9 | { 10 | "type": "UInt256", 11 | "value": "" 12 | } 13 | ] 14 | } 15 | ] -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/utils/wrap_nfts_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "Array", 8 | "value": [ 9 | { 10 | "type": "UInt256", 11 | "value": "" 12 | } 13 | ] 14 | } 15 | ] -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/bridge_nfts_from_evm_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "Array", 8 | "value": [ 9 | { 10 | "type": "UInt256", 11 | "value": "" 12 | } 13 | ] 14 | } 15 | ] -------------------------------------------------------------------------------- /transactions/scripts/collections/get_locked_nfts_length.cdc: -------------------------------------------------------------------------------- 1 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 2 | 3 | // This script determines how many NFTs are locked in the Top Shot Locking contract 4 | 5 | // Returns: Int 6 | // The number of locked NFTs 7 | 8 | access(all) fun main(): Int { 9 | return TopShotLocking.getLockedNFTsLength() 10 | } 11 | -------------------------------------------------------------------------------- /transactions/scripts/get_currentSeries.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the current series from the TopShot contract and 4 | // returns that number to the caller 5 | 6 | // Returns: UInt32 7 | // currentSeries field in TopShot contract 8 | 9 | access(all) fun main(): UInt32 { 10 | 11 | return TopShot.currentSeries 12 | } -------------------------------------------------------------------------------- /transactions/scripts/plays/get_all_plays.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns an array of all the plays 4 | // that have ever been created for Top Shot 5 | 6 | // Returns: [TopShot.Play] 7 | // array of all plays created for Topshot 8 | 9 | access(all) fun main(): [TopShot.Play] { 10 | 11 | return TopShot.getAllPlays() 12 | } -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/admin/set_up_royalty_management_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "String", 8 | "value": "" 9 | }, 10 | { 11 | "type": "String", 12 | "value": "" 13 | }, 14 | { 15 | "type": "UInt128", 16 | "value": "" 17 | } 18 | ] -------------------------------------------------------------------------------- /transactions/scripts/subeditions/get_all_subeditions.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns an array of all the plays 4 | // that have ever been created for Top Shot 5 | 6 | // Returns: [TopShot.Play] 7 | // array of all plays created for Topshot 8 | 9 | access(all) fun main(): &[TopShot.Subedition] { 10 | 11 | return TopShot.getAllSubeditions() 12 | } -------------------------------------------------------------------------------- /transactions/scripts/get_totalSupply.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the current number of moments that have been minted 4 | // from the TopShot contract and returns that number to the caller 5 | 6 | // Returns: UInt64 7 | // Number of moments minted from TopShot contract 8 | 9 | access(all) fun main(): UInt64 { 10 | 11 | return TopShot.totalSupply 12 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_nextSetID.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the next Set ID from the TopShot contract and 4 | // returns that number to the caller 5 | 6 | // Returns: UInt32 7 | // Value of nextSetID field in TopShot contract 8 | 9 | access(all) fun main(): UInt32 { 10 | 11 | log(TopShot.nextSetID) 12 | 13 | return TopShot.nextSetID 14 | } -------------------------------------------------------------------------------- /lib/go/templates/data/set_metadata.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | // Cadence requires a mapping of string->string, which can be handled through json tags when marshalling. 4 | // It also does not allow for null values, so we will be omitting them if empty 5 | type SetMetadata struct { 6 | ID string 7 | FlowId *uint32 8 | FlowSeriesNumber *uint32 9 | FlowName string 10 | } 11 | -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_player_win_count_for_run.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(runId: String, playerAddress: Address): UInt64 { 4 | let playerId = FastBreakV1.getPlayerIdByAccount(accountAddress: playerAddress) 5 | if let run = FastBreakV1.getFastBreakRun(id: runId) { 6 | return run.runWinCount[playerId] ?? 0 7 | } 8 | return 0 9 | } -------------------------------------------------------------------------------- /transactions/admin/create_set_and_play_struct.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | transaction() { 4 | 5 | prepare(acct: auth(BorrowValue) &Account) { 6 | 7 | let metadata: {String: String} = {"PlayType": "Shoe becomes untied"} 8 | 9 | let newPlay = TopShot.Play(metadata: metadata) 10 | 11 | let newSet = TopShot.SetData(name: "Sneaky Sneakers") 12 | } 13 | } -------------------------------------------------------------------------------- /transactions/scripts/plays/get_nextPlayID.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the public nextPlayID from the TopShot contract and 4 | // returns that number to the caller 5 | 6 | // Returns: UInt32 7 | // the nextPlayID field in TopShot contract 8 | 9 | access(all) fun main(): UInt32 { 10 | 11 | log(TopShot.nextPlayID) 12 | 13 | return TopShot.nextPlayID 14 | } -------------------------------------------------------------------------------- /transactions/scripts/subeditions/get_nextSubeditionID.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the nextSubeditionID from the SubeditionAdmin resource and 4 | // returns that number to the caller 5 | 6 | // Returns: UInt32 7 | // the next number in nextSubeditionID from the SubeditionAdmin resource 8 | 9 | access(all) fun main(): UInt32 { 10 | 11 | return TopShot.getNextSubeditionID() 12 | } -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/transfer_erc721s_to_evm_address_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "String", 8 | "value": "" 9 | }, 10 | { 11 | "type": "Array", 12 | "value": [ 13 | { 14 | "type": "UInt256", 15 | "value": "" 16 | } 17 | ] 18 | } 19 | ] -------------------------------------------------------------------------------- /evm-bridging/cadence/scripts/get_evm_address_string.cdc: -------------------------------------------------------------------------------- 1 | import "EVM" 2 | 3 | /// Returns the hex encoded address of the COA in the given Flow address 4 | /// 5 | access(all) fun main(flowAddress: Address): String? { 6 | return getAuthAccount(flowAddress) 7 | .storage.borrow<&EVM.CadenceOwnedAccount>(from: /storage/evm) 8 | ?.address() 9 | ?.toString() 10 | ?? nil 11 | } 12 | -------------------------------------------------------------------------------- /transactions/fastbreak/scripts/get_player_score.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | access(all) fun main(id: String, playerAddress: Address): UInt64 { 4 | let playerId = FastBreakV1.getPlayerIdByAccount(accountAddress: playerAddress) 5 | let fastBreak = FastBreakV1.getFastBreakGame(id: id) 6 | let submission = fastBreak?.getFastBreakSubmissionByPlayerId(playerId: playerId)! 7 | 8 | return submission?.points ?? 0 9 | } -------------------------------------------------------------------------------- /evm-bridging/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc_version = "0.8.24" 3 | optimizer = true 4 | optimizer_runs = 50000 5 | 6 | src = "src" 7 | out = "out" 8 | libs = ["lib"] 9 | script = "script" 10 | test = "test" 11 | cache_path = "cache" 12 | build_info = true 13 | extra_output = ["storageLayout"] 14 | 15 | ffi = true 16 | ast = true 17 | 18 | # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options 19 | -------------------------------------------------------------------------------- /transactions/marketV3/scripts/get_sale_len.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | access(all) fun main(sellerAddress: Address): Int { 5 | let acct = getAccount(sellerAddress) 6 | let collectionRef = acct.capabilities.borrow<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketPublicPath) 7 | ?? panic("Could not borrow capability from public collection") 8 | 9 | return collectionRef.getIDs().length 10 | } -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/bridge_nfts_to_evm_args.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "String", 4 | "value": "" 5 | }, 6 | { 7 | "type": "Array", 8 | "value": [ 9 | { 10 | "type": "UInt64", 11 | "value": "" 12 | } 13 | ] 14 | }, 15 | { 16 | "type": "Optional", 17 | "value": { 18 | "type": "String", 19 | "value": "" 20 | } 21 | } 22 | ] -------------------------------------------------------------------------------- /transactions/marketV3/scripts/get_sale_percentage.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | access(all) fun main(sellerAddress: Address): UFix64 { 5 | let acct = getAccount(sellerAddress) 6 | let collectionRef = acct.capabilities.borrow<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketPublicPath) 7 | ?? panic("Could not borrow capability from public collection") 8 | 9 | return collectionRef.cutPercentage 10 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_plays_in_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns an array of the play IDs that are 4 | // in the specified set 5 | 6 | // Parameters: 7 | // 8 | // setID: The unique ID for the set whose data needs to be read 9 | 10 | // Returns: [UInt32] 11 | // Array of play IDs in specified set 12 | 13 | access(all) fun main(setID: UInt32): [UInt32] { 14 | 15 | let plays = TopShot.getPlaysInSet(setID: setID)! 16 | 17 | return plays 18 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_setSeries.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script reads the series of the specified set and returns it 4 | 5 | // Parameters: 6 | // 7 | // setID: The unique ID for the set whose data needs to be read 8 | 9 | // Returns: UInt32 10 | // unique ID of series 11 | 12 | access(all) fun main(setID: UInt32): UInt32 { 13 | 14 | let series = TopShot.getSetSeries(setID: setID) 15 | ?? panic("Could not find the specified set") 16 | 17 | return series 18 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_set_data.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns all the metadata about the specified set 4 | 5 | // Parameters: 6 | // 7 | // setID: The unique ID for the set whose data needs to be read 8 | 9 | // Returns: TopShot.QuerySetData 10 | 11 | access(all) fun main(setID: UInt32): TopShot.QuerySetData { 12 | 13 | let data = TopShot.getSetData(setID: setID) 14 | ?? panic("Could not get data for the specified set ID") 15 | 16 | return data 17 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_setName.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the setName of a set with specified setID 4 | 5 | // Parameters: 6 | // 7 | // setID: The unique ID for the set whose data needs to be read 8 | 9 | // Returns: String 10 | // Name of set with specified setID 11 | 12 | access(all) fun main(setID: UInt32): String { 13 | 14 | let name = TopShot.getSetName(setID: setID) 15 | ?? panic("Could not find the specified set") 16 | 17 | return name 18 | } -------------------------------------------------------------------------------- /transactions/admin/unlock_all_moments.cdc: -------------------------------------------------------------------------------- 1 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 2 | 3 | transaction() { 4 | let adminRef: &TopShotLocking.Admin 5 | 6 | prepare(acct: auth(BorrowValue) &Account) { 7 | // Set TopShotLocking admin ref 8 | self.adminRef = acct.storage.borrow<&TopShotLocking.Admin>(from: /storage/TopShotLockingAdmin) 9 | ?? panic("Could not find reference to TopShotLocking Admin resource") 10 | } 11 | 12 | execute { 13 | self.adminRef.unlockAll() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /transactions/marketV3/change_receiver.cdc: -------------------------------------------------------------------------------- 1 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 2 | 3 | transaction(receiverPath: PublicPath) { 4 | prepare(acct: auth(BorrowValue) &Account) { 5 | 6 | let topshotSaleCollection = acct.storage.borrow(from: /storage/topshotSaleCollection) 7 | ?? panic("Could not borrow from sale in storage") 8 | 9 | topshotSaleCollection.changeOwnerReceiver(acct.capabilities.get<&{FungibleToken.Receiver}>(receiverPath)!) 10 | } 11 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_setIDs_by_name.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns an array of the setIDs 4 | // that have the specified name 5 | 6 | // Parameters: 7 | // 8 | // setName: The name of the set whose data needs to be read 9 | 10 | // Returns: [UInt32] 11 | // Array of setIDs that have specified set name 12 | 13 | access(all) fun main(setName: String): [UInt32] { 14 | 15 | let ids = TopShot.getSetIDsByName(setName: setName) 16 | ?? panic("Could not find the specified set name") 17 | 18 | return ids 19 | } -------------------------------------------------------------------------------- /transactions/scripts/subeditions/get_subedition_by_id.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns the full Subedition entity from 4 | // the TopShot smart contract 5 | 6 | // Parameters: 7 | // 8 | // subeditionID: The unique ID for the subedition whose data needs to be read 9 | 10 | // Returns: Subedition 11 | // struct from TopShot contract 12 | 13 | access(all) fun main(subeditionID: UInt32): &TopShot.Subedition { 14 | 15 | let subedititon = TopShot.getSubeditionByID(subeditionID: subeditionID) 16 | 17 | return subedititon 18 | } -------------------------------------------------------------------------------- /transactions/scripts/users/is_account_all_set_up.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from "NonFungibleToken" 2 | import PackNFT from "PackNFT" 3 | import TopShot from "TopShot" 4 | 5 | /// Check if an account has been set up to hold Pinnacle NFTs. 6 | /// 7 | access(all) fun main(address: Address): Bool { 8 | let account = getAccount(address) 9 | return account.capabilities.borrow< 10 | &TopShot.Collection>(/public/MomentCollection) != nil && 11 | account.capabilities.borrow< 12 | &PackNFT.Collection>(PackNFT.CollectionPublicPath) != nil 13 | } 14 | -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/IBridgePermissions.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; 5 | 6 | interface IBridgePermissions is IERC165 { 7 | /** 8 | * @dev Emitted when the permissions for the contract are updated. 9 | */ 10 | event PermissionsUpdated(bool newPermissions); 11 | 12 | /** 13 | * @dev Returns true if the contract allows bridging of its assets. 14 | */ 15 | function allowsBridging() external view returns (bool); 16 | } 17 | -------------------------------------------------------------------------------- /transactions/scripts/sets/get_set_locked.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns a boolean indicating if the specified set is locked 4 | // meaning new plays cannot be added to it 5 | 6 | // Parameters: 7 | // 8 | // setID: The unique ID for the set whose data needs to be read 9 | 10 | // Returns: Bool 11 | // Whether specified set is locked 12 | 13 | access(all) fun main(setID: UInt32): Bool { 14 | 15 | let isLocked = TopShot.isSetLocked(setID: setID) 16 | ?? panic("Could not find the specified set") 17 | 18 | return isLocked 19 | } -------------------------------------------------------------------------------- /transactions/marketV3/scripts/get_sale_set_id.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | access(all) fun main(sellerAddress: Address, momentID: UInt64): UInt32 { 5 | let saleRef = getAccount(sellerAddress).capabilities.borrow<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketPublicPath) 6 | ?? panic("Could not get public sale reference") 7 | 8 | let token = saleRef.borrowMoment(id: momentID) 9 | ?? panic("Could not borrow a reference to the specified moment") 10 | 11 | let data = token.data 12 | 13 | return data.setID 14 | } -------------------------------------------------------------------------------- /transactions/marketV3/scripts/get_sale_price.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | access(all) fun main(sellerAddress: Address, momentID: UInt64): UFix64 { 5 | 6 | let acct = getAccount(sellerAddress) 7 | let collectionRef = acct.capabilities.borrow<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketPublicPath) 8 | ?? panic("Could not borrow capability from public collection") 9 | 10 | let price = collectionRef.getPrice(tokenID: UInt64(momentID)) 11 | ?? panic("Could not find price") 12 | 13 | return price 14 | 15 | } -------------------------------------------------------------------------------- /transactions/user/batch_unlock_moments.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import NonFungibleToken from 0xNFTADDRESS 3 | 4 | // This transaction unlocks a list of TopShot NFTs 5 | 6 | // Parameters 7 | // 8 | // ids: array of TopShot moment Flow IDs 9 | 10 | transaction(ids: [UInt64]) { 11 | prepare(acct: auth(BorrowValue) &Account) { 12 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 13 | ?? panic("Could not borrow from MomentCollection in storage") 14 | 15 | collectionRef.batchUnlock(ids: ids) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /transactions/scripts/sets/get_edition_retired.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction reads if a specified edition is retired 4 | 5 | // Parameters: 6 | // 7 | // setID: The unique ID for the set whose data needs to be read 8 | // playID: The unique ID for the play whose data needs to be read 9 | 10 | // Returns: Bool 11 | // Whether specified set is retired 12 | 13 | access(all) fun main(setID: UInt32, playID: UInt32): Bool { 14 | 15 | let isRetired = TopShot.isEditionRetired(setID: setID, playID: playID) 16 | ?? panic("Could not find the specified edition") 17 | 18 | return isRetired 19 | } -------------------------------------------------------------------------------- /transactions/scripts/plays/get_play_metadata.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns the full metadata associated with a play 4 | // in the TopShot smart contract 5 | 6 | // Parameters: 7 | // 8 | // playID: The unique ID for the play whose data needs to be read 9 | 10 | // Returns: {String:String} 11 | // A dictionary of all the play metadata associated 12 | // with the specified playID 13 | 14 | access(all) fun main(playID: UInt32): {String:String} { 15 | 16 | let metadata = TopShot.getPlayMetaData(playID: playID) ?? panic("Play doesn't exist") 17 | 18 | log(metadata) 19 | 20 | return metadata 21 | } -------------------------------------------------------------------------------- /lib/go/contracts/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dapperlabs/nba-smart-contracts/lib/go/contracts 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/kevinburke/go-bindata v3.22.0+incompatible 7 | github.com/stretchr/testify v1.7.1 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.1 // indirect 12 | github.com/kr/pretty v0.3.0 // indirect 13 | github.com/pmezard/go-difflib v1.0.0 // indirect 14 | github.com/rogpeppe/go-internal v1.8.0 // indirect 15 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 16 | gopkg.in/yaml.v3 v3.0.0 // indirect 17 | ) 18 | 19 | replace github.com/dapperlabs/nba-smart-contracts/lib/go/contracts => ../contracts 20 | -------------------------------------------------------------------------------- /transactions/admin/set_nfts_lock_expiry.cdc: -------------------------------------------------------------------------------- 1 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 2 | 3 | transaction(ids: [UInt64], expiryTimestamp: UFix64) { 4 | let adminRef: &TopShotLocking.Admin 5 | 6 | prepare(acct: auth(BorrowValue) &Account) { 7 | // Set TopShotLocking admin ref 8 | self.adminRef = acct.storage.borrow<&TopShotLocking.Admin>(from: /storage/TopShotLockingAdmin) 9 | ?? panic("Could not find reference to TopShotLocking Admin resource") 10 | } 11 | 12 | execute { 13 | for id in ids { 14 | self.adminRef.setLockExpiryByID(id: id, expiryTimestamp: expiryTimestamp) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Closes: #??? 2 | 3 | ## Description 4 | 5 | 8 | 9 | ______ 10 | 11 | For contributor use: 12 | 13 | - [ ] Targeted PR against `master` branch 14 | - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. 15 | - [ ] Code follows the [standards mentioned here](https://github.com/onflow/flow-nft/blob/master/CONTRIBUTING.md#styleguides). 16 | - [ ] Updated relevant documentation 17 | - [ ] Re-reviewed `Files changed` in the Github PR explorer 18 | - [ ] Added appropriate labels -------------------------------------------------------------------------------- /transactions/admin/grant_topshot_locking_admin.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 3 | 4 | // this transaction takes a TopShot Locking Admin resource and 5 | // saves it to the account storage of the account second authorizer 6 | 7 | transaction { 8 | prepare(acct: auth(BorrowValue) &Account, acct2: auth(SaveValue) &Account) { 9 | let topShotLockingAdmin = acct.storage.borrow<&TopShotLocking.Admin>(from: TopShotLocking.AdminStoragePath()) 10 | ?? panic("could not borrow admin reference") 11 | 12 | acct2.storage.save(<- topShotLockingAdmin.createNewAdmin(), to: TopShotLocking.AdminStoragePath()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /transactions/market/scripts/get_sale_len.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | 3 | // This script gets the number of moments an account has for sale 4 | 5 | // Parameters: 6 | // 7 | // sellerAddress: The Flow Address of the account whose sale collection needs to be read 8 | 9 | // Returns: Int 10 | // Number of moments up for sale in an account 11 | 12 | access(all) fun main(sellerAddress: Address): Int { 13 | 14 | let acct = getAccount(sellerAddress) 15 | 16 | let collectionRef = acct.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 17 | ?? panic("Could not borrow capability from public collection") 18 | 19 | return collectionRef.getIDs().length 20 | } -------------------------------------------------------------------------------- /transactions/scripts/get_topshot_metadata.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import MetadataViews from 0xMETADATAVIEWSADDRESS 3 | 4 | 5 | access(all) fun main(address: Address, id: UInt64): TopShot.TopShotMomentMetadataView { 6 | let account = getAccount(address) 7 | 8 | let collectionRef = account.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 9 | 10 | let nft = collectionRef.borrowMoment(id: id)! 11 | 12 | // Get the Top Shot specific metadata for this NFT 13 | let view = nft.resolveView(Type())! 14 | 15 | let metadata = view as! TopShot.TopShotMomentMetadataView 16 | 17 | return metadata 18 | } -------------------------------------------------------------------------------- /transactions/user/unlock_moment.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import NonFungibleToken from 0xNFTADDRESS 3 | 4 | // This transaction unlocks a TopShot NFT removing it from the locked dictionary 5 | // and re-enabling the ability to withdraw, sell, and transfer the moment 6 | 7 | // Parameters 8 | // 9 | // id: the Flow ID of the TopShot moment 10 | transaction(id: UInt64) { 11 | prepare(acct: auth(BorrowValue) &Account) { 12 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 13 | ?? panic("Could not borrow from MomentCollection in storage") 14 | 15 | collectionRef.unlock(id: id) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "evm-bridging/lib/forge-std"] 2 | path = evm-bridging/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "evm-bridging/lib/openzeppelin-contracts"] 5 | path = evm-bridging/lib/openzeppelin-contracts 6 | url = https://github.com/openzeppelin/openzeppelin-contracts 7 | [submodule "evm-bridging/lib/openzeppelin-contracts-upgradeable"] 8 | path = evm-bridging/lib/openzeppelin-contracts-upgradeable 9 | url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable 10 | [submodule "evm-bridging/lib/openzeppelin-foundry-upgrades"] 11 | path = evm-bridging/lib/openzeppelin-foundry-upgrades 12 | url = https://github.com/openzeppelin/openzeppelin-foundry-upgrades 13 | -------------------------------------------------------------------------------- /transactions/fastbreak/player/update_submission.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | import NonFungibleToken from 0xNFTADDRESS 3 | 4 | transaction(fastBreakGameID: String, topShotMomentIds: [UInt64]) { 5 | 6 | let gameRef: auth(FastBreakV1.Update) &FastBreakV1.Player 7 | 8 | prepare(acct: auth(BorrowValue) &Account) { 9 | 10 | self.gameRef = acct.storage 11 | .borrow(from: FastBreakV1.PlayerStoragePath) 12 | ?? panic("could not borrow a reference to the accounts player") 13 | 14 | } 15 | 16 | execute { 17 | self.gameRef.updateSubmission(fastBreakGameID: fastBreakGameID, topShots: topShotMomentIds) 18 | } 19 | } -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/update_fast_break_game.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | transaction(id: String, status: UInt8, winner: UInt64) { 4 | 5 | let oracleRef: auth(FastBreakV1.Update) &FastBreakV1.FastBreakDaemon 6 | 7 | prepare(acct: auth(Storage, Capabilities) &Account) { 8 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 9 | ?? panic("could not borrow a reference to the oracle resource") 10 | } 11 | 12 | execute { 13 | 14 | self.oracleRef.updateFastBreakGame( 15 | id: id, 16 | status: status, 17 | winner: winner 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /transactions/scripts/plays/get_play_metadata_field.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns the value for the specified metadata field 4 | // associated with a play in the TopShot smart contract 5 | 6 | // Parameters: 7 | // 8 | // playID: The unique ID for the play whose data needs to be read 9 | // field: The specified metadata field whose data needs to be read 10 | 11 | // Returns: String 12 | // Value of specified metadata field associated with specified playID 13 | 14 | access(all) fun main(playID: UInt32, field: String): String { 15 | 16 | let field = TopShot.getPlayMetaDataByField(playID: playID, field: field) ?? panic("Play doesn't exist") 17 | 18 | log(field) 19 | 20 | return field 21 | } -------------------------------------------------------------------------------- /transactions/scripts/sets/get_numMoments_in_edition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns the number of specified moments that have been 4 | // minted for the specified edition 5 | 6 | // Parameters: 7 | // 8 | // setID: The unique ID for the set whose data needs to be read 9 | // playID: The unique ID for the play whose data needs to be read 10 | 11 | // Returns: UInt32 12 | // number of moments with specified playID minted for a set with specified setID 13 | 14 | access(all) fun main(setID: UInt32, playID: UInt32): UInt32 { 15 | 16 | let numMoments = TopShot.getNumMomentsInEdition(setID: setID, playID: playID) 17 | ?? panic("Could not find the specified edition") 18 | 19 | return numMoments 20 | } -------------------------------------------------------------------------------- /lib/go/templates/fastbreak_user_templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "github.com/dapperlabs/nba-smart-contracts/lib/go/templates/internal/assets" 5 | ) 6 | 7 | const ( 8 | fastBreakSetupGameFilename = "fastbreak/player/create_player.cdc" 9 | fastBreakPlayFilename = "fastbreak/player/play.cdc" 10 | ) 11 | 12 | func GenerateFastBreakCreateAccountScript(env Environment) []byte { 13 | code := assets.MustAssetString(transactionsPath + fastBreakSetupGameFilename) 14 | 15 | return []byte(replaceAddresses(code, env)) 16 | } 17 | 18 | func GeneratePlayFastBreakScript(env Environment) []byte { 19 | code := assets.MustAssetString(transactionsPath + fastBreakPlayFilename) 20 | 21 | return []byte(replaceAddresses(code, env)) 22 | } 23 | -------------------------------------------------------------------------------- /transactions/admin/transfer_admin.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopshotAdminReceiver from 0xADMINRECEIVERADDRESS 3 | 4 | // this transaction takes a TopShot Admin resource and 5 | // saves it to the account storage of the account 6 | // where the contract is deployed 7 | 8 | transaction { 9 | 10 | // Local variable for the topshot Admin object 11 | let adminRef: @TopShot.Admin 12 | 13 | prepare(acct: auth(LoadValue) &Account) { 14 | 15 | self.adminRef <- acct.storage.load<@TopShot.Admin>(from: /storage/TopShotAdmin) 16 | ?? panic("No topshot admin in storage") 17 | } 18 | 19 | execute { 20 | 21 | TopshotAdminReceiver.storeAdmin(newAdmin: <-self.adminRef) 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/update_submission_deadline.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0x0b2a3299cc857e29 2 | 3 | transaction(fastBreakGameID: String, submissionDeadline: UInt64) { 4 | 5 | let oracleRef: auth(FastBreakV1.Update) &FastBreakV1.FastBreakDaemon 6 | 7 | prepare(acct: auth(Storage, Capabilities) &Account) { 8 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 9 | ?? panic("Could not borrow a reference to the oracle resource") 10 | } 11 | 12 | execute { 13 | 14 | self.oracleRef.setSubmissionDeadline( 15 | fastBreakGameID: fastBreakGameID, 16 | deadline: submissionDeadline 17 | ) 18 | } 19 | } -------------------------------------------------------------------------------- /transactions/admin/create_new_subedition_admin_resource.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is for the admin to create a new subedition admin resource 4 | // and store it in the top shot smart contract 5 | 6 | transaction() { 7 | 8 | // Local variable for the topshot Admin object 9 | let adminRef: &TopShot.Admin 10 | 11 | prepare(acct: auth(BorrowValue) &Account) { 12 | 13 | // borrow a reference to the Admin resource in storage 14 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 15 | ?? panic("Could not borrow a reference to the Admin resource") 16 | } 17 | 18 | execute { 19 | self.adminRef.createSubeditionAdminResource() 20 | } 21 | } -------------------------------------------------------------------------------- /transactions/market/scripts/get_sale_percentage.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | 3 | // This script gets the percentage cut that beneficiary will take 4 | // of moments in an account's sale collection 5 | 6 | // Parameters: 7 | // 8 | // sellerAddress: The Flow Address of the account whose sale collection needs to be read 9 | 10 | // Returns: UFix64 11 | // The percentage cut of an account's sale collection 12 | 13 | access(all) fun main(sellerAddress: Address): UFix64 { 14 | 15 | let acct = getAccount(sellerAddress) 16 | 17 | let collectionRef = acct.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 18 | ?? panic("Could not borrow capability from public collection") 19 | 20 | return collectionRef.cutPercentage 21 | } -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/ITransferValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | interface ITransferValidator721 { 5 | /// @notice Ensure that a transfer has been authorized for a specific tokenId 6 | function validateTransfer( 7 | address caller, 8 | address from, 9 | address to, 10 | uint256 tokenId 11 | ) external view; 12 | } 13 | 14 | interface ITransferValidator1155 { 15 | /// @notice Ensure that a transfer has been authorized for a specific amount of a specific tokenId, and reduce the transferable amount remaining 16 | function validateTransfer( 17 | address caller, 18 | address from, 19 | address to, 20 | uint256 tokenId, 21 | uint256 amount 22 | ) external; 23 | } 24 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_isLocked.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 3 | 4 | // This script determines if a moment is locked 5 | 6 | // Parameters: 7 | // 8 | // account: The Flow Address of the account who owns the moment 9 | // id: The unique ID for the moment 10 | 11 | // Returns: Bool 12 | // Whether the moment is locked 13 | 14 | access(all) fun main(account: Address, id: UInt64): Bool { 15 | 16 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 17 | ?? panic("Could not get public moment collection reference") 18 | 19 | let nftRef = collectionRef.borrowNFT(id)! 20 | 21 | return TopShotLocking.isLocked(nftRef: nftRef) 22 | } 23 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_id_in_Collection.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script returns true if a moment with the specified ID 4 | // exists in a user's collection 5 | 6 | // Parameters: 7 | // 8 | // account: The Flow Address of the account whose moment data needs to be read 9 | // id: The unique ID for the moment whose data needs to be read 10 | 11 | // Returns: Bool 12 | // Whether a moment with specified ID exists in user's collection 13 | 14 | access(all) fun main(account: Address, id: UInt64): Bool { 15 | 16 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 17 | ?? panic("Could not get public moment collection reference") 18 | 19 | return collectionRef.borrowNFT(id) != nil 20 | } -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/add_stat_to_game.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | transaction(fastBreakGameID: String, name: String, rawType: UInt8, valueNeeded: UInt64) { 4 | 5 | let oracleRef: auth(FastBreakV1.Update) &FastBreakV1.FastBreakDaemon 6 | 7 | prepare(acct: auth(Storage, Capabilities) &Account) { 8 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 9 | ?? panic("Could not borrow a reference to the oracle resource") 10 | } 11 | 12 | execute { 13 | 14 | self.oracleRef.addStatToFastBreakGame( 15 | fastBreakGameID: fastBreakGameID, 16 | name: name, 17 | rawType: rawType, 18 | valueNeeded: valueNeeded 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/ICreatorToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | interface ICreatorToken { 5 | event TransferValidatorUpdated(address oldValidator, address newValidator); 6 | 7 | function getTransferValidator() external view returns (address validator); 8 | 9 | function getTransferValidationFunction() 10 | external 11 | view 12 | returns (bytes4 functionSignature, bool isViewFunction); 13 | 14 | function setTransferValidator(address validator) external; 15 | } 16 | 17 | interface ILegacyCreatorToken { 18 | event TransferValidatorUpdated(address oldValidator, address newValidator); 19 | 20 | function getTransferValidator() external view returns (address validator); 21 | 22 | function setTransferValidator(address validator) external; 23 | } 24 | -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/ICrossVMBridgeCallable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity 0.8.24; 3 | 4 | /** 5 | * @title ICrossVMBridgeCallable 6 | * @dev An interface intended for use by implementations on Flow EVM, allowing a contract to define 7 | * access to the Cadence X EVM bridge on certain methods. 8 | */ 9 | interface ICrossVMBridgeCallable { 10 | 11 | /// @dev Should encounter when the vmBridgeAddress is initialized to 0x0 12 | error CrossVMBridgeCallableZeroInitialization(); 13 | /// @dev Should encounter when a VM bridge privileged method is triggered by unauthorized caller 14 | error CrossVMBridgeCallableUnauthorizedAccount(address account); 15 | 16 | /** 17 | * @dev Returns the designated VM bridge’s EVM address 18 | */ 19 | function vmBridgeAddress() external view returns (address); 20 | } -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/create_run.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | 4 | transaction(id: String, name: String, runStart: UInt64, runEnd: UInt64, fatigueModeOn: Bool) { 5 | 6 | let oracleRef: auth(FastBreakV1.Create) &FastBreakV1.FastBreakDaemon 7 | 8 | prepare(acct: auth(Storage, Capabilities) &Account) { 9 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 10 | ?? panic("Could not borrow a reference to the oracle resource") 11 | } 12 | 13 | execute { 14 | self.oracleRef.createFastBreakRun(id: id, name: name, runStart: runStart, runEnd: runEnd, fatigueModeOn: fatigueModeOn) 15 | } 16 | 17 | post { 18 | FastBreakV1.getFastBreakRun(id: id)?.name! == name: "could not find fast break run" 19 | } 20 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_collection_ids.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This is the script to get a list of all the moments' ids an account owns 4 | // Just change the argument to `getAccount` to whatever account you want 5 | // and as long as they have a published Collection receiver, you can see 6 | // the moments they own. 7 | 8 | // Parameters: 9 | // 10 | // account: The Flow Address of the account whose moment data needs to be read 11 | 12 | // Returns: [UInt64] 13 | // list of all moments' ids an account owns 14 | 15 | access(all) fun main(account: Address): [UInt64] { 16 | 17 | let acct = getAccount(account) 18 | 19 | let collectionRef = acct.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 20 | 21 | log(collectionRef.getIDs()) 22 | 23 | return collectionRef.getIDs() 24 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_lockExpiry.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 3 | 4 | // This script gets the time at which a moment will be eligible for unlocking 5 | 6 | // Parameters: 7 | // 8 | // account: The Flow Address of the account who owns the moment 9 | // id: The unique ID for the moment 10 | 11 | // Returns: UFix64 12 | // The unix timestamp when the moment is unlockable 13 | 14 | access(all) fun main(account: Address, id: UInt64): UFix64 { 15 | 16 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 17 | ?? panic("Could not get public moment collection reference") 18 | 19 | let nftRef = collectionRef.borrowNFT(id)! 20 | 21 | return TopShotLocking.getLockExpiry(nftRef: nftRef) 22 | } 23 | -------------------------------------------------------------------------------- /transactions/market/scripts/get_sale_price.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | 3 | // This script gets the price of a moment in an account's sale collection 4 | // by looking up its unique ID. 5 | 6 | // Parameters: 7 | // 8 | // sellerAddress: The Flow Address of the account whose sale collection needs to be read 9 | // momentID: The unique ID for the moment whose data needs to be read 10 | 11 | // Returns: UFix64 12 | // The price of moment with specified ID on sale 13 | 14 | access(all) fun main(sellerAddress: Address, momentID: UInt64): UFix64 { 15 | 16 | let acct = getAccount(sellerAddress) 17 | 18 | let collectionRef = acct.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 19 | ?? panic("Could not borrow capability from public collection") 20 | 21 | return collectionRef.getPrice(tokenID: UInt64(momentID))! 22 | } -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 6 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /transactions/marketV3/change_price.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | // This transaction changes the price of a moment that a user has for sale 5 | 6 | // Parameters: 7 | // 8 | // tokenID: the ID of the moment whose price is being changed 9 | // newPrice: the new price of the moment 10 | 11 | transaction(tokenID: UInt64, newPrice: UFix64) { 12 | prepare(acct: auth(Storage, Capabilities) &Account) { 13 | 14 | // borrow a reference to the owner's sale collection 15 | let topshotSaleCollection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) 16 | ?? panic("Could not borrow from sale in storage") 17 | 18 | // Change the price of the moment 19 | topshotSaleCollection.listForSale(tokenID: tokenID, price: newPrice) 20 | } 21 | } -------------------------------------------------------------------------------- /transactions/admin/set_nft_subedition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction links nft to subedititon 4 | 5 | // Parameters: 6 | // 7 | // nftID: the unique ID of nft 8 | // subeditionID: the unique ID of subedition 9 | 10 | transaction(nftID: UInt64, subeditionID: UInt32, setID: UInt32, playID: UInt32) { 11 | 12 | // Local variable for the topshot Admin object 13 | let adminRef: &TopShot.Admin 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | // borrow a reference to the admin resource 17 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 18 | ?? panic("No admin resource in storage") 19 | } 20 | 21 | execute { 22 | // Create a subedition with the specified metadata 23 | self.adminRef.setMomentsSubedition(nftID: nftID, subeditionID: subeditionID, setID: setID, playID: playID) 24 | } 25 | } -------------------------------------------------------------------------------- /transactions/admin/add_plays_to_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction adds multiple plays to a set 4 | 5 | // Parameters: 6 | // 7 | // setID: the ID of the set to which multiple plays are added 8 | // plays: an array of play IDs being added to the set 9 | 10 | transaction(setID: UInt32, plays: [UInt32]) { 11 | 12 | // Local variable for the topshot Admin object 13 | let adminRef: &TopShot.Admin 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // borrow a reference to the Admin resource in storage 18 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)! 19 | } 20 | 21 | execute { 22 | 23 | // borrow a reference to the set to be added to 24 | let setRef = self.adminRef.borrowSet(setID: setID) 25 | 26 | // Add the specified play IDs 27 | setRef.addPlays(playIDs: plays) 28 | } 29 | } -------------------------------------------------------------------------------- /transactions/admin/retireAll_plays_from_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is for retiring all plays from a set, which 4 | // makes it so that moments can no longer be minted 5 | // from all the editions with that set 6 | 7 | // Parameters: 8 | // 9 | // setID: the ID of the set to be retired entirely 10 | 11 | transaction(setID: UInt32) { 12 | let adminRef: &TopShot.Admin 13 | 14 | prepare(acct: auth(BorrowValue) &Account) { 15 | 16 | // borrow a reference to the Admin resource in storage 17 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 18 | ?? panic("No admin resource in storage") 19 | } 20 | 21 | execute { 22 | // borrow a reference to the specified set 23 | let setRef = self.adminRef.borrowSet(setID: setID) 24 | 25 | // retire all the plays 26 | setRef.retireAll() 27 | } 28 | } -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/create_game.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | transaction( 4 | id: String, 5 | name: String, 6 | fastBreakRunID: String, 7 | submissionDeadline: UInt64, 8 | numPlayers: UInt64 9 | ) { 10 | 11 | let oracleRef: auth(FastBreakV1.Create) &FastBreakV1.FastBreakDaemon 12 | 13 | prepare(acct: auth(Storage, Capabilities) &Account) { 14 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 15 | ?? panic("Could not borrow a reference to the oracle resource") 16 | } 17 | 18 | execute { 19 | 20 | self.oracleRef.createFastBreakGame( 21 | id: id, 22 | name: name, 23 | fastBreakRunID: fastBreakRunID, 24 | submissionDeadline: submissionDeadline, 25 | numPlayers: numPlayers 26 | ) 27 | } 28 | } -------------------------------------------------------------------------------- /transactions/market/scripts/get_sale_set_id.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | 3 | // This script gets the setID of a moment in an account's sale collection 4 | // by looking up its unique ID 5 | 6 | // Parameters: 7 | // 8 | // sellerAddress: The Flow Address of the account whose sale collection needs to be read 9 | // momentID: The unique ID for the moment whose data needs to be read 10 | 11 | // Returns: UInt32 12 | // The setID of moment with specified ID 13 | 14 | access(all) fun main(sellerAddress: Address, momentID: UInt64): UInt32 { 15 | 16 | let saleRef = getAccount(sellerAddress).capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 17 | ?? panic("Could not get public sale reference") 18 | 19 | let token = saleRef.borrowMoment(id: momentID) 20 | ?? panic("Could not borrow a reference to the specified moment") 21 | 22 | let data = token.data 23 | 24 | return data.setID 25 | } -------------------------------------------------------------------------------- /transactions/admin/start_new_series.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is for an Admin to start a new Top Shot series 4 | 5 | transaction { 6 | 7 | // Local variable for the topshot Admin object 8 | let adminRef: &TopShot.Admin 9 | let currentSeries: UInt32 10 | 11 | prepare(acct: auth(BorrowValue) &Account) { 12 | 13 | // borrow a reference to the Admin resource in storage 14 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 15 | ?? panic("No admin resource in storage") 16 | 17 | self.currentSeries = TopShot.currentSeries 18 | } 19 | 20 | execute { 21 | 22 | // Increment the series number 23 | self.adminRef.startNewSeries() 24 | } 25 | 26 | post { 27 | 28 | TopShot.currentSeries == self.currentSeries + 1 as UInt32: 29 | "new series not started" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/go/events/set_locked.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventSetLocked = "TopShot.SetLocked" 10 | ) 11 | 12 | type SetLockedEvent interface { 13 | SetID() uint32 14 | } 15 | 16 | type setLockedEvent map[string]any 17 | 18 | var _ SetLockedEvent = (*setLockedEvent)(nil) 19 | 20 | func (evt setLockedEvent) SetID() uint32 { 21 | return evt["setID"].(uint32) 22 | } 23 | 24 | func DecodeSetLockedEvent(b []byte) (SetLockedEvent, error) { 25 | cadenceValue, err := decoder.GetCadenceEvent(b) 26 | if err != nil { 27 | return nil, err 28 | } 29 | if cadenceValue.EventType.QualifiedIdentifier != EventSetLocked { 30 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 31 | } 32 | eventMap, err := decoder.ConvertEvent(cadenceValue) 33 | event := setLockedEvent(eventMap) 34 | return event, nil 35 | } 36 | -------------------------------------------------------------------------------- /transactions/fastbreak/oracle/score_fast_break_submission.cdc: -------------------------------------------------------------------------------- 1 | import FastBreakV1 from 0xFASTBREAKADDRESS 2 | 3 | transaction(fastBreakGameID: String, playerAddress: Address, points: UInt64, win: Bool) { 4 | 5 | let oracleRef: auth(FastBreakV1.Update) &FastBreakV1.FastBreakDaemon 6 | let playerId: UInt64 7 | 8 | prepare(acct: auth(Storage, Capabilities) &Account) { 9 | self.oracleRef = acct.storage.borrow(from: FastBreakV1.OracleStoragePath) 10 | ?? panic("could not borrow a reference to the oracle resource") 11 | 12 | self.playerId = FastBreakV1.getPlayerIdByAccount(accountAddress: playerAddress) 13 | } 14 | 15 | execute { 16 | 17 | self.oracleRef.updateFastBreakScore( 18 | fastBreakGameID: fastBreakGameID, 19 | playerId: self.playerId, 20 | points: points, 21 | win: win 22 | ) 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /transactions/admin/lock_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction locks a set so that new plays can no longer be added to it 4 | 5 | // Parameters: 6 | // 7 | // setID: the ID of the set to be locked 8 | 9 | transaction(setID: UInt32) { 10 | 11 | // local variable for the admin resource 12 | let adminRef: &TopShot.Admin 13 | 14 | prepare(acct: auth(BorrowValue) &Account) { 15 | // borrow a reference to the admin resource 16 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 17 | ?? panic("No admin resource in storage") 18 | } 19 | 20 | execute { 21 | // borrow a reference to the Set 22 | let setRef = self.adminRef.borrowSet(setID: setID) 23 | 24 | // lock the set permanently 25 | setRef.lock() 26 | } 27 | 28 | post { 29 | 30 | TopShot.isSetLocked(setID: setID)!: 31 | "Set did not lock" 32 | } 33 | } -------------------------------------------------------------------------------- /transactions/admin/retire_all.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This is a transaction an admin would use to retire all the plays in a set 4 | // which makes it so that no more moments can be minted from the retired plays 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of the set to be retired entirely 9 | 10 | transaction(setID: UInt32) { 11 | 12 | // local variable for the admin reference 13 | let adminRef: &TopShot.Admin 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // borrow a reference to the admin resource 18 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 19 | ?? panic("No admin resource in storage") 20 | } 21 | 22 | execute { 23 | 24 | // borrow a reference to the specified set 25 | let setRef = self.adminRef.borrowSet(setID: setID) 26 | 27 | // retire all the plays permenantely 28 | setRef.retireAll() 29 | } 30 | } -------------------------------------------------------------------------------- /transactions/marketV3/start_sale.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | 4 | // This transaction is for a user to put a new moment up for sale 5 | // They must have TopShot Collection and a TopShotMarketV2 Sale Collection already 6 | // stored in their account 7 | 8 | // Parameters 9 | // 10 | // momentId: the ID of the moment to be listed for sale 11 | // price: the sell price of the moment 12 | 13 | transaction(momentID: UInt64, price: UFix64) { 14 | prepare(acct: auth(BorrowValue) &Account) { 15 | 16 | // borrow a reference to the topshot Sale Collection 17 | let topshotSaleCollection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) 18 | ?? panic("Could not borrow from sale in storage") 19 | 20 | // List the specified moment for sale 21 | topshotSaleCollection.listForSale(tokenID: momentID, price: price) 22 | } 23 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_serialNum.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the serial number of a moment 4 | // by borrowing a reference to the moment 5 | // and returning its serial number 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: UInt32 13 | // The serialNumber associated with a moment with a specified ID 14 | 15 | access(all) fun main(account: Address, id: UInt64): UInt32 { 16 | 17 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 18 | ?? panic("Could not get public moment collection reference") 19 | 20 | let token = collectionRef.borrowMoment(id: id) 21 | ?? panic("Could not borrow a reference to the specified moment") 22 | 23 | let data = token.data 24 | 25 | return data.serialNumber 26 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_playID.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the playID associated with a moment 4 | // in a collection by getting a reference to the moment 5 | // and then looking up its playID 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: UInt32 13 | // The playID associated with a moment with a specified ID 14 | 15 | access(all) fun main(account: Address, id: UInt64): UInt32 { 16 | 17 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 18 | ?? panic("Could not get public moment collection reference") 19 | 20 | let token = collectionRef.borrowMoment(id: id) 21 | ?? panic("Could not borrow a reference to the specified moment") 22 | 23 | let data = token.data 24 | 25 | return data.playID 26 | } -------------------------------------------------------------------------------- /lib/go/events/moment_unlocked.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | MomentUnlocked = "TopShotLocking.MomentUnlocked" 10 | ) 11 | 12 | type MomentUnlockedEvent interface { 13 | FlowID() uint64 14 | } 15 | 16 | type momentUnlockedEvent map[string]any 17 | 18 | func (evt momentUnlockedEvent) FlowID() uint64 { 19 | return evt["id"].(uint64) 20 | } 21 | 22 | var _ MomentUnlockedEvent = (*momentUnlockedEvent)(nil) 23 | 24 | func DecodeMomentUnlockedEvent(b []byte) (MomentUnlockedEvent, error) { 25 | cadenceValue, err := decoder.GetCadenceEvent(b) 26 | if err != nil { 27 | return nil, err 28 | } 29 | if cadenceValue.EventType.QualifiedIdentifier != MomentUnlocked { 30 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 31 | } 32 | eventMap, err := decoder.ConvertEvent(cadenceValue) 33 | 34 | event := momentUnlockedEvent(eventMap) 35 | return event, nil 36 | } 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_series.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the series associated with a moment 4 | // in a collection by getting a reference to the moment 5 | // and then looking up its series 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: UInt32 13 | // The series associated with a moment with a specified ID 14 | 15 | access(all) fun main(account: Address, id: UInt64): UInt32 { 16 | 17 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 18 | ?? panic("Could not get public moment collection reference") 19 | 20 | let token = collectionRef.borrowMoment(id: id) 21 | ?? panic("Could not borrow a reference to the specified moment") 22 | 23 | let data = token.data 24 | 25 | return TopShot.getSetSeries(setID: data.setID)! 26 | } -------------------------------------------------------------------------------- /transactions/user/lock_moment.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | // This transaction locks a TopShot NFT rendering it unable to be withdrawn, sold, or transferred 6 | 7 | // Parameters 8 | // 9 | // id: the Flow ID of the TopShot moment 10 | // duration: number of seconds that the moment will be locked for 11 | 12 | transaction(id: UInt64, duration: UFix64) { 13 | prepare(acct: auth(BorrowValue) &Account) { 14 | if let saleRef = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 15 | saleRef.cancelSale(tokenID: id) 16 | } 17 | 18 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 19 | ?? panic("Could not borrow from MomentCollection in storage") 20 | 21 | collectionRef.lock(id: id, duration: duration) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/admin/deploy/deploy_contract.cdc: -------------------------------------------------------------------------------- 1 | import "FungibleToken" 2 | import "FlowToken" 3 | import "EVM" 4 | 5 | /// Deploys a compiled solidity contract from bytecode to the EVM, with the signer's COA as the deployer 6 | /// 7 | transaction(bytecode: String, gasLimit: UInt64) { 8 | let coa: auth(EVM.Deploy) &EVM.CadenceOwnedAccount 9 | 10 | prepare(signer: auth(BorrowValue) &Account) { 11 | self.coa = signer.storage.borrow(from: /storage/evm) 12 | ?? panic("Could not borrow reference to the signer's bridged account") 13 | } 14 | 15 | execute { 16 | let result = self.coa.deploy( 17 | code: bytecode.decodeHex(), 18 | gasLimit: gasLimit, 19 | value: EVM.Balance(attoflow: 0) 20 | ) 21 | assert(result.status == EVM.Status.successful && result.deployedContract != nil, 22 | message: "EVM deployment failed with error code: ".concat(result.errorCode.toString()) 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /transactions/user/lock_fake_nft.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | // This transaction attempts to send an NFT that is impersonating a TopShot NFT 6 | // to the locking contract, it must fail 7 | 8 | // Parameters 9 | // 10 | // id: the Flow ID of the TopShot moment 11 | // duration: number of seconds that the moment will be locked for 12 | 13 | transaction(id: UInt64, duration: UFix64) { 14 | prepare(acct: auth(BorrowValue) &Account) { 15 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 16 | ?? panic("Could not borrow from MomentCollection in storage") 17 | 18 | let nft <- collectionRef.withdraw(withdrawID: id) 19 | 20 | let lockedNFT <- TopShotLocking.lockNFT(nft: <-nft, duration: duration) 21 | 22 | // destroy here to get rid of loss of resource error - should not actually get here 23 | destroy <- lockedNFT 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/go/events/set.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventSetCreated = "TopShot.SetCreated" 10 | ) 11 | 12 | type SetCreatedEvent interface { 13 | SetID() uint32 14 | Series() uint32 15 | } 16 | 17 | type setCreatedEvent map[string]any 18 | 19 | func (evt setCreatedEvent) SetID() uint32 { 20 | return evt["setID"].(uint32) 21 | } 22 | 23 | func (evt setCreatedEvent) Series() uint32 { 24 | return evt["series"].(uint32) 25 | } 26 | 27 | var _ SetCreatedEvent = (*setCreatedEvent)(nil) 28 | 29 | func DecodeSetCreatedEvent(b []byte) (SetCreatedEvent, error) { 30 | cadenceValue, err := decoder.GetCadenceEvent(b) 31 | if err != nil { 32 | return nil, err 33 | } 34 | if cadenceValue.EventType.QualifiedIdentifier != EventSetCreated { 35 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 36 | } 37 | eventMap, err := decoder.ConvertEvent(cadenceValue) 38 | event := setCreatedEvent(eventMap) 39 | return event, nil 40 | } 41 | -------------------------------------------------------------------------------- /lib/go/events/moment_destroy.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventMomentDestroyed = "TopShot.MomentDestroyed" 10 | EventMomentDestroyedV2 = "TopShot.NFT.ResourceDestroyed" 11 | ) 12 | 13 | type MomentDestroyedEvent interface { 14 | Id() uint64 15 | } 16 | 17 | type momentDestroyedEvent map[string]any 18 | 19 | func (evt momentDestroyedEvent) Id() uint64 { 20 | return evt["id"].(uint64) 21 | } 22 | 23 | func DecodeMomentDestroyedEvent(b []byte) (MomentDestroyedEvent, error) { 24 | cadenceValue, err := decoder.GetCadenceEvent(b) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if cadenceValue.EventType.QualifiedIdentifier != EventMomentDestroyed && cadenceValue.EventType.QualifiedIdentifier != EventMomentDestroyedV2 { 29 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 30 | } 31 | eventMap, err := decoder.ConvertEvent(cadenceValue) 32 | event := momentDestroyedEvent(eventMap) 33 | return event, nil 34 | } 35 | -------------------------------------------------------------------------------- /transactions/admin/mark_moment_unlockable.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotLocking from 0xTOPSHOTLOCKINGADDRESS 3 | 4 | transaction(ownerAddress: Address, id: UInt64) { 5 | let adminRef: &TopShotLocking.Admin 6 | 7 | prepare(acct: auth(BorrowValue) &Account) { 8 | // Set TopShotLocking admin ref 9 | self.adminRef = acct.storage.borrow<&TopShotLocking.Admin>(from: /storage/TopShotLockingAdmin) 10 | ?? panic("Could not find reference to TopShotLocking Admin resource") 11 | } 12 | 13 | execute { 14 | // Set Top Shot NFT Owner collection ref 15 | let owner = getAccount(ownerAddress) 16 | 17 | let collectionRef = owner.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 18 | ?? panic("Could not reference owner's moment collection") 19 | 20 | // borrow the nft reference 21 | let nftRef = collectionRef.borrowNFT(id)! 22 | 23 | // mark the nft as unlockable 24 | self.adminRef.markNFTUnlockable(nftRef: nftRef) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/go/events/play.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventPlayCreated = "TopShot.PlayCreated" 10 | ) 11 | 12 | type PlayCreatedEvent interface { 13 | Id() uint32 14 | MetaData() map[interface{}]interface{} 15 | } 16 | 17 | type playCreatedEvent map[string]any 18 | 19 | func (evt playCreatedEvent) Id() uint32 { 20 | return evt["id"].(uint32) 21 | } 22 | 23 | func (evt playCreatedEvent) MetaData() map[interface{}]interface{} { 24 | return evt["metadata"].(map[interface{}]interface{}) 25 | } 26 | 27 | func DecodePlayCreatedEvent(b []byte) (PlayCreatedEvent, error) { 28 | cadenceValue, err := decoder.GetCadenceEvent(b) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if cadenceValue.EventType.QualifiedIdentifier != EventPlayCreated { 33 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 34 | } 35 | eventMap, err := decoder.ConvertEvent(cadenceValue) 36 | event := playCreatedEvent(eventMap) 37 | return event, nil 38 | } 39 | -------------------------------------------------------------------------------- /transactions/market/change_price.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import Market from 0xMARKETADDRESS 3 | 4 | // This transaction changes the price of a moment that a user has for sale 5 | 6 | // Parameters: 7 | // 8 | // tokenID: the ID of the moment whose price is being changed 9 | // newPrice: the new price of the moment 10 | 11 | transaction(tokenID: UInt64, newPrice: UFix64) { 12 | 13 | // Local variable for the account's topshot sale collection 14 | let topshotSaleCollectionRef: auth(Market.Update) &Market.SaleCollection 15 | 16 | prepare(acct: auth(Storage, Capabilities) &Account) { 17 | 18 | // borrow a reference to the owner's sale collection 19 | self.topshotSaleCollectionRef = acct.storage.borrow(from: /storage/topshotSaleCollection) 20 | ?? panic("Could not borrow from sale in storage") 21 | } 22 | 23 | execute { 24 | 25 | // Change the price of the moment 26 | self.topshotSaleCollectionRef.changePrice(tokenID: tokenID, newPrice: newPrice) 27 | } 28 | 29 | 30 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/borrow_nft_safe.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This is a script to get a boolean value safely to see if a moment exists in a collection 4 | // We expect this will not panic if the NFT is not in the collection 5 | // Change the `account` to whatever account you want 6 | // and as long as they have a published Collection receiver, you can 7 | // get reference to the NFTs they own. 8 | 9 | // Parameters: 10 | // 11 | // account: The Flow Address of the account whose moment data needs to be read 12 | // nftID: The ID of the NFT to return 13 | 14 | // Returns: Boolean value indicating if the NFT is in the collection 15 | 16 | access(all) fun main(account: Address, nftID: UInt64 ): Bool { 17 | 18 | let acct = getAccount(account) 19 | 20 | let collectionRef = acct.capabilities.borrow<&TopShot.Collection>(/public/MomentCollection)! 21 | 22 | let optionalNFT = collectionRef.borrowNFT(nftID) 23 | 24 | // optional binding 25 | if let nft = optionalNFT { 26 | return true 27 | } else { 28 | return false 29 | } 30 | } -------------------------------------------------------------------------------- /transactions/user/batch_lock_moments.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | // This transaction locks a list of TopShot NFTs rendering them unable to be withdrawn, sold, or transferred 6 | 7 | // Parameters 8 | // 9 | // ids: array of TopShot moment Flow IDs 10 | // duration: number of seconds that the moment will be locked for 11 | 12 | transaction(ids: [UInt64], duration: UFix64) { 13 | prepare(acct: auth(BorrowValue) &Account) { 14 | if let saleRef = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 15 | for id in ids { 16 | saleRef.cancelSale(tokenID: id) 17 | } 18 | } 19 | 20 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 21 | ?? panic("Could not borrow from MomentCollection in storage") 22 | 23 | collectionRef.batchLock(ids: ids, duration: duration) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /transactions/admin/create_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is for the admin to create a new set resource 4 | // and store it in the top shot smart contract 5 | 6 | // Parameters: 7 | // 8 | // setName: the name of a new Set to be created 9 | 10 | transaction(setName: String) { 11 | 12 | // Local variable for the topshot Admin object 13 | let adminRef: &TopShot.Admin 14 | let currSetID: UInt32 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | // borrow a reference to the Admin resource in storage 19 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 20 | ?? panic("Could not borrow a reference to the Admin resource") 21 | self.currSetID = TopShot.nextSetID; 22 | } 23 | 24 | execute { 25 | 26 | // Create a set with the specified name 27 | self.adminRef.createSet(name: setName) 28 | } 29 | 30 | post { 31 | 32 | TopShot.getSetName(setID: self.currSetID) == setName: 33 | "Could not find the specified set" 34 | } 35 | } -------------------------------------------------------------------------------- /transactions/fastbreak/player/play.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import FastBreakV1 from 0xFASTBREAKADDRESS 3 | 4 | transaction( 5 | fastBreakGameID: String, 6 | topShots: [UInt64] 7 | ) { 8 | 9 | let gameRef: auth(FastBreakV1.Play) &FastBreakV1.Player 10 | let recipient: &{FastBreakV1.FastBreakNFTCollectionPublic} 11 | 12 | prepare(acct: auth(Storage, Capabilities) &Account) { 13 | self.gameRef = acct.storage 14 | .borrow(from: FastBreakV1.PlayerStoragePath) 15 | ?? panic("could not borrow a reference to the accounts player") 16 | 17 | self.recipient = acct.capabilities.borrow<&FastBreakV1.Collection>(FastBreakV1.CollectionPublicPath) 18 | ?? panic("could not borrow a reference to the collection receiver") 19 | 20 | } 21 | 22 | execute { 23 | 24 | let nft <- self.gameRef.play( 25 | fastBreakGameID: fastBreakGameID, 26 | topShots: topShots 27 | ) 28 | self.recipient.deposit(token: <- (nft as @{NonFungibleToken.NFT})) 29 | } 30 | } -------------------------------------------------------------------------------- /transactions/market/change_receiver.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 3 | 4 | // This transaction changes the path which receives tokens for purchases of an account 5 | 6 | // Parameters: 7 | // 8 | // receiverPath: The new fungible token capability for the account who receives tokens for purchases 9 | 10 | transaction(receiverPath: PublicPath) { 11 | 12 | // Local variables for the sale collection object and receiver 13 | let saleCollectionRef: auth(Market.Update) &Market.SaleCollection 14 | let receiverPathRef: Capability<&{FungibleToken.Receiver}> 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | self.saleCollectionRef = acct.storage.borrow(from: /storage/topshotSaleCollection) 19 | ?? panic("Could not borrow from sale in storage") 20 | self.receiverPathRef = acct.capabilities.get<&{FungibleToken.Receiver}>(receiverPath)! 21 | } 22 | 23 | execute { 24 | 25 | self.saleCollectionRef.changeOwnerReceiver(self.receiverPathRef) 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /transactions/scripts/setup_sharded_locker_room.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotShardedCollection from 0xSHARDEDADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | /* 6 | This transaction creates a capability on the minter account 7 | that links it to the locker room account. 8 | 9 | For example, on testnet: 10 | minter account = 0x70dff4d1005824db 11 | locker account = 0xd80d84b4b0a88782 12 | */ 13 | 14 | 15 | transaction() { 16 | 17 | prepare(minter: auth(Storage, Capabilities) &Account, locker: auth(Storage, Capabilities) &Account) { 18 | 19 | minter.storage.save( 20 | locker.capabilities.storage.issue(/storage/TopShotShardedCollection), 21 | to: /storage/lockerTSShardedCollection2 22 | ) 23 | 24 | minter.storage.save( 25 | locker.capabilities.storage.issue(/storage/MomentCollection), 26 | to: /storage/lockerTSCollection2 27 | ) 28 | } 29 | } -------------------------------------------------------------------------------- /lib/go/events/set_play.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventPlayAddedToSet = "TopShot.PlayAddedToSet" 10 | ) 11 | 12 | type PlayAddedToSetEvent interface { 13 | SetID() uint32 14 | PlayID() uint32 15 | } 16 | 17 | type playAddedToSetEvent map[string]any 18 | 19 | func (evt playAddedToSetEvent) SetID() uint32 { 20 | return evt["setID"].(uint32) 21 | } 22 | 23 | func (evt playAddedToSetEvent) PlayID() uint32 { 24 | return evt["playID"].(uint32) 25 | } 26 | 27 | var _ PlayAddedToSetEvent = (*playAddedToSetEvent)(nil) 28 | 29 | func DecodePlayAddedToSetEvent(b []byte) (PlayAddedToSetEvent, error) { 30 | cadenceValue, err := decoder.GetCadenceEvent(b) 31 | if err != nil { 32 | return nil, err 33 | } 34 | if cadenceValue.EventType.QualifiedIdentifier != EventPlayAddedToSet { 35 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 36 | } 37 | eventMap, err := decoder.ConvertEvent(cadenceValue) 38 | event := playAddedToSetEvent(eventMap) 39 | return event, nil 40 | } 41 | -------------------------------------------------------------------------------- /lib/go/events/set_locked_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_SetLocked(t *testing.T) { 14 | setID := uint32(1234) 15 | 16 | setLockedEventType := cadence.NewEventType( 17 | utils.TestLocation, 18 | "TopShot.SetLocked", 19 | []cadence.Field{ 20 | { 21 | Identifier: "setID", 22 | Type: cadence.UInt32Type, 23 | }, 24 | }, 25 | nil, 26 | ) 27 | 28 | setLockedEvent := cadence.NewEvent([]cadence.Value{ 29 | cadence.NewUInt32(setID), 30 | }).WithType(setLockedEventType) 31 | 32 | payload, err := jsoncdc.Encode(setLockedEvent) 33 | require.NoError(t, err, "failed to encode set locked cadence event") 34 | 35 | decodeSetLockPurchasedEvent, err := DecodeSetLockedEvent(payload) 36 | require.NoError(t, err, "failed to decode set locked cadence event") 37 | 38 | assert.Equal(t, setID, decodeSetLockPurchasedEvent.SetID()) 39 | } 40 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_setID.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the setID associated with a moment 4 | // in a collection by getting a reference to the moment 5 | // and then looking up its setID 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: UInt32 13 | // The setID associated with a moment with a specified ID 14 | 15 | access(all) fun main(account: Address, id: UInt64): UInt32 { 16 | 17 | // borrow a public reference to the owner's moment collection 18 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 19 | ?? panic("Could not get public moment collection reference") 20 | 21 | // borrow a reference to the specified moment in the collection 22 | let token = collectionRef.borrowMoment(id: id) 23 | ?? panic("Could not borrow a reference to the specified moment") 24 | 25 | let data = token.data 26 | 27 | return data.setID 28 | } -------------------------------------------------------------------------------- /transactions/admin/add_play_to_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is how a Top Shot admin adds a created play to a set 4 | 5 | // Parameters: 6 | // 7 | // setID: the ID of the set to which a created play is added 8 | // playID: the ID of the play being added 9 | 10 | transaction(setID: UInt32, playID: UInt32) { 11 | 12 | // Local variable for the topshot Admin object 13 | let adminRef: &TopShot.Admin 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // borrow a reference to the Admin resource in storage 18 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 19 | ?? panic("Could not borrow a reference to the Admin resource") 20 | } 21 | 22 | execute { 23 | 24 | // Borrow a reference to the set to be added to 25 | let setRef = self.adminRef.borrowSet(setID: setID) 26 | 27 | // Add the specified play ID 28 | setRef.addPlay(playID: playID) 29 | } 30 | 31 | post { 32 | TopShot.getPlaysInSet(setID: setID)!.contains(playID): 33 | "set does not contain playID" 34 | } 35 | } -------------------------------------------------------------------------------- /lib/go/events/moment_destroy_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_MomentDestroyed(t *testing.T) { 14 | id := uint64(1234) 15 | momentDestroyedEventType := cadence.NewEventType( 16 | utils.TestLocation, 17 | "TopShot.MomentDestroyed", 18 | []cadence.Field{ 19 | { 20 | Identifier: "id", 21 | Type: cadence.UInt64Type, 22 | }, 23 | }, 24 | nil, 25 | ) 26 | 27 | momentDestroyedEvent := cadence.NewEvent([]cadence.Value{ 28 | cadence.NewUInt64(id), 29 | }).WithType(momentDestroyedEventType) 30 | 31 | payload, err := jsoncdc.Encode(momentDestroyedEvent) 32 | require.NoError(t, err, "failed to encode moment destroyed cadence event") 33 | 34 | decodeSetLockDestroyedEvent, err := DecodeMomentDestroyedEvent(payload) 35 | require.NoError(t, err, "failed to decode moment destroyed cadence event") 36 | 37 | assert.Equal(t, id, decodeSetLockDestroyedEvent.Id()) 38 | } 39 | -------------------------------------------------------------------------------- /transactions/market/change_percentage.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | 3 | // This transaction changes the percentage cut of a moment's sale given to beneficiary 4 | 5 | // Parameters: 6 | // 7 | // newPercentage: new percentage of tokens the beneficiary will receive from the sale 8 | 9 | transaction(newPercentage: UFix64) { 10 | 11 | // Local variable for the account's topshot sale collection 12 | let topshotSaleCollectionRef: auth(Market.Update) &Market.SaleCollection 13 | 14 | prepare(acct: auth(Storage, Capabilities) &Account) { 15 | 16 | // borrow a reference to the owner's sale collection 17 | self.topshotSaleCollectionRef = acct.storage.borrow(from: /storage/topshotSaleCollection) 18 | ?? panic("Could not borrow from sale in storage") 19 | } 20 | 21 | execute { 22 | 23 | // Change the percentage of the moment 24 | self.topshotSaleCollectionRef.changePercentage(newPercentage) 25 | } 26 | 27 | post { 28 | 29 | self.topshotSaleCollectionRef.cutPercentage! == newPercentage: 30 | "cutPercentage not changed" 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_moment_setName.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the set name associated with a moment 4 | // in a collection by getting a reference to the moment 5 | // and then looking up its name 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: String 13 | // The set name associated with a moment with a specified ID 14 | 15 | access(all) fun main(account: Address, id: UInt64): String { 16 | 17 | // borrow a public reference to the owner's moment collection 18 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 19 | ?? panic("Could not get public moment collection reference") 20 | 21 | // borrow a reference to the specified moment in the collection 22 | let token = collectionRef.borrowMoment(id: id) 23 | ?? panic("Could not borrow a reference to the specified moment") 24 | 25 | let data = token.data 26 | 27 | return TopShot.getSetName(setID: data.setID)! 28 | } -------------------------------------------------------------------------------- /transactions/fastbreak/player/create_player.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import FastBreakV1 from 0xFASTBREAKADDRESS 3 | 4 | transaction(playerName: String) { 5 | 6 | prepare(signer: auth(Storage, Capabilities) &Account) { 7 | if signer.storage.borrow<&FastBreakV1.Collection>(from: FastBreakV1.CollectionStoragePath) == nil { 8 | 9 | let collection <- FastBreakV1.createEmptyCollection(nftType: Type<@FastBreakV1.NFT>()) 10 | signer.storage.save(<-collection, to: FastBreakV1.CollectionStoragePath) 11 | signer.capabilities.unpublish(FastBreakV1.CollectionPublicPath) 12 | signer.capabilities.publish( 13 | signer.capabilities.storage.issue<&FastBreakV1.Collection>(FastBreakV1.CollectionStoragePath), 14 | at: FastBreakV1.CollectionPublicPath 15 | ) 16 | 17 | } 18 | 19 | if signer.storage.borrow<&FastBreakV1.Player>(from: FastBreakV1.PlayerStoragePath) == nil { 20 | 21 | let player <- FastBreakV1.createPlayer(playerName: playerName) 22 | signer.storage.save(<-player, to: FastBreakV1.PlayerStoragePath) 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /transactions/user/setup_collection.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import MetadataViews from 0xMETADATAVIEWSADDRESS 4 | 5 | // This transaction sets up an account to use Top Shot 6 | // by storing an empty moment collection and creating 7 | // a public capability for it 8 | 9 | transaction { 10 | 11 | prepare(acct: auth(Storage, Capabilities) &Account) { 12 | 13 | // First, check to see if a moment collection already exists 14 | if acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) == nil { 15 | // create a new TopShot Collection 16 | let collection <- TopShot.createEmptyCollection(nftType: Type<@TopShot.NFT>()) as! @TopShot.Collection 17 | // Put the new Collection in storage 18 | acct.storage.save(<-collection, to: /storage/MomentCollection) 19 | } 20 | 21 | acct.capabilities.unpublish(/public/MomentCollection) 22 | acct.capabilities.publish( 23 | acct.capabilities.storage.issue<&TopShot.Collection>(/storage/MomentCollection), 24 | at: /public/MomentCollection 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Requesting a Feature or Improvement 3 | about: "For feature requests. Please search for existing issues first. Also see CONTRIBUTING." 4 | title: '' 5 | labels: Feedback, Feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Instructions 11 | 12 | Please fill out the template below to the best of your ability and include a label indicating which tool/service you were working with when you encountered the problem. 13 | 14 | ### Issue To Be Solved 15 | (Replace This Text: Please present a concise description of the problem to be addressed by this feature request. Please be clear what parts of the problem are considered to be in-scope and out-of-scope.) 16 | 17 | ### (Optional): Suggest A Solution 18 | (Replace This Text: A concise description of your preferred solution. Things to address include: 19 | * Details of the technical implementation 20 | * Tradeoffs made in design decisions 21 | * Caveats and considerations for the future 22 | 23 | If there are multiple solutions, please present each one separately. Save comparisons for the very end.) 24 | 25 | ### (Optional): Context 26 | 27 | -------------------------------------------------------------------------------- /lib/go/events/subedition_added_to_moment.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | var ( 9 | EventSubeditionAddedToMoment = "TopShot.SubeditionAddedToMoment" 10 | ) 11 | 12 | type SubeditionAddedToMomentEvent interface { 13 | MomentID() uint64 14 | SubeditionID() uint32 15 | } 16 | 17 | type subeditionAddedToMomentEvent map[string]any 18 | 19 | func (evt subeditionAddedToMomentEvent) MomentID() uint64 { 20 | return evt["momentID"].(uint64) 21 | } 22 | 23 | func (evt subeditionAddedToMomentEvent) SubeditionID() uint32 { 24 | return evt["subeditionID"].(uint32) 25 | } 26 | 27 | func DecodeSubeditionAddedToMomentEvent(b []byte) (SubeditionAddedToMomentEvent, error) { 28 | cadenceValue, err := decoder.GetCadenceEvent(b) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if cadenceValue.EventType.QualifiedIdentifier != EventSubeditionAddedToMoment { 33 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 34 | } 35 | eventMap, err := decoder.ConvertEvent(cadenceValue) 36 | event := subeditionAddedToMomentEvent(eventMap) 37 | return event, nil 38 | } 39 | -------------------------------------------------------------------------------- /transactions/admin/create_play.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction creates a new play struct 4 | // and stores it in the Top Shot smart contract 5 | // We currently stringify the metadata and insert it into the 6 | // transaction string, but want to use transaction arguments soon 7 | 8 | // Parameters: 9 | // 10 | // metadata: A dictionary of all the play metadata associated 11 | 12 | transaction(metadata: {String: String}) { 13 | 14 | // Local variable for the topshot Admin object 15 | let adminRef: &TopShot.Admin 16 | let currPlayID: UInt32 17 | 18 | prepare(acct: auth(BorrowValue) &Account) { 19 | 20 | // borrow a reference to the admin resource 21 | self.currPlayID = TopShot.nextPlayID; 22 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 23 | ?? panic("No admin resource in storage") 24 | } 25 | 26 | execute { 27 | 28 | // Create a play with the specified metadata 29 | self.adminRef.createPlay(metadata: metadata) 30 | } 31 | 32 | post { 33 | 34 | TopShot.getPlayMetaData(playID: self.currPlayID) != nil: 35 | "playID doesnt exist" 36 | } 37 | } -------------------------------------------------------------------------------- /transactions/admin/create_subedition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction creates a new subedition struct 4 | // and stores it in the Top Shot smart contract 5 | 6 | // Parameters: 7 | // 8 | // name: the name of a new Subedition to be created 9 | // metadata: A dictionary of all the play metadata associated 10 | 11 | transaction(name:String, metadata:{String:String}) { 12 | 13 | // Local variable for the topshot Admin object 14 | let adminRef: &TopShot.Admin 15 | let currSubeditionID: UInt32 16 | 17 | prepare(acct: auth(BorrowValue) &Account) { 18 | 19 | // borrow a reference to the admin resource 20 | self.currSubeditionID = TopShot.getNextSubeditionID(); 21 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 22 | ?? panic("No admin resource in storage") 23 | } 24 | 25 | execute { 26 | 27 | // Create a subedition with the specified metadata 28 | self.adminRef.createSubedition(name: name, metadata: metadata) 29 | } 30 | 31 | post { 32 | 33 | TopShot.getSubeditionByID(subeditionID: self.currSubeditionID) != nil: 34 | "SubedititonID doesnt exist" 35 | } 36 | } -------------------------------------------------------------------------------- /transactions/admin/retire_play_from_set.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is for retiring a play from a set, which 4 | // makes it so that moments can no longer be minted from that edition 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of the set in which a play is to be retired 9 | // playID: the ID of the play to be retired 10 | 11 | transaction(setID: UInt32, playID: UInt32) { 12 | 13 | // local variable for storing the reference to the admin resource 14 | let adminRef: &TopShot.Admin 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | // borrow a reference to the Admin resource in storage 19 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 20 | ?? panic("No admin resource in storage") 21 | } 22 | 23 | execute { 24 | 25 | // borrow a reference to the specified set 26 | let setRef = self.adminRef.borrowSet(setID: setID) 27 | 28 | // retire the play 29 | setRef.retirePlay(playID: playID) 30 | } 31 | 32 | post { 33 | 34 | self.adminRef.borrowSet(setID: setID).getRetired()[playID]!: 35 | "play is not retired" 36 | } 37 | } -------------------------------------------------------------------------------- /lib/go/events/withdraw.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventWithdraw = "TopShot.Withdraw" 10 | ) 11 | 12 | type WithdrawEvent interface { 13 | Id() uint64 14 | From() string 15 | Owner() string 16 | } 17 | 18 | type withdrawEvent map[string]any 19 | 20 | var _ WithdrawEvent = (*withdrawEvent)(nil) 21 | 22 | func (evt withdrawEvent) Id() uint64 { 23 | return evt["id"].(uint64) 24 | } 25 | 26 | func (evt withdrawEvent) From() string { 27 | optionalAddress := evt["from"] 28 | if optionalAddress == nil { 29 | return "undefined" 30 | } 31 | return optionalAddress.(string) 32 | } 33 | 34 | func (evt withdrawEvent) Owner() string { 35 | return evt.From() 36 | } 37 | 38 | func DecodeWithdrawEvent(b []byte) (WithdrawEvent, error) { 39 | cadenceValue, err := decoder.GetCadenceEvent(b) 40 | if err != nil { 41 | return nil, err 42 | } 43 | if cadenceValue.EventType.QualifiedIdentifier != EventWithdraw { 44 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 45 | } 46 | eventMap, err := decoder.ConvertEvent(cadenceValue) 47 | event := withdrawEvent(eventMap) 48 | return event, nil 49 | } 50 | -------------------------------------------------------------------------------- /transactions/shardedCollection/transfer_from_sharded.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotShardedCollection from 0xSHARDEDADDRESS 4 | 5 | // This transaction deposits an NFT to a recipient 6 | 7 | // Parameters 8 | // 9 | // recipient: the Flow address who will receive the NFT 10 | // momentID: moment ID of NFT that recipient will receive 11 | 12 | transaction(recipient: Address, momentID: UInt64) { 13 | 14 | let transferToken: @{NonFungibleToken.NFT} 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | self.transferToken <- acct.storage.borrow(from: /storage/ShardedMomentCollection)!.withdraw(withdrawID: momentID) 19 | } 20 | 21 | execute { 22 | 23 | // get the recipient's public account object 24 | let recipient = getAccount(recipient) 25 | 26 | // get the Collection reference for the receiver 27 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 28 | 29 | // deposit the NFT in the receivers collection 30 | receiverRef.deposit(token: <-self.transferToken) 31 | } 32 | } -------------------------------------------------------------------------------- /lib/go/events/moment_locked.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | var ( 9 | MomentLocked = "TopShotLocking.MomentLocked" 10 | ) 11 | 12 | type MomentLockedEvent interface { 13 | FlowID() uint64 14 | Duration() uint64 15 | ExpiryTimestamp() uint64 16 | } 17 | 18 | type momentLockedEvent map[string]any 19 | 20 | func (evt momentLockedEvent) FlowID() uint64 { 21 | return evt["id"].(uint64) 22 | } 23 | 24 | func (evt momentLockedEvent) Duration() uint64 { 25 | return evt["duration"].(uint64) 26 | } 27 | 28 | func (evt momentLockedEvent) ExpiryTimestamp() uint64 { 29 | return evt["expiryTimestamp"].(uint64) 30 | } 31 | 32 | var _ MomentLockedEvent = (*momentLockedEvent)(nil) 33 | 34 | func DecodeMomentLockedEvent(b []byte) (MomentLockedEvent, error) { 35 | cadenceValue, err := decoder.GetCadenceEvent(b) 36 | if err != nil { 37 | return nil, err 38 | } 39 | if cadenceValue.EventType.QualifiedIdentifier != MomentLocked { 40 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 41 | } 42 | eventMap, err := decoder.ConvertEvent(cadenceValue) 43 | 44 | event := momentLockedEvent(eventMap) 45 | 46 | return event, nil 47 | } 48 | -------------------------------------------------------------------------------- /lib/go/events/set_play_retired.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventPlayRetiredFromSet = "TopShot.PlayRetiredFromSet" 10 | ) 11 | 12 | type SetPlayRetiredEvent interface { 13 | SetID() uint32 14 | PlayID() uint32 15 | NumMoments() uint32 16 | } 17 | 18 | type setPlayRetiredEvent map[string]any 19 | 20 | func (evt setPlayRetiredEvent) SetID() uint32 { 21 | return evt["setID"].(uint32) 22 | } 23 | 24 | func (evt setPlayRetiredEvent) PlayID() uint32 { 25 | return evt["playID"].(uint32) 26 | } 27 | 28 | func (evt setPlayRetiredEvent) NumMoments() uint32 { 29 | return evt["numMoments"].(uint32) 30 | } 31 | 32 | var _ SetPlayRetiredEvent = (*setPlayRetiredEvent)(nil) 33 | 34 | func DecodeSetPlayRetiredEvent(b []byte) (SetPlayRetiredEvent, error) { 35 | cadenceValue, err := decoder.GetCadenceEvent(b) 36 | if err != nil { 37 | return nil, err 38 | } 39 | if cadenceValue.EventType.QualifiedIdentifier != EventPlayRetiredFromSet { 40 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 41 | } 42 | eventMap, err := decoder.ConvertEvent(cadenceValue) 43 | event := setPlayRetiredEvent(eventMap) 44 | return event, nil 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /transactions/user/batch_transfer.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | 4 | // This transaction transfers a number of moments to a recipient 5 | 6 | // Parameters 7 | // 8 | // recipientAddress: the Flow address who will receive the NFTs 9 | // momentIDs: an array of moment IDs of NFTs that recipient will receive 10 | 11 | transaction(recipientAddress: Address, momentIDs: [UInt64]) { 12 | 13 | let transferTokens: @NonFungibleToken.Collection 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | self.transferTokens <- acct.storage.borrow(from: /storage/MomentCollection)!.batchWithdraw(ids: momentIDs) 18 | } 19 | 20 | execute { 21 | 22 | // get the recipient's public account object 23 | let recipient = getAccount(recipientAddress) 24 | 25 | // get the Collection reference for the receiver 26 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 27 | ?? panic("Cannot borrow a reference to the recipient's collection") 28 | 29 | // deposit the NFT in the receivers collection 30 | receiverRef.batchDeposit(token: <-self.transferTokens) 31 | } 32 | } -------------------------------------------------------------------------------- /transactions/shardedCollection/batch_from_sharded.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotShardedCollection from 0xSHARDEDADDRESS 4 | 5 | // This transaction deposits a number of NFTs to a recipient 6 | 7 | // Parameters 8 | // 9 | // recipient: the Flow address who will receive the NFTs 10 | // momentIDs: an array of moment IDs of NFTs that recipient will receive 11 | 12 | transaction(recipient: Address, momentIDs: [UInt64]) { 13 | 14 | let transferTokens: @{NonFungibleToken.Collection} 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | self.transferTokens <- acct.storage.borrow(from: /storage/ShardedMomentCollection)!.batchWithdraw(ids: momentIDs) 19 | } 20 | 21 | execute { 22 | 23 | // get the recipient's public account object 24 | let recipient = getAccount(recipient) 25 | 26 | // get the Collection reference for the receiver 27 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 28 | 29 | // deposit the NFT in the receivers collection 30 | receiverRef.batchDeposit(tokens: <-self.transferTokens) 31 | } 32 | } -------------------------------------------------------------------------------- /lib/go/events/set_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_SetCreated(t *testing.T) { 14 | setID := uint32(1234) 15 | series := uint32(1234) 16 | 17 | setCreatedEventType := cadence.NewEventType( 18 | utils.TestLocation, 19 | "TopShot.SetCreated", 20 | []cadence.Field{ 21 | { 22 | Identifier: "setID", 23 | Type: cadence.UInt32Type, 24 | }, 25 | { 26 | Identifier: "series", 27 | Type: cadence.UInt32Type, 28 | }, 29 | }, 30 | nil, 31 | ) 32 | 33 | setCreatedEvent := cadence.NewEvent([]cadence.Value{ 34 | cadence.NewUInt32(setID), 35 | cadence.NewUInt32(series), 36 | }).WithType(setCreatedEventType) 37 | 38 | payload, err := jsoncdc.Encode(setCreatedEvent) 39 | require.NoError(t, err, "failed to encode set created cadence event") 40 | 41 | decodedSetCreatedEventType, err := DecodeSetCreatedEvent(payload) 42 | require.NoError(t, err, "failed to decode set created cadence event") 43 | 44 | assert.Equal(t, setID, decodedSetCreatedEventType.SetID()) 45 | assert.Equal(t, series, decodedSetCreatedEventType.Series()) 46 | } 47 | -------------------------------------------------------------------------------- /lib/go/events/revealed.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | // EventRevealed specifies that there is a Revealed Event on a PackNFT Contract located at the address 11 | EventRevealed = "PackNFT.Revealed" 12 | ) 13 | 14 | type RevealedEvent interface { 15 | Id() uint64 16 | Salt() string 17 | NFTs() string 18 | } 19 | 20 | type revealedEvent map[string]any 21 | 22 | var _ RevealedEvent = (*revealedEvent)(nil) 23 | 24 | func (evt revealedEvent) Id() uint64 { 25 | return evt["id"].(uint64) 26 | } 27 | 28 | func (evt revealedEvent) Salt() string { 29 | return evt["salt"].(string) 30 | } 31 | 32 | func (evt revealedEvent) NFTs() string { 33 | return evt["nfts"].(string) 34 | } 35 | 36 | func parseNFTs(nft string) []string { 37 | return strings.Split(nft, ",") 38 | } 39 | 40 | func DecodeRevealedEvent(b []byte) (RevealedEvent, error) { 41 | cadenceValue, err := decoder.GetCadenceEvent(b) 42 | if err != nil { 43 | return nil, err 44 | } 45 | if cadenceValue.EventType.QualifiedIdentifier != EventRevealed { 46 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 47 | } 48 | eventMap, err := decoder.ConvertEvent(cadenceValue) 49 | event := revealedEvent(eventMap) 50 | return event, nil 51 | } 52 | -------------------------------------------------------------------------------- /lib/go/events/set_play_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_PlayAddedToSet(t *testing.T) { 14 | setID := uint32(1234) 15 | playID := uint32(1234) 16 | 17 | playAddedToSetEventType := cadence.NewEventType( 18 | utils.TestLocation, 19 | "TopShot.PlayAddedToSet", 20 | []cadence.Field{ 21 | { 22 | Identifier: "setID", 23 | Type: cadence.UInt32Type, 24 | }, 25 | { 26 | Identifier: "playID", 27 | Type: cadence.UInt32Type, 28 | }, 29 | }, 30 | nil, 31 | ) 32 | 33 | playAddedToSetEvent := cadence.NewEvent([]cadence.Value{ 34 | cadence.NewUInt32(setID), 35 | cadence.NewUInt32(playID), 36 | }).WithType(playAddedToSetEventType) 37 | 38 | payload, err := jsoncdc.Encode(playAddedToSetEvent) 39 | require.NoError(t, err, "failed to encode play added to set cadence event") 40 | 41 | decodedPlayAddedToSetEventType, err := DecodePlayAddedToSetEvent(payload) 42 | require.NoError(t, err, "failed to decode play added to set cadence event") 43 | 44 | assert.Equal(t, setID, decodedPlayAddedToSetEventType.SetID()) 45 | assert.Equal(t, playID, decodedPlayAddedToSetEventType.PlayID()) 46 | } 47 | -------------------------------------------------------------------------------- /lib/go/events/deposit_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/onflow/flow-go-sdk" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | 8 | "github.com/onflow/cadence" 9 | jsoncdc "github.com/onflow/cadence/encoding/json" 10 | "github.com/onflow/cadence/runtime/tests/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCadenceEvents_Deposit(t *testing.T) { 15 | id := uint64(1234) 16 | address := flow.HexToAddress("0x12345678") 17 | to := [8]byte(address) 18 | 19 | depositEventType := cadence.NewEventType( 20 | utils.TestLocation, 21 | "TopShot.Deposit", 22 | []cadence.Field{ 23 | { 24 | Identifier: "id", 25 | Type: cadence.UInt64Type, 26 | }, 27 | { 28 | Identifier: "to", 29 | Type: &cadence.OptionalType{}, 30 | }, 31 | }, 32 | nil, 33 | ) 34 | 35 | depositEvent := cadence.NewEvent([]cadence.Value{ 36 | cadence.NewUInt64(id), 37 | cadence.NewOptional(cadence.NewAddress(to)), 38 | }).WithType(depositEventType) 39 | 40 | payload, err := jsoncdc.Encode(depositEvent) 41 | require.NoError(t, err, "failed to encode deposit cadence event") 42 | 43 | decodedDepositEventType, err := DecodeDepositEvent(payload) 44 | require.NoError(t, err, "failed to decode deposit cadence event") 45 | 46 | assert.Equal(t, id, decodedDepositEventType.Id()) 47 | assert.Equal(t, address.String(), decodedDepositEventType.To()) 48 | } 49 | -------------------------------------------------------------------------------- /evm-bridging/src/lib/ERC721TransferValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | import { ICreatorToken } from "../interfaces/ICreatorToken.sol"; 5 | 6 | /** 7 | * @title ERC721TransferValidator 8 | * @notice Functionality to use a transfer validator. 9 | */ 10 | abstract contract ERC721TransferValidator is ICreatorToken { 11 | /// @dev Store the transfer validator. The null address means no transfer validator is set. 12 | address internal _transferValidator; 13 | 14 | /// @notice Revert with an error if the transfer validator is being set to the same address. 15 | error SameTransferValidator(); 16 | 17 | /// @notice Returns the currently active transfer validator. 18 | /// The null address means no transfer validator is set. 19 | function getTransferValidator() external view returns (address) { 20 | return _transferValidator; 21 | } 22 | 23 | /// @notice Set the transfer validator. 24 | /// The external method that uses this must include access control. 25 | function _setTransferValidator(address newValidator) internal { 26 | address oldValidator = _transferValidator; 27 | if (oldValidator == newValidator) { 28 | revert SameTransferValidator(); 29 | } 30 | _transferValidator = newValidator; 31 | emit TransferValidatorUpdated(oldValidator, newValidator); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/go/events/subedition.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | EventSubeditionCreated = "TopShot.SubeditionCreated" 10 | ) 11 | 12 | type SubeditionCreatedEvent interface { 13 | SubeditionId() uint32 14 | Name() string 15 | MetaData() map[string]interface{} 16 | } 17 | 18 | type subeditionCreatedEvent map[string]any 19 | 20 | func (evt subeditionCreatedEvent) SubeditionId() uint32 { 21 | return evt["subeditionID"].(uint32) 22 | } 23 | 24 | func (evt subeditionCreatedEvent) Name() string { 25 | return evt["name"].(string) 26 | } 27 | 28 | func (evt subeditionCreatedEvent) MetaData() map[string]interface{} { 29 | metadata := evt["metadata"].(map[interface{}]interface{}) 30 | result := make(map[string]interface{}) 31 | for k, v := range metadata { 32 | result[k.(string)] = v 33 | } 34 | return result 35 | } 36 | 37 | func DecodeSubeditionCreatedEvent(b []byte) (SubeditionCreatedEvent, error) { 38 | cadenceValue, err := decoder.GetCadenceEvent(b) 39 | if err != nil { 40 | return nil, err 41 | } 42 | if cadenceValue.EventType.QualifiedIdentifier != EventSubeditionCreated { 43 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 44 | } 45 | eventMap, err := decoder.ConvertEvent(cadenceValue) 46 | event := subeditionCreatedEvent(eventMap) 47 | return event, nil 48 | } 49 | -------------------------------------------------------------------------------- /transactions/shardedCollection/setup_sharded_collection.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import TopShotShardedCollection from 0xSHARDEDADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | // This transaction creates and stores an empty moment collection 6 | // and creates a public capability for it. 7 | // Moments are split into a number of buckets 8 | // This makes storage more efficient and performant 9 | 10 | // Parameters 11 | // 12 | // numBuckets: The number of buckets to split Moments into 13 | 14 | transaction(numBuckets: UInt64) { 15 | 16 | prepare(acct: auth(Storage, Capabilities) &Account) { 17 | 18 | if acct.storage.borrow<&TopShotShardedCollection.ShardedCollection>(from: /storage/ShardedMomentCollection) == nil { 19 | 20 | let collection <- TopShotShardedCollection.createEmptyCollection(numBuckets: numBuckets) 21 | // Put a new Collection in storage 22 | acct.storage.save(<-collection, to: /storage/ShardedMomentCollection) 23 | 24 | acct.capabilities.unpublish(/public/MomentCollection) 25 | acct.capabilities.publish( 26 | acct.capabilities.storage.issue<&TopShotShardedCollection.ShardedCollection>(/storage/ShardedMomentCollection), 27 | at: /public/MomentCollection 28 | ) 29 | } else { 30 | 31 | panic("Sharded Collection already exists!") 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /lib/go/events/withdraw_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/onflow/flow-go-sdk" 5 | "github.com/stretchr/testify/require" 6 | "testing" 7 | 8 | "github.com/onflow/cadence" 9 | jsoncdc "github.com/onflow/cadence/encoding/json" 10 | "github.com/onflow/cadence/runtime/tests/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCadenceEvents_Withdraw(t *testing.T) { 15 | id := uint64(1234) 16 | address := flow.HexToAddress("0x12345678") 17 | from := [8]byte(address) 18 | 19 | withdrawEventType := cadence.NewEventType( 20 | utils.TestLocation, 21 | "TopShot.Withdraw", 22 | []cadence.Field{ 23 | { 24 | Identifier: "id", 25 | Type: cadence.UInt64Type, 26 | }, 27 | { 28 | Identifier: "from", 29 | Type: &cadence.OptionalType{}, 30 | }, 31 | }, 32 | nil, 33 | ) 34 | 35 | withdrawEvent := cadence.NewEvent([]cadence.Value{ 36 | cadence.NewUInt64(id), 37 | cadence.NewOptional(cadence.NewAddress(from)), 38 | }).WithType(withdrawEventType) 39 | 40 | payload, err := jsoncdc.Encode(withdrawEvent) 41 | require.NoError(t, err, "failed to encode withdraw cadence event") 42 | 43 | decodedWithdrawEventType, err := DecodeWithdrawEvent(payload) 44 | require.NoError(t, err, "failed to decode withdraw cadence event") 45 | 46 | assert.Equal(t, id, decodedWithdrawEventType.Id()) 47 | assert.Equal(t, address.String(), decodedWithdrawEventType.From()) 48 | } 49 | -------------------------------------------------------------------------------- /transactions/market/create_sale.cdc: -------------------------------------------------------------------------------- 1 | import Market from 0xMARKETADDRESS 2 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 3 | 4 | // This transaction creates a public sale collection capability that any user can interact with 5 | 6 | // Parameters: 7 | // 8 | // tokenReceiverPath: token capability for the account who will receive tokens for purchase 9 | // beneficiaryAccount: the Flow address of the account where a cut of the purchase will be sent 10 | // cutPercentage: how much in percentage the beneficiary will receive from the sale 11 | 12 | transaction(tokenReceiverPath: PublicPath, beneficiaryAccount: Address, cutPercentage: UFix64) { 13 | 14 | prepare(acct: auth(Storage, Capabilities) &Account) { 15 | 16 | let ownerCapability = acct.capabilities.get<&{FungibleToken.Receiver}>(tokenReceiverPath)! 17 | 18 | let beneficiaryCapability = getAccount(beneficiaryAccount).capabilities.get<&{FungibleToken.Receiver}>(tokenReceiverPath)! 19 | 20 | let collection <- Market.createSaleCollection(ownerCapability: ownerCapability, beneficiaryCapability: beneficiaryCapability, cutPercentage: cutPercentage) 21 | 22 | acct.storage.save(<-collection, to: /storage/topshotSaleCollection) 23 | acct.capabilities.publish( 24 | acct.capabilities.storage.issue<&Market.SaleCollection>(/storage/topshotSaleCollection), 25 | at: /public/topshotSaleCollection 26 | ) 27 | } 28 | } -------------------------------------------------------------------------------- /transactions/user/destroy_moments_v2.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 4 | 5 | // This transaction destroys a number of moments owned by a user 6 | 7 | // Parameters 8 | // 9 | // momentIDs: an array of moment IDs of NFTs to be destroyed 10 | 11 | transaction(momentIDs: [UInt64]) { 12 | 13 | let collectionRef: auth(NonFungibleToken.Update) &TopShot.Collection 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | // delist any of the moments that are listed (this delists for both MarketV1 and Marketv3) 17 | if let topshotSaleV3Collection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 18 | for id in momentIDs { 19 | if topshotSaleV3Collection.borrowMoment(id: id) != nil{ 20 | // cancel the moment from the sale, thereby de-listing it 21 | topshotSaleV3Collection.cancelSale(tokenID: id) 22 | } 23 | } 24 | } 25 | 26 | self.collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 27 | ?? panic("Could not borrow from MomentCollection in storage") 28 | } 29 | 30 | execute { 31 | self.collectionRef.destroyMoments(ids: momentIDs) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/TopshotAdminReceiver.cdc: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | AdminReceiver.cdc 4 | 5 | This contract defines a function that takes a TopShot Admin 6 | object and stores it in the storage of the contract account 7 | so it can be used. 8 | 9 | */ 10 | 11 | import TopShot from 0xTOPSHOTADDRESS 12 | import TopShotShardedCollection from 0xSHARDEDADDRESS 13 | 14 | access(all) contract TopshotAdminReceiver { 15 | 16 | // storeAdmin takes a TopShot Admin resource and 17 | // saves it to the account storage of the account 18 | // where the contract is deployed 19 | access(all) fun storeAdmin(newAdmin: @TopShot.Admin) { 20 | self.account.storage.save(<-newAdmin, to: /storage/TopShotAdmin) 21 | } 22 | 23 | init() { 24 | // Save a copy of the sharded Moment Collection to the account storage 25 | if self.account.storage.borrow<&TopShotShardedCollection.ShardedCollection>(from: /storage/ShardedMomentCollection) == nil { 26 | let collection <- TopShotShardedCollection.createEmptyCollection(numBuckets: 32) 27 | // Put a new Collection in storage 28 | self.account.storage.save(<-collection, to: /storage/ShardedMomentCollection) 29 | let cap = self.account.capabilities.storage.issue<&TopShotShardedCollection.ShardedCollection>(/storage/ShardedMomentCollection) 30 | self.account.capabilities.publish(cap, at: /public/MomentCollection) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/go/templates/topshot_sharded_collection_templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "github.com/dapperlabs/nba-smart-contracts/lib/go/templates/internal/assets" 5 | ) 6 | 7 | const ( 8 | setupShardedCollectionFilename = "shardedCollection/setup_sharded_collection.cdc" 9 | transferFromShardedFilename = "shardedCollection/transfer_from_sharded.cdc" 10 | batchTransferFromShardedFilename = "shardedCollection/batch_from_sharded.cdc" 11 | ) 12 | 13 | // GenerateSetupShardedCollectionScript creates a script that sets up an account to use topshot 14 | func GenerateSetupShardedCollectionScript(env Environment) []byte { 15 | code := assets.MustAssetString(transactionsPath + setupShardedCollectionFilename) 16 | 17 | return []byte(replaceAddresses(code, env)) 18 | } 19 | 20 | // GenerateTransferMomentfromShardedCollectionScript creates a script that transfers a moment 21 | func GenerateTransferMomentfromShardedCollectionScript(env Environment) []byte { 22 | code := assets.MustAssetString(transactionsPath + transferFromShardedFilename) 23 | 24 | return []byte(replaceAddresses(code, env)) 25 | } 26 | 27 | // GenerateBatchTransferMomentfromShardedCollectionScript creates a script that transfers a moment 28 | func GenerateBatchTransferMomentfromShardedCollectionScript(env Environment) []byte { 29 | code := assets.MustAssetString(transactionsPath + batchTransferFromShardedFilename) 30 | 31 | return []byte(replaceAddresses(code, env)) 32 | } 33 | -------------------------------------------------------------------------------- /lib/go/events/deposit.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 7 | ) 8 | 9 | const ( 10 | GenericNFTEventDeposit = "NonFungibleToken.Deposited" 11 | TopShotEventDeposit = "TopShot.Deposit" 12 | ) 13 | 14 | type DepositEvent interface { 15 | Id() uint64 16 | Owner() string // deprecated: use To() 17 | To() string 18 | } 19 | 20 | type depositEvent map[string]any 21 | 22 | var _ DepositEvent = (*depositEvent)(nil) 23 | 24 | func (evt depositEvent) Id() uint64 { 25 | return evt["id"].(uint64) 26 | } 27 | 28 | func (evt depositEvent) To() string { 29 | optionalAddress := evt["to"] 30 | if optionalAddress == nil { 31 | return "undefined" 32 | } 33 | address := optionalAddress.(string) 34 | return address 35 | } 36 | 37 | func (evt depositEvent) Owner() string { 38 | return evt.To() 39 | } 40 | 41 | func DecodeDepositEvent(b []byte) (DepositEvent, error) { 42 | cadenceValue, err := decoder.GetCadenceEvent(b) 43 | if err != nil { 44 | return nil, err 45 | } 46 | if id := cadenceValue.EventType.QualifiedIdentifier; id != GenericNFTEventDeposit && id != TopShotEventDeposit { 47 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 48 | } 49 | eventMap, err := decoder.ConvertEvent(cadenceValue) 50 | if err != nil { 51 | return nil, err 52 | } 53 | event := depositEvent(eventMap) 54 | return event, nil 55 | } 56 | -------------------------------------------------------------------------------- /transactions/admin/update_tagline.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction updates multiple existing plays' taglines 4 | // and stores them in the Top Shot smart contract 5 | 6 | // Parameters: 7 | // 8 | // plays: A dictionary of {playID: tagline} pairs 9 | 10 | transaction(plays: {UInt32: String}) { 11 | 12 | // Local variable for the topshot Admin object 13 | let adminRef: &TopShot.Admin 14 | let firstKey: UInt32 15 | let lastKey: UInt32 16 | 17 | prepare(acct: auth(BorrowValue) &Account) { 18 | 19 | // borrow a reference to the admin resource 20 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin) 21 | ?? panic("No admin resource in storage") 22 | self.firstKey = plays.keys[0] 23 | self.lastKey = plays.keys[plays.keys.length - 1] 24 | } 25 | 26 | execute { 27 | // update multiple plays with the specified metadata 28 | for key in plays.keys { 29 | self.adminRef.updatePlayTagline(playID: key, tagline: plays[key] ?? panic("No tagline for play")) 30 | } 31 | } 32 | 33 | post { 34 | TopShot.getPlayMetaDataByField(playID: self.firstKey, field: "Tagline") != nil: 35 | "First play's tagline does not exist" 36 | TopShot.getPlayMetaDataByField(playID: self.lastKey, field: "Tagline") != nil: 37 | "Last play's tagline does not exist" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /transactions/user/destroy_moments.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 4 | 5 | // This transaction destroys a number of moments owned by a user 6 | 7 | // Parameters 8 | // 9 | // momentIDs: an array of moment IDs of NFTs to be destroyed 10 | 11 | transaction(momentIDs: [UInt64]) { 12 | 13 | let collectionRef: auth(NonFungibleToken.Withdraw) &TopShot.Collection 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | // delist any of the moments that are listed (this delists for both MarketV1 and Marketv3) 17 | if let topshotSaleV3Collection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 18 | for id in momentIDs { 19 | if topshotSaleV3Collection.borrowMoment(id: id) != nil{ 20 | // cancel the moment from the sale, thereby de-listing it 21 | topshotSaleV3Collection.cancelSale(tokenID: id) 22 | } 23 | } 24 | } 25 | 26 | self.collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 27 | ?? panic("Could not borrow from MomentCollection in storage") 28 | } 29 | 30 | execute { 31 | let tokens <- self.collectionRef.batchWithdraw(ids: momentIDs) 32 | // destroy the NFTs 33 | destroy tokens 34 | } 35 | } -------------------------------------------------------------------------------- /transactions/scripts/collections/get_metadata.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the metadata associated with a moment 4 | // in a collection by looking up its playID and then searching 5 | // for that play's metadata in the TopShot contract 6 | 7 | // Parameters: 8 | // 9 | // account: The Flow Address of the account whose moment data needs to be read 10 | // id: The unique ID for the moment whose data needs to be read 11 | 12 | // Returns: {String: String} 13 | // A dictionary of all the play metadata associated 14 | // with the specified moment 15 | 16 | access(all) fun main(account: Address, id: UInt64): {String: String} { 17 | 18 | // get the public capability for the owner's moment collection 19 | // and borrow a reference to it 20 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 21 | ?? panic("Could not get public moment collection reference") 22 | 23 | // Borrow a reference to the specified moment 24 | let token = collectionRef.borrowMoment(id: id) 25 | ?? panic("Could not borrow a reference to the specified moment") 26 | 27 | // Get the moment's metadata to access its play and Set IDs 28 | let data = token.data 29 | 30 | // Use the moment's play ID 31 | // to get all the metadata associated with that play 32 | let metadata = TopShot.getPlayMetaData(playID: data.playID) ?? panic("Play doesn't exist") 33 | 34 | log(metadata) 35 | 36 | return metadata 37 | } -------------------------------------------------------------------------------- /transactions/marketV3/mint_and_purchase.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import DapperUtilityCoin from 0xDUCADDRESS 3 | import TopShot from 0xTOPSHOTADDRESS 4 | import Market from 0xMARKETADDRESS 5 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 6 | 7 | transaction(sellerAddress: Address, recipient: Address, tokenID: UInt64, purchaseAmount: UFix64) { 8 | 9 | prepare(signer: auth(BorrowValue) &Account) { 10 | 11 | let tokenAdmin = signer 12 | .storage.borrow<&DapperUtilityCoin.Minter>(from: /storage/dapperUtilityCoinAdmin) 13 | ?? panic("Signer is not the token admin") 14 | 15 | 16 | let mintedVault <- tokenAdmin.mintTokens(amount: purchaseAmount) as! @DapperUtilityCoin.Vault 17 | 18 | 19 | let seller = getAccount(sellerAddress) 20 | let topshotSaleCollection = seller.capabilities.borrow<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketPublicPath) 21 | ?? panic("Could not borrow public sale reference") 22 | 23 | let boughtToken <- topshotSaleCollection.purchase(tokenID: tokenID, buyTokens: <-mintedVault) 24 | 25 | // get the recipient's public account object and borrow a reference to their moment receiver 26 | let recipient = getAccount(recipient).capabilities.borrow<&TopShot.Collection>(/public/MomentCollection) 27 | ?? panic("Could not borrow a reference to the moment collection") 28 | 29 | // deposit the NFT in the receivers collection 30 | recipient.deposit(token: <-boughtToken) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_metadata_field.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script gets the metadata associated with a moment 4 | // in a collection by looking up its playID and then searching 5 | // for that play's metadata in the TopShot contract. It returns 6 | // the value for the specified metadata field 7 | 8 | // Parameters: 9 | // 10 | // account: The Flow Address of the account whose moment data needs to be read 11 | // momentID: The unique ID for the moment whose data needs to be read 12 | // fieldToSearch: The specified metadata field whose data needs to be read 13 | 14 | // Returns: String 15 | // Value of specified metadata field 16 | 17 | access(all) fun main(account: Address, momentID: UInt64, fieldToSearch: String): String { 18 | 19 | // borrow a public reference to the owner's moment collection 20 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 21 | ?? panic("Could not get public moment collection reference") 22 | 23 | // borrow a reference to the specified moment in the collection 24 | let token = collectionRef.borrowMoment(id: id) 25 | ?? panic("Could not borrow a reference to the specified moment") 26 | 27 | // Get the tokens data 28 | let data = token.data 29 | 30 | // Get the metadata field associated with the specific play 31 | let field = TopShot.getPlayMetaDataByField(playID: data.playID, field: fieldToSearch) ?? panic("Play doesn't exist") 32 | 33 | log(field) 34 | 35 | return field 36 | } -------------------------------------------------------------------------------- /lib/go/events/subedition_added_to_moment_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/onflow/cadence" 9 | jsoncdc "github.com/onflow/cadence/encoding/json" 10 | "github.com/onflow/cadence/runtime/tests/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCadenceEvents_SubeditionAddedToMoment(t *testing.T) { 15 | var ( 16 | subeditionID = uint32(1234) 17 | momentID = uint64(1234) 18 | ) 19 | 20 | subeditionAddedToMomentEventType := cadence.NewEventType( 21 | utils.TestLocation, 22 | "TopShot.SubeditionAddedToMoment", 23 | []cadence.Field{ 24 | { 25 | Identifier: "momentID", 26 | Type: cadence.UInt64Type, 27 | }, 28 | { 29 | Identifier: "subeditionID", 30 | Type: cadence.UInt32Type, 31 | }, 32 | }, 33 | nil, 34 | ) 35 | 36 | subeditionAddedToMomentEvent := cadence.NewEvent([]cadence.Value{ 37 | cadence.NewUInt64(momentID), 38 | cadence.NewUInt32(subeditionID), 39 | }).WithType(subeditionAddedToMomentEventType) 40 | 41 | payload, err := jsoncdc.Encode(subeditionAddedToMomentEvent) 42 | require.NoError(t, err, "failed to encode subedition added to moment cadence event") 43 | 44 | decodedPlayCreatedEventType, err := DecodeSubeditionAddedToMomentEvent(payload) 45 | require.NoError(t, err, "failed to decode subedition added to moment cadence event") 46 | 47 | assert.Equal(t, momentID, decodedPlayCreatedEventType.MomentID()) 48 | assert.Equal(t, subeditionID, decodedPlayCreatedEventType.SubeditionID()) 49 | } 50 | -------------------------------------------------------------------------------- /transactions/market/stop_sale.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import Market from 0xMARKETADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | 6 | // This transaction is for a user to stop a moment sale in their account 7 | // by withdrawing that moment from their sale collection and depositing 8 | // it into their normal moment collection 9 | 10 | // Parameters 11 | // 12 | // tokenID: the ID of the moment whose sale is to be delisted 13 | 14 | transaction(tokenID: UInt64) { 15 | 16 | let collectionRef: &TopShot.Collection 17 | let saleCollectionRef: auth(NonFungibleToken.Withdraw) &Market.SaleCollection 18 | 19 | prepare(acct: auth(Storage, Capabilities) &Account) { 20 | 21 | // Borrow a reference to the NFT collection in the signers account 22 | self.collectionRef = acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) 23 | ?? panic("Could not borrow from MomentCollection in storage") 24 | 25 | // borrow a reference to the owner's sale collection 26 | self.saleCollectionRef = acct.storage.borrow(from: /storage/topshotSaleCollection) 27 | ?? panic("Could not borrow from sale in storage") 28 | } 29 | 30 | execute { 31 | 32 | // withdraw the moment from the sale, thereby de-listing it 33 | let token <- self.saleCollectionRef.withdraw(tokenID: tokenID) 34 | 35 | // deposit the moment into the owner's collection 36 | self.collectionRef.deposit(token: <-token) 37 | } 38 | } -------------------------------------------------------------------------------- /transactions/admin/mint_moment.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is what an admin would use to mint a single new moment 4 | // and deposit it in a user's collection 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of a set containing the target play 9 | // playID: the ID of a play from which a new moment is minted 10 | // recipientAddr: the Flow address of the account receiving the newly minted moment 11 | 12 | transaction(setID: UInt32, playID: UInt32, recipientAddr: Address) { 13 | // local variable for the admin reference 14 | let adminRef: &TopShot.Admin 15 | 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | // borrow a reference to the Admin resource in storage 18 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)! 19 | } 20 | 21 | execute { 22 | // Borrow a reference to the specified set 23 | let setRef = self.adminRef.borrowSet(setID: setID) 24 | 25 | // Mint a new NFT 26 | let moment1 <- setRef.mintMoment(playID: playID) 27 | 28 | // get the public account object for the recipient 29 | let recipient = getAccount(recipientAddr) 30 | 31 | // get the Collection reference for the receiver 32 | 33 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 34 | ?? panic("Cannot borrow a reference to the recipient's moment collection") 35 | 36 | // deposit the NFT in the receivers collection 37 | receiverRef.deposit(token: <-moment1) 38 | } 39 | } -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/transfer_flow_to_evm_address.cdc: -------------------------------------------------------------------------------- 1 | import "FungibleToken" 2 | import "FlowToken" 3 | 4 | import "EVM" 5 | 6 | /// Transfers $FLOW from the signer's account Cadence Flow balance to the recipient's hex-encoded EVM address. 7 | /// 8 | transaction(recipientEVMAddressHex: String, amount: UFix64) { 9 | 10 | var sentVault: @FlowToken.Vault 11 | let recipientEVMAddress: EVM.EVMAddress 12 | let recipientPreBalance: UFix64 13 | 14 | prepare(signer: auth(BorrowValue, SaveValue) &Account) { 15 | // Borrow a reference to the signer's FlowToken.Vault and withdraw the amount 16 | let vaultRef = signer.storage.borrow( 17 | from: /storage/flowTokenVault 18 | ) ?? panic("Could not borrow reference to the owner's Vault!") 19 | self.sentVault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault 20 | 21 | // Get the recipient's EVM address 22 | self.recipientEVMAddress = EVM.addressFromString(recipientEVMAddressHex) 23 | 24 | // Get the recipient's balance before the transfer to check the amount transferred 25 | self.recipientPreBalance = self.recipientEVMAddress.balance().inFLOW() 26 | } 27 | 28 | execute { 29 | // Deposit the amount to the recipient's EVM address 30 | self.recipientEVMAddress.deposit(from: <-self.sentVault) 31 | } 32 | 33 | post { 34 | self.recipientEVMAddress.balance().inFLOW() == self.recipientPreBalance + amount: 35 | "Problem transferring value to EVM address" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /transactions/market/start_sale.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import Market from 0xMARKETADDRESS 3 | import NonFungibleToken from 0xNFTADDRESS 4 | 5 | // This transaction is for a user to put a new moment up for sale 6 | // They must have TopShot Collection and a Market Sale Collection 7 | // stored in their account 8 | 9 | // Parameters 10 | // 11 | // momentId: the ID of the moment to be listed for sale 12 | // price: the sell price of the moment 13 | 14 | transaction(momentID: UInt64, price: UFix64) { 15 | 16 | let collectionRef: auth(NonFungibleToken.Withdraw) &TopShot.Collection 17 | let saleCollectionRef: auth(Market.Create) &Market.SaleCollection 18 | 19 | prepare(acct: auth(Storage, Capabilities) &Account) { 20 | 21 | // borrow a reference to the Top Shot Collection 22 | self.collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 23 | ?? panic("Could not borrow from MomentCollection in storage") 24 | 25 | // borrow a reference to the topshot Sale Collection 26 | self.saleCollectionRef = acct.storage.borrow(from: /storage/topshotSaleCollection) 27 | ?? panic("Could not borrow from sale in storage") 28 | } 29 | 30 | execute { 31 | 32 | // withdraw the specified token from the collection 33 | let token <- self.collectionRef.withdraw(withdrawID: momentID) as! @TopShot.NFT 34 | 35 | // List the specified moment for sale 36 | self.saleCollectionRef.listForSale(token: <-token, price: price) 37 | } 38 | } -------------------------------------------------------------------------------- /transactions/marketV3/stop_sale.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import Market from 0xMARKETADDRESS 3 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 4 | import NonFungibleToken from 0xNFTADDRESS 5 | 6 | // This transaction is for a user to stop a moment sale in their account 7 | 8 | // Parameters 9 | // 10 | // tokenID: the ID of the moment whose sale is to be delisted 11 | 12 | transaction(tokenID: UInt64) { 13 | 14 | prepare(acct: auth(BorrowValue) &Account) { 15 | 16 | // borrow a reference to the owner's sale collection 17 | if let topshotSaleV3Collection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 18 | 19 | // cancel the moment from the sale, thereby de-listing it 20 | topshotSaleV3Collection.cancelSale(tokenID: tokenID) 21 | 22 | } else if let topshotSaleCollection = acct.storage.borrow(from: /storage/topshotSaleCollection) { 23 | // Borrow a reference to the NFT collection in the signers account 24 | let collectionRef = acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) 25 | ?? panic("Could not borrow from MomentCollection in storage") 26 | 27 | // withdraw the moment from the sale, thereby de-listing it 28 | let token <- topshotSaleCollection.withdraw(tokenID: tokenID) 29 | 30 | // deposit the moment into the owner's collection 31 | collectionRef.deposit(token: <-token) 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /lib/go/events/set_play_retired_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_SetPlayRetired(t *testing.T) { 14 | setID := uint32(1234) 15 | playID := uint32(1234) 16 | numMoments := uint32(1234) 17 | 18 | setPlayRetiredEventType := cadence.NewEventType( 19 | utils.TestLocation, 20 | "TopShot.PlayRetiredFromSet", 21 | []cadence.Field{ 22 | { 23 | Identifier: "setID", 24 | Type: cadence.UInt32Type, 25 | }, 26 | { 27 | Identifier: "playID", 28 | Type: cadence.UInt32Type, 29 | }, 30 | { 31 | Identifier: "numMoments", 32 | Type: cadence.UInt32Type, 33 | }, 34 | }, 35 | nil, 36 | ) 37 | 38 | setPlayRetiredEvent := cadence.NewEvent([]cadence.Value{ 39 | cadence.NewUInt32(setID), 40 | cadence.NewUInt32(playID), 41 | cadence.NewUInt32(numMoments), 42 | }).WithType(setPlayRetiredEventType) 43 | 44 | payload, err := jsoncdc.Encode(setPlayRetiredEvent) 45 | require.NoError(t, err, "failed to encode set play retired cadence event") 46 | 47 | decodedSetPlayRetiredEvent, err := DecodeSetPlayRetiredEvent(payload) 48 | require.NoError(t, err, "failed to decode set play retired cadence event") 49 | 50 | assert.Equal(t, setID, decodedSetPlayRetiredEvent.SetID()) 51 | assert.Equal(t, playID, decodedSetPlayRetiredEvent.SetID()) 52 | assert.Equal(t, numMoments, decodedSetPlayRetiredEvent.SetID()) 53 | } 54 | -------------------------------------------------------------------------------- /lib/go/templates/fastbreak_oracle_templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "github.com/dapperlabs/nba-smart-contracts/lib/go/templates/internal/assets" 5 | ) 6 | 7 | const ( 8 | createFastBreakRunFilename = "fastbreak/oracle/create_run.cdc" 9 | createFastBreakGameFilename = "fastbreak/oracle/create_game.cdc" 10 | addStatToFastBreakGameFilename = "fastbreak/oracle/add_stat_to_game.cdc" 11 | updateFastBreakGameFilename = "fastbreak/oracle/update_fast_break_game.cdc" 12 | scoreFastBreakSubmissionFilename = "fastbreak/oracle/score_fast_break_submission.cdc" 13 | ) 14 | 15 | func GenerateCreateRunScript(env Environment) []byte { 16 | code := assets.MustAssetString(transactionsPath + createFastBreakRunFilename) 17 | 18 | return []byte(replaceAddresses(code, env)) 19 | } 20 | 21 | func GenerateCreateGameScript(env Environment) []byte { 22 | code := assets.MustAssetString(transactionsPath + createFastBreakGameFilename) 23 | 24 | return []byte(replaceAddresses(code, env)) 25 | } 26 | 27 | func GenerateAddStatToGameScript(env Environment) []byte { 28 | code := assets.MustAssetString(transactionsPath + addStatToFastBreakGameFilename) 29 | 30 | return []byte(replaceAddresses(code, env)) 31 | } 32 | 33 | func GenerateUpdateFastBreakGameScript(env Environment) []byte { 34 | code := assets.MustAssetString(transactionsPath + updateFastBreakGameFilename) 35 | 36 | return []byte(replaceAddresses(code, env)) 37 | } 38 | 39 | func GenerateScoreFastBreakSubmissionScript(env Environment) []byte { 40 | code := assets.MustAssetString(transactionsPath + scoreFastBreakSubmissionFilename) 41 | 42 | return []byte(replaceAddresses(code, env)) 43 | } 44 | -------------------------------------------------------------------------------- /lib/go/events/moment_minted.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "github.com/dapperlabs/nba-smart-contracts/lib/go/events/decoder" 6 | ) 7 | 8 | const ( 9 | // This variable specifies that there is a MomentMinted Event on a TopShot Contract located at address 0x04 10 | EventMomentMinted = "TopShot.MomentMinted" 11 | ) 12 | 13 | type MomentMintedEvent interface { 14 | MomentId() uint64 15 | PlayId() uint32 16 | SetId() uint32 17 | SerialNumber() uint32 18 | SubeditionId() uint32 19 | } 20 | 21 | type momentMintedEvent map[string]any 22 | 23 | func (evt momentMintedEvent) MomentId() uint64 { 24 | return evt["momentID"].(uint64) 25 | } 26 | 27 | func (evt momentMintedEvent) PlayId() uint32 { 28 | return evt["playID"].(uint32) 29 | } 30 | 31 | func (evt momentMintedEvent) SetId() uint32 { 32 | return evt["setID"].(uint32) 33 | } 34 | 35 | func (evt momentMintedEvent) SerialNumber() uint32 { 36 | return evt["serialNumber"].(uint32) 37 | } 38 | 39 | func (evt momentMintedEvent) SubeditionId() uint32 { 40 | if val, ok := evt["subeditionID"]; ok { 41 | return val.(uint32) 42 | } 43 | return 0 44 | } 45 | 46 | var _ MomentMintedEvent = (*momentMintedEvent)(nil) 47 | 48 | func DecodeMomentMintedEvent(b []byte) (MomentMintedEvent, error) { 49 | cadenceValue, err := decoder.GetCadenceEvent(b) 50 | if err != nil { 51 | return nil, err 52 | } 53 | if cadenceValue.EventType.QualifiedIdentifier != EventMomentMinted { 54 | return nil, fmt.Errorf("unexpected event type: %s", cadenceValue.EventType.QualifiedIdentifier) 55 | } 56 | eventMap, err := decoder.ConvertEvent(cadenceValue) 57 | event := momentMintedEvent(eventMap) 58 | return event, nil 59 | } 60 | -------------------------------------------------------------------------------- /lib/go/events/revealed_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/onflow/cadence" 10 | jsoncdc "github.com/onflow/cadence/encoding/json" 11 | "github.com/onflow/cadence/runtime/tests/utils" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestCadenceEvents_Reveal(t *testing.T) { 16 | var ( 17 | packID = uint64(10) 18 | salt = "salt" 19 | 20 | momentID1 = uint64(1) 21 | momentID2 = uint64(2) 22 | momentID3 = uint64(3) 23 | momentIDs = fmt.Sprintf(`%d,%d,%d`, momentID1, momentID2, momentID3) 24 | ) 25 | 26 | revealedEventType := cadence.NewEventType( 27 | utils.TestLocation, 28 | "PackNFT.Revealed", 29 | []cadence.Field{ 30 | { 31 | Identifier: "id", 32 | Type: cadence.UInt32Type, 33 | }, 34 | { 35 | Identifier: "salt", 36 | Type: cadence.StringType, 37 | }, 38 | { 39 | Identifier: "nfts", 40 | Type: cadence.StringType, 41 | }, 42 | }, 43 | nil, 44 | ) 45 | 46 | revealedEvent := cadence.NewEvent([]cadence.Value{ 47 | cadence.NewUInt64(packID), 48 | NewCadenceString(salt), 49 | NewCadenceString(momentIDs), 50 | }).WithType(revealedEventType) 51 | 52 | revealedPayload, err := jsoncdc.Encode(revealedEvent) 53 | require.NoError(t, err, "failed to encode revealed cadence event") 54 | 55 | decodedRevealedEventType, err := DecodeRevealedEvent(revealedPayload) 56 | require.NoError(t, err, "failed to decode revealed cadence event") 57 | 58 | assert.Equal(t, packID, decodedRevealedEventType.Id()) 59 | assert.Equal(t, salt, decodedRevealedEventType.Salt()) 60 | assert.Equal(t, momentIDs, decodedRevealedEventType.NFTs()) 61 | 62 | } 63 | -------------------------------------------------------------------------------- /transactions/admin/mint_moment_with_subedition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction is what an admin would use to mint a single new moment 4 | // and deposit it in a user's collection 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of a set containing the target play 9 | // playID: the ID of a play from which a new moment is minted 10 | // subeditionID: the ID of play's subedition 11 | // recipientAddr: the Flow address of the account receiving the newly minted moment 12 | 13 | transaction(setID: UInt32, playID: UInt32, subeditionID: UInt32, recipientAddr: Address) { 14 | // local variable for the admin reference 15 | let adminRef: &TopShot.Admin 16 | 17 | prepare(acct: auth(BorrowValue) &Account) { 18 | // borrow a reference to the Admin resource in storage 19 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)! 20 | } 21 | 22 | execute { 23 | // Borrow a reference to the specified set 24 | let setRef = self.adminRef.borrowSet(setID: setID) 25 | 26 | // Mint a new NFT with Subedition 27 | let moment1 <- setRef.mintMomentWithSubedition(playID: playID, subeditionID: subeditionID) 28 | 29 | // get the public account object for the recipient 30 | let recipient = getAccount(recipientAddr) 31 | 32 | // get the Collection reference for the receiver 33 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 34 | ?? panic("Cannot borrow a reference to the recipient's moment collection") 35 | 36 | // deposit the NFT in the receivers collection 37 | receiverRef.deposit(token: <-moment1) 38 | } 39 | } -------------------------------------------------------------------------------- /transactions/admin/batch_mint_moment.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction mints multiple moments 4 | // from a single set/play combination (otherwise known as edition) 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of the set to be minted from 9 | // playID: the ID of the Play from which the Moments are minted 10 | // quantity: the quantity of Moments to be minted 11 | // recipientAddr: the Flow address of the account receiving the collection of minted moments 12 | 13 | transaction(setID: UInt32, playID: UInt32, quantity: UInt64, recipientAddr: Address) { 14 | 15 | // Local variable for the topshot Admin object 16 | let adminRef: &TopShot.Admin 17 | 18 | prepare(acct: auth(BorrowValue) &Account) { 19 | 20 | // borrow a reference to the Admin resource in storage 21 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)! 22 | } 23 | 24 | execute { 25 | 26 | // borrow a reference to the set to be minted from 27 | let setRef = self.adminRef.borrowSet(setID: setID) 28 | 29 | // Mint all the new NFTs 30 | let collection <- setRef.batchMintMoment(playID: playID, quantity: quantity) 31 | 32 | // Get the account object for the recipient of the minted tokens 33 | let recipient = getAccount(recipientAddr) 34 | 35 | // get the Collection reference for the receiver 36 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 37 | ?? panic("Cannot borrow a reference to the recipient's collection") 38 | 39 | // deposit the NFT in the receivers collection 40 | receiverRef.batchDeposit(tokens: <-collection) 41 | } 42 | } -------------------------------------------------------------------------------- /transactions/admin/fulfill_pack.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotShardedCollection from 0xSHARDEDADDRESS 4 | 5 | // This transaction is what Top Shot uses to send the moments in a "pack" to 6 | // a user's collection 7 | 8 | // Parameters: 9 | // 10 | // recipientAddr: the Flow address of the account receiving a pack of moments 11 | // momentsIDs: an array of moment IDs to be withdrawn from the owner's moment collection 12 | 13 | transaction(recipientAddr: Address, momentIDs: [UInt64]) { 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // get the recipient's public account object 18 | let recipient = getAccount(recipientAddr) 19 | 20 | // borrow a reference to the recipient's moment collection 21 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 22 | ?? panic("Cannot borrow a reference to the recipient's collection") 23 | 24 | 25 | 26 | // borrow a reference to the owner's moment collection 27 | if let collection = acct.storage.borrow(from: /storage/ShardedMomentCollection) { 28 | 29 | receiverRef.batchDeposit(tokens: <-collection.batchWithdraw(ids: momentIDs)) 30 | } else { 31 | 32 | let collection = acct.storage.borrow(from: /storage/MomentCollection)! 33 | 34 | // Deposit the pack of moments to the recipient's collection 35 | receiverRef.batchDeposit(tokens: <-collection.batchWithdraw(ids: momentIDs)) 36 | 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /transactions/user/transfer_moment.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | 4 | // This transaction transfers a moment to a recipient 5 | 6 | // This transaction is how a topshot user would transfer a moment 7 | // from their account to another account 8 | // The recipient must have a TopShot Collection object stored 9 | // and a public MomentCollectionPublic capability stored at 10 | // `/public/MomentCollection` 11 | 12 | // Parameters: 13 | // 14 | // recipient: The Flow address of the account to receive the moment. 15 | // withdrawID: The id of the moment to be transferred 16 | 17 | transaction(recipient: Address, withdrawID: UInt64) { 18 | 19 | // local variable for storing the transferred token 20 | let transferToken: @{NonFungibleToken.NFT} 21 | 22 | prepare(acct: auth(BorrowValue) &Account) { 23 | 24 | // borrow a reference to the owner's collection 25 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 26 | ?? panic("Could not borrow a reference to the stored Moment collection") 27 | 28 | // withdraw the NFT 29 | self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID) 30 | } 31 | 32 | execute { 33 | 34 | // get the recipient's public account object 35 | let recipient = getAccount(recipient) 36 | 37 | // get the Collection reference for the receiver 38 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 39 | 40 | // deposit the NFT in the receivers collection 41 | receiverRef.deposit(token: <-self.transferToken) 42 | } 43 | } -------------------------------------------------------------------------------- /transactions/marketV3/upgrade_sale.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | import Market from 0xMARKETADDRESS 3 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 4 | import NonFungibleToken from 0xNFTADDRESS 5 | 6 | // This transaction is for a user to change a moment sale from 7 | // the first version of the market contract to the third version 8 | 9 | // Parameters 10 | // 11 | // tokenID: the ID of the moment whose sale is to be upgraded 12 | 13 | transaction(tokenID: UInt64, price: UFix64) { 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // Borrow a reference to the NFT collection in the signers account 18 | let nftCollection = acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) 19 | ?? panic("Could not borrow from MomentCollection in storage") 20 | 21 | // borrow a reference to the owner's sale collection 22 | let topshotSaleCollection = acct.storage.borrow(from: /storage/topshotSaleCollection) 23 | ?? panic("Could not borrow from sale in storage") 24 | 25 | let topshotSaleV3Collection = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) 26 | ?? panic("Could not borrow reference to sale V2 in storage") 27 | 28 | // withdraw the moment from the sale, thereby de-listing it 29 | let token <- topshotSaleCollection.withdraw(tokenID: tokenID) 30 | 31 | // deposit the moment into the owner's collection 32 | nftCollection.deposit(token: <-token) 33 | 34 | // List the specified moment for sale 35 | topshotSaleV3Collection.listForSale(tokenID: tokenID, price: price) 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /lib/go/events/play_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/onflow/cadence" 9 | jsoncdc "github.com/onflow/cadence/encoding/json" 10 | "github.com/onflow/cadence/runtime/tests/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCadenceEvents_PlayCreated(t *testing.T) { 15 | var ( 16 | id = uint32(1234) 17 | playerKey = "playerID" 18 | playerValue = "player ID" 19 | teamKey = "teamAtMoment" 20 | teamValue = "current team" 21 | ) 22 | 23 | playCreatedEventType := cadence.NewEventType( 24 | utils.TestLocation, 25 | "TopShot.PlayCreated", 26 | []cadence.Field{ 27 | { 28 | Identifier: "id", 29 | Type: cadence.UInt32Type, 30 | }, 31 | { 32 | Identifier: "metadata", 33 | Type: &cadence.DictionaryType{}, 34 | }, 35 | }, 36 | nil, 37 | ) 38 | 39 | playCreatedEvent := cadence.NewEvent([]cadence.Value{ 40 | cadence.NewUInt32(id), 41 | cadence.NewDictionary([]cadence.KeyValuePair{ 42 | {Key: NewCadenceString(playerKey), Value: NewCadenceString(playerValue)}, 43 | {Key: NewCadenceString(teamKey), Value: NewCadenceString(teamValue)}, 44 | }), 45 | }).WithType(playCreatedEventType) 46 | 47 | payload, err := jsoncdc.Encode(playCreatedEvent) 48 | require.NoError(t, err, "failed to encode play created cadence event") 49 | 50 | decodedPlayCreatedEventType, err := DecodePlayCreatedEvent(payload) 51 | require.NoError(t, err, "failed to decode play created cadence event") 52 | 53 | assert.Equal(t, id, decodedPlayCreatedEventType.Id()) 54 | assert.Equal(t, map[interface{}]interface{}{ 55 | playerKey: playerValue, 56 | teamKey: teamValue, 57 | }, decodedPlayCreatedEventType.MetaData()) 58 | } 59 | -------------------------------------------------------------------------------- /transactions/user/transfer_moment_v3_sale.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 4 | 5 | // This transaction transfers a moment to a recipient 6 | // and cancels the sale in the V3 collection if it exists 7 | 8 | // Parameters: 9 | // 10 | // recipient: The Flow address of the account to receive the moment. 11 | // withdrawID: The id of the moment to be transferred 12 | 13 | transaction(recipient: Address, withdrawID: UInt64) { 14 | 15 | // local variable for storing the transferred token 16 | let transferToken: @{NonFungibleToken.NFT} 17 | 18 | prepare(acct: auth(Storage, Capabilities) &Account) { 19 | 20 | // borrow a reference to the owner's collection 21 | let collectionRef = acct.storage.borrow(from: /storage/MomentCollection) 22 | ?? panic("Could not borrow a reference to the stored Moment collection") 23 | 24 | // withdraw the NFT 25 | self.transferToken <- collectionRef.withdraw(withdrawID: withdrawID) 26 | 27 | if let saleRef = acct.storage.borrow(from: TopShotMarketV3.marketStoragePath) { 28 | saleRef.cancelSale(tokenID: withdrawID) 29 | } 30 | } 31 | 32 | execute { 33 | 34 | // get the recipient's public account object 35 | let recipient = getAccount(recipient) 36 | 37 | // get the Collection reference for the receiver 38 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection)! 39 | 40 | // deposit the NFT in the receivers collection 41 | receiverRef.deposit(token: <-self.transferToken) 42 | } 43 | } -------------------------------------------------------------------------------- /lib/go/README.md: -------------------------------------------------------------------------------- 1 | # NBA Top Shot Go Packages 2 | 3 | This directory conains packages for interacting with the NBA Top Shot 4 | smart contracts from a Go programming environment. 5 | 6 | # Package Guides 7 | 8 | - `contracts`: Contains functions to generate the text of the contract code 9 | for the contracts in the `/nba-smart-contracts/contracts` directory. 10 | To generate the contracts: 11 | 1. Fetch the `contracts` package: `go get github.com/dapperlabs/nba-smart-contracts/contracts@v0.1.9` 12 | 2. Import the package at the top of your Go File: `import "github.com/dapperlabs/nba-smart-contracts/lib/go/contracts"` 13 | 3. Call the `GenerateTopShotContract` and others to generate the full text of the contracts. 14 | - `events`: Contains go definitions for the events that are emitted by 15 | the Top Shot contracts so that these events can be monitored by applications. 16 | - `templates`: Contains functions to return transaction templates 17 | for common transactions and scripts for interacting with the Top Shot 18 | smart contracts. 19 | If you want to import the transactions in your Go programs 20 | so you can submit them to interact with the NBA Top Shot smart contracts, 21 | you can do so with the `templates` package: 22 | 1. Fetch the `templates` package: `go get github.com/dapperlabs/nba-smart-contracts/templates@v0.1.10` 23 | 2. Import the package at the top of your Go File: `import "github.com/dapperlabs/nba-smart-contracts/lib/go/templates"` 24 | 3. Call the various functions in the `templates` package like `templates.GenerateTransferMomentScript()` and others to generate the full text of the templates that you can fill in with your arguments. 25 | - `templates/data`: Contains go constructs for representing play metadata 26 | for Top Shot plays on chain. 27 | - `test`: Contains automated go tests for testing the functionality 28 | of the Top Shot smart contracts. -------------------------------------------------------------------------------- /transactions/admin/batch_mint_moment_with_subedition.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This transaction mints multiple moments 4 | // from a single set/play/subedition combination 5 | 6 | // Parameters: 7 | // 8 | // setID: the ID of the set to be minted from 9 | // playID: the ID of the Play from which the Moments are minted 10 | // subeditionID: the ID of play's subedition 11 | // quantity: the quantity of Moments to be minted 12 | // recipientAddr: the Flow address of the account receiving the collection of minted moments 13 | 14 | transaction(setID: UInt32, playID: UInt32, quantity: UInt64, subeditionID: UInt32, recipientAddr: Address) { 15 | 16 | // Local variable for the topshot Admin object 17 | let adminRef: &TopShot.Admin 18 | 19 | prepare(acct: auth(BorrowValue) &Account) { 20 | 21 | // borrow a reference to the Admin resource in storage 22 | self.adminRef = acct.storage.borrow<&TopShot.Admin>(from: /storage/TopShotAdmin)! 23 | } 24 | 25 | execute { 26 | 27 | // borrow a reference to the set to be minted from 28 | let setRef = self.adminRef.borrowSet(setID: setID) 29 | 30 | // Mint all the new NFTs with Subeditions 31 | let collection <- setRef.batchMintMomentWithSubedition(playID: playID, quantity: quantity, subeditionID: subeditionID) 32 | 33 | // Get the account object for the recipient of the minted tokens 34 | let recipient = getAccount(recipientAddr) 35 | 36 | // get the Collection reference for the receiver 37 | let receiverRef = recipient.capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 38 | ?? panic("Cannot borrow a reference to the recipient's collection") 39 | 40 | // deposit the NFT in the receivers collection 41 | receiverRef.batchDeposit(tokens: <-collection) 42 | } 43 | } -------------------------------------------------------------------------------- /transactions/user/setup_up_all_collections.cdc: -------------------------------------------------------------------------------- 1 | import NonFungibleToken from 0xNFTADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import MetadataViews from 0xMETADATAVIEWSADDRESS 4 | import PackNFT from 0xPACKNFTADDRESS 5 | 6 | // This transaction sets up an account to use Top Shot 7 | // by storing an empty moment collection and creating 8 | // a public capability for it 9 | 10 | transaction { 11 | 12 | prepare(acct: auth(Storage, Capabilities) &Account) { 13 | 14 | // First, check to see if a moment collection already exists 15 | if acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) == nil { 16 | // create a new TopShot Collection 17 | let collection <- TopShot.createEmptyCollection() as! @TopShot.Collection 18 | // Put the new Collection in storage 19 | acct.storage.save(<-collection, to: /storage/MomentCollection) 20 | } 21 | 22 | acct.capabilities.unpublish(/public/MomentCollection) 23 | acct.capabilities.publish( 24 | acct.capabilities.storage.issue<&TopShot.Collection>(/storage/MomentCollection), 25 | at: /public/MomentCollection 26 | ) 27 | 28 | // Create a PackNFT collection in the signer account if it doesn't already have one 29 | if acct.storage.borrow<&PackNFT.Collection>(from: PackNFT.CollectionStoragePath) == nil { 30 | acct.storage.save(<- PackNFT.createEmptyCollection(), to: PackNFT.CollectionStoragePath); 31 | } 32 | 33 | // Create collection public capability if it doesn't already exist 34 | acct.capabilities.unpublish(PackNFT.CollectionPublicPath) 35 | acct.capabilities.publish( 36 | acct.capabilities.storage.issue<&PackNFT.Collection>(PackNFT.CollectionStoragePath), 37 | at: PackNFT.CollectionPublicPath 38 | ) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /evm-bridging/cadence/scripts/is_erc721_wrapped.cdc: -------------------------------------------------------------------------------- 1 | import "EVM" 2 | 3 | /// Returns true if the NFT is wrapped in the underlying ERC721 contract 4 | /// 5 | /// @param flowAccountWithCoa - A Flow account with a COA 6 | /// @param nftID - The ID of the NFT 7 | /// @param underlying - The address of the underlying ERC721 contract 8 | /// @param wrapper - The address of the wrapper ERC721 contract 9 | /// 10 | access(all) fun main(flowAccountWithCoa: Address, nftID: UInt64, underlying: String, wrapper: String): Bool { 11 | let coa = getAuthAccount(flowAccountWithCoa) 12 | .storage.borrow(from: /storage/evm) 13 | ?? panic("No COA found in signer's account") 14 | 15 | return isNFTWrapped(coa, nftID: nftID, underlying: EVM.addressFromString(underlying), wrapper: EVM.addressFromString(wrapper)) 16 | } 17 | 18 | /// Checks if the provided NFT is wrapped in the underlying ERC721 contract 19 | /// 20 | access(all) fun isNFTWrapped( 21 | _ coa: auth(EVM.Call) &EVM.CadenceOwnedAccount, 22 | nftID: UInt64, 23 | underlying: EVM.EVMAddress, 24 | wrapper: EVM.EVMAddress 25 | ): Bool { 26 | let res = coa.call( 27 | to: underlying, 28 | data: EVM.encodeABIWithSignature("ownerOf(uint256)", [nftID]), 29 | gasLimit: 100_000, 30 | value: EVM.Balance(attoflow: 0) 31 | ) 32 | 33 | // If the call fails, return false 34 | if res.status != EVM.Status.successful { 35 | return false 36 | } 37 | 38 | // Decode and compare the addresses 39 | let decodedResult = EVM.decodeABI( 40 | types: [Type()], 41 | data: res.data 42 | ) 43 | assert(decodedResult.length == 1, message: "Invalid response length") 44 | let owner = decodedResult[0] as! EVM.EVMAddress 45 | return owner.toString() == wrapper.toString() 46 | } 47 | -------------------------------------------------------------------------------- /transactions/scripts/collections/get_setplays_are_owned.cdc: -------------------------------------------------------------------------------- 1 | import TopShot from 0xTOPSHOTADDRESS 2 | 3 | // This script checks whether for each SetID/PlayID combo, 4 | // they own a moment matching that SetPlay. 5 | 6 | // Parameters: 7 | // 8 | // account: The Flow Address of the account whose moment data needs to be read 9 | // setIDs: A list of unique IDs for the sets whose data needs to be read 10 | // playIDs: A list of unique IDs for the plays whose data needs to be read 11 | 12 | // Returns: Bool 13 | // Whether for each SetID/PlayID combo, 14 | // account owns a moment matching that SetPlay. 15 | 16 | access(all) fun main(account: Address, setIDs: [UInt32], playIDs: [UInt32]): Bool { 17 | 18 | assert( 19 | setIDs.length == playIDs.length, 20 | message: "set and play ID arrays have mismatched lengths" 21 | ) 22 | 23 | let collectionRef = getAccount(account).capabilities.borrow<&{TopShot.MomentCollectionPublic}>(/public/MomentCollection) 24 | ?? panic("Could not get public moment collection reference") 25 | 26 | let momentIDs = collectionRef.getIDs() 27 | 28 | // For each SetID/PlayID combo, loop over each moment in the account 29 | // to see if they own a moment matching that SetPlay. 30 | var i = 0 31 | 32 | while i < setIDs.length { 33 | var hasMatchingMoment = false 34 | for momentID in momentIDs { 35 | let token = collectionRef.borrowMoment(id: momentID) 36 | ?? panic("Could not borrow a reference to the specified moment") 37 | 38 | let momentData = token.data 39 | if momentData.setID == setIDs[i] && momentData.playID == playIDs[i] { 40 | hasMatchingMoment = true 41 | break 42 | } 43 | } 44 | if !hasMatchingMoment { 45 | return false 46 | } 47 | i = i + 1 48 | } 49 | 50 | return true 51 | } -------------------------------------------------------------------------------- /lib/go/templates/fastbreak_script_templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "github.com/dapperlabs/nba-smart-contracts/lib/go/templates/internal/assets" 5 | ) 6 | 7 | const ( 8 | fastBreakScriptsPath = "../../../transactions/fastbreak/scripts/" 9 | 10 | getFastBreakByIdFilename = "get_fast_break.cdc" 11 | getFastBreakTokenCountFilename = "get_token_count.cdc" 12 | getScoreByPlayerFilename = "get_player_score.cdc" 13 | getFastBreakStatsFilename = "get_fast_break_stats.cdc" 14 | fastBreakCurrentPlayer = "get_current_player.cdc" 15 | getPlayerWinCountForRunFilename = "get_player_win_count_for_run.cdc" 16 | ) 17 | 18 | func GenerateGetFastBreakScript(env Environment) []byte { 19 | code := assets.MustAssetString(fastBreakScriptsPath + getFastBreakByIdFilename) 20 | 21 | return []byte(replaceAddresses(code, env)) 22 | } 23 | 24 | func GenerateGetFastBreakTokenCountScript(env Environment) []byte { 25 | code := assets.MustAssetString(fastBreakScriptsPath + getFastBreakTokenCountFilename) 26 | 27 | return []byte(replaceAddresses(code, env)) 28 | } 29 | 30 | func GenerateGetPlayerScoreScript(env Environment) []byte { 31 | code := assets.MustAssetString(fastBreakScriptsPath + getScoreByPlayerFilename) 32 | 33 | return []byte(replaceAddresses(code, env)) 34 | } 35 | 36 | func GenerateGetFastBreakStatsScript(env Environment) []byte { 37 | code := assets.MustAssetString(fastBreakScriptsPath + getFastBreakStatsFilename) 38 | 39 | return []byte(replaceAddresses(code, env)) 40 | } 41 | 42 | func GenerateCurrentPlayerScript(env Environment) []byte { 43 | code := assets.MustAssetString(fastBreakScriptsPath + fastBreakCurrentPlayer) 44 | 45 | return []byte(replaceAddresses(code, env)) 46 | } 47 | 48 | func GenerateGetPlayerWinCountForRunScript(env Environment) []byte { 49 | code := assets.MustAssetString(fastBreakScriptsPath + getPlayerWinCountForRunFilename) 50 | 51 | return []byte(replaceAddresses(code, env)) 52 | } 53 | -------------------------------------------------------------------------------- /lib/go/contracts/contracts_test.go: -------------------------------------------------------------------------------- 1 | package contracts_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/dapperlabs/nba-smart-contracts/lib/go/contracts" 9 | ) 10 | 11 | var addrA = "0A" 12 | var addrB = "0B" 13 | var addrC = "0C" 14 | var addrD = "0D" 15 | var addrE = "0E" 16 | var addrF = "0F" 17 | var addrG = "0G" 18 | var network = "mainnet" 19 | var flowEvmContractAddr = "0x1234565789012345657890123456578901234565" 20 | var evmBaseURI = "https://base.uri/moment/" 21 | 22 | func TestTopShotContract(t *testing.T) { 23 | contract := contracts.GenerateTopShotContract(addrA, addrA, addrA, addrA, addrA, addrA, addrA, addrA, network, flowEvmContractAddr, evmBaseURI) 24 | assert.NotNil(t, contract) 25 | } 26 | 27 | func TestTopShotShardedCollectionContract(t *testing.T) { 28 | contract := contracts.GenerateTopShotShardedCollectionContract(addrA, addrB, addrC) 29 | assert.NotNil(t, contract) 30 | assert.Contains(t, string(contract), addrA) 31 | } 32 | 33 | func TestTopShotAdminReceiverContract(t *testing.T) { 34 | contract := contracts.GenerateTopshotAdminReceiverContract(addrA, addrB) 35 | assert.NotNil(t, contract) 36 | assert.Contains(t, string(contract), addrA) 37 | } 38 | 39 | func TestTopShotMarketContract(t *testing.T) { 40 | contract := contracts.GenerateTopShotMarketContract(addrA, addrB, addrC, addrD) 41 | assert.NotNil(t, contract) 42 | assert.Contains(t, string(contract), addrA) 43 | } 44 | 45 | func TestTopShotMarketV3Contract(t *testing.T) { 46 | contract := contracts.GenerateTopShotMarketV3Contract(addrA, addrB, addrC, addrD, addrE, addrF, addrG) 47 | assert.NotNil(t, contract) 48 | assert.Contains(t, string(contract), addrA) 49 | } 50 | 51 | func TestFastBreakContract(t *testing.T) { 52 | contract := contracts.GenerateFastBreakContract(addrA, addrB, addrC, addrD) 53 | assert.NotNil(t, contract) 54 | assert.Contains(t, string(contract), addrA) 55 | assert.Contains(t, string(contract), addrB) 56 | } 57 | -------------------------------------------------------------------------------- /transactions/marketV3/create_sale.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import TopShot from 0xTOPSHOTADDRESS 3 | import Market from 0xMARKETADDRESS 4 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 5 | import NonFungibleToken from 0xNFTADDRESS 6 | 7 | // This transaction creates a sale collection and stores it in the signer's account 8 | // It does not put an NFT up for sale 9 | 10 | // Parameters 11 | // 12 | // beneficiaryAccount: the Flow address of the account where a cut of the purchase will be sent 13 | // cutPercentage: how much in percentage the beneficiary will receive from the sale 14 | 15 | transaction(tokenReceiverPath: PublicPath, beneficiaryAccount: Address, cutPercentage: UFix64) { 16 | prepare(acct: auth(Storage, Capabilities) &Account) { 17 | let ownerCapability = acct.capabilities.get<&{FungibleToken.Receiver}>(tokenReceiverPath)! 18 | 19 | let beneficiaryCapability = getAccount(beneficiaryAccount).capabilities.get<&{FungibleToken.Receiver}>(tokenReceiverPath)! 20 | 21 | let ownerCollection = acct.capabilities.storage.issue(/storage/MomentCollection) 22 | 23 | let collection <- TopShotMarketV3.createSaleCollection(ownerCollection: ownerCollection, 24 | ownerCapability: ownerCapability, 25 | beneficiaryCapability: beneficiaryCapability, 26 | cutPercentage: cutPercentage, 27 | marketV1Capability: nil) 28 | 29 | acct.storage.save(<-collection, to: TopShotMarketV3.marketStoragePath) 30 | 31 | acct.capabilities.publish( 32 | acct.capabilities.storage.issue<&TopShotMarketV3.SaleCollection>(TopShotMarketV3.marketStoragePath), 33 | at: TopShotMarketV3.marketPublicPath 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/go/events/subedition_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/onflow/cadence" 9 | jsoncdc "github.com/onflow/cadence/encoding/json" 10 | "github.com/onflow/cadence/runtime/tests/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCadenceEvents_SubeditionCreated(t *testing.T) { 15 | var ( 16 | id = uint32(1234) 17 | name = "Subedition #1" 18 | setKey = "setID" 19 | setValue = "1234" 20 | playKey = "playID" 21 | playValue = "1234" 22 | ) 23 | 24 | subeditionCreatedEventType := cadence.NewEventType( 25 | utils.TestLocation, 26 | "TopShot.SubeditionCreated", 27 | []cadence.Field{ 28 | { 29 | Identifier: "subeditionID", 30 | Type: cadence.UInt32Type, 31 | }, 32 | { 33 | Identifier: "name", 34 | Type: cadence.StringType, 35 | }, 36 | { 37 | Identifier: "metadata", 38 | Type: &cadence.DictionaryType{}, 39 | }, 40 | }, 41 | nil, 42 | ) 43 | 44 | subeditionCreatedEvent := cadence.NewEvent([]cadence.Value{ 45 | cadence.NewUInt32(id), 46 | NewCadenceString(name), 47 | cadence.NewDictionary([]cadence.KeyValuePair{ 48 | {Key: NewCadenceString(setKey), Value: NewCadenceString(setValue)}, 49 | {Key: NewCadenceString(playKey), Value: NewCadenceString(playValue)}, 50 | }), 51 | }).WithType(subeditionCreatedEventType) 52 | 53 | payload, err := jsoncdc.Encode(subeditionCreatedEvent) 54 | require.NoError(t, err, "failed to encode play created cadence event") 55 | 56 | decodedSubeditionCreatedEventType, err := DecodeSubeditionCreatedEvent(payload) 57 | require.NoError(t, err, "failed to decode play created cadence event") 58 | 59 | assert.Equal(t, id, decodedSubeditionCreatedEventType.SubeditionId()) 60 | assert.Equal(t, name, decodedSubeditionCreatedEventType.Name()) 61 | assert.Equal(t, map[string]interface{}{ 62 | setKey: setValue, 63 | playKey: playValue, 64 | }, decodedSubeditionCreatedEventType.MetaData()) 65 | } 66 | -------------------------------------------------------------------------------- /transactions/marketV3/purchase_moment.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import DapperUtilityCoin from 0xDUCADDRESS 3 | import TopShot from 0xTOPSHOTADDRESS 4 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 5 | 6 | // This transaction is for a user to purchase a moment that another user 7 | // has for sale in their sale collection 8 | 9 | // Parameters 10 | // 11 | // sellerAddress: the Flow address of the account issuing the sale of a moment 12 | // tokenID: the ID of the moment being purchased 13 | // purchaseAmount: the amount for which the user is paying for the moment; must not be less than the moment's price 14 | 15 | transaction(sellerAddress: Address, tokenID: UInt64, purchaseAmount: UFix64) { 16 | prepare(acct: auth(BorrowValue) &Account) { 17 | 18 | // borrow a reference to the signer's collection 19 | let collection = acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) 20 | ?? panic("Could not borrow reference to the Moment Collection") 21 | 22 | // borrow a reference to the signer's fungible token Vault 23 | let provider = acct.storage.borrow(from: /storage/dapperUtilityCoinVault)! 24 | 25 | // withdraw tokens from the signer's vault 26 | let tokens <- provider.withdraw(amount: purchaseAmount) as! @DapperUtilityCoin.Vault 27 | 28 | // get the seller's public account object 29 | let seller = getAccount(sellerAddress) 30 | 31 | // borrow a public reference to the seller's sale collection 32 | let topshotSaleCollection = seller.capabilities.borrow<&TopShotMarketV3.SaleCollection>(/public/topshotSalev3Collection) 33 | ?? panic("Could not borrow public sale reference") 34 | 35 | // purchase the moment 36 | let purchasedToken <- topshotSaleCollection.purchase(tokenID: tokenID, buyTokens: <-tokens) 37 | 38 | // deposit the purchased moment into the signer's collection 39 | collection.deposit(token: <-purchasedToken) 40 | } 41 | } -------------------------------------------------------------------------------- /lib/go/events/moment_minted_test.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | 7 | "github.com/onflow/cadence" 8 | jsoncdc "github.com/onflow/cadence/encoding/json" 9 | "github.com/onflow/cadence/runtime/tests/utils" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCadenceEvents_MomentMinted(t *testing.T) { 14 | momentID := uint64(1234) 15 | playID := uint32(1234) 16 | setID := uint32(1234) 17 | serialNumber := uint32(1234) 18 | subeditionID := uint32(1234) 19 | 20 | momentMintedEventType := cadence.NewEventType( 21 | utils.TestLocation, 22 | "TopShot.MomentMinted", 23 | []cadence.Field{ 24 | { 25 | Identifier: "momentID", 26 | Type: cadence.UInt64Type, 27 | }, 28 | { 29 | Identifier: "playID", 30 | Type: cadence.UInt32Type, 31 | }, 32 | { 33 | Identifier: "setID", 34 | Type: cadence.UInt32Type, 35 | }, 36 | { 37 | Identifier: "serialNumber", 38 | Type: cadence.UInt32Type, 39 | }, 40 | { 41 | Identifier: "subeditionID", 42 | Type: cadence.UInt32Type, 43 | }, 44 | }, 45 | nil, 46 | ) 47 | 48 | momentMintedEvent := cadence.NewEvent([]cadence.Value{ 49 | cadence.NewUInt64(momentID), 50 | cadence.NewUInt32(playID), 51 | cadence.NewUInt32(setID), 52 | cadence.NewUInt32(serialNumber), 53 | cadence.NewUInt32(subeditionID), 54 | }).WithType(momentMintedEventType) 55 | 56 | payload, err := jsoncdc.Encode(momentMintedEvent) 57 | require.NoError(t, err, "failed to encode moment minted cadence event") 58 | 59 | decodedMomentMintedEventType, err := DecodeMomentMintedEvent(payload) 60 | require.NoError(t, err, "failed to decode moment minted cadence event") 61 | 62 | assert.Equal(t, momentID, decodedMomentMintedEventType.MomentId()) 63 | assert.Equal(t, playID, decodedMomentMintedEventType.PlayId()) 64 | assert.Equal(t, setID, decodedMomentMintedEventType.SetId()) 65 | assert.Equal(t, serialNumber, decodedMomentMintedEventType.SerialNumber()) 66 | assert.Equal(t, subeditionID, decodedMomentMintedEventType.SubeditionId()) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /transactions/market/mint_and_purchase.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import DapperUtilityCoin from 0xDUCADDRESS 3 | import TopShot from 0xTOPSHOTADDRESS 4 | import Market from 0xMARKETADDRESS 5 | 6 | // This transaction mints DapperUtilityCoin (a Fungible Token) to self, 7 | // then purchases a moment for sale from a seller 8 | // then deposits bought moment to a recipient 9 | 10 | // Parameters: 11 | // 12 | // sellerAddress: the Flow address of the account issuing the sale of a moment 13 | // recipient: the Flow address who will receive the moment 14 | // tokenID: the ID of the moment being purchased 15 | // purchaseAmount: the amount for which the user is paying for the moment; must not be less than the moment's price 16 | 17 | transaction(sellerAddress: Address, recipient: Address, tokenID: UInt64, purchaseAmount: UFix64) { 18 | 19 | // Local variable for the coin admin 20 | let ducRef: &DapperUtilityCoin.Minter 21 | 22 | prepare(signer: auth(Storage, Capabilities) &Account) { 23 | 24 | self.ducRef = signer.storage.borrow<&DapperUtilityCoin.Minter>(from: /storage/dapperUtilityCoinAdmin) 25 | ?? panic("Signer is not the token admin") 26 | } 27 | 28 | execute { 29 | 30 | let mintedVault <- self.ducRef.mintTokens(amount: purchaseAmount) as! @DapperUtilityCoin.Vault 31 | 32 | 33 | let seller = getAccount(sellerAddress) 34 | 35 | let topshotSaleCollection = seller.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 36 | ?? panic("Could not borrow public sale reference") 37 | 38 | let boughtToken <- topshotSaleCollection.purchase(tokenID: tokenID, buyTokens: <-mintedVault) 39 | 40 | // get the recipient's public account object and borrow a reference to their moment receiver 41 | let recipient = getAccount(recipient).capabilities.borrow<&TopShot.Collection>(/public/MomentCollection) 42 | ?? panic("Could not borrow a reference to the moment collection") 43 | 44 | // deposit the NFT in the receivers collection 45 | recipient.deposit(token: <-boughtToken) 46 | } 47 | } -------------------------------------------------------------------------------- /transactions/user/setup_switchboard_account.cdc: -------------------------------------------------------------------------------- 1 | import FungibleTokenSwitchboard from 0xFUNGIBLETOKENSWITCHBOARDADDRESS 2 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 3 | 4 | // This transaction is a template for a transaction that could be used by 5 | // anyone to to add a Switchboard resource to their account so that they can 6 | // receive multiple fungible tokens using a single {FungibleToken.Receiver} 7 | transaction { 8 | 9 | prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) { 10 | 11 | // Check if the account already has a Switchboard resource, return early if so 12 | if signer.storage.borrow<&FungibleTokenSwitchboard.Switchboard>(from: FungibleTokenSwitchboard.StoragePath) != nil { 13 | return 14 | } 15 | 16 | // Create a new Switchboard resource and put it into storage 17 | signer.storage.save( 18 | <- FungibleTokenSwitchboard.createSwitchboard(), 19 | to: FungibleTokenSwitchboard.StoragePath 20 | ) 21 | 22 | // Clear existing Capabilities at canonical paths 23 | signer.capabilities.unpublish(FungibleTokenSwitchboard.ReceiverPublicPath) 24 | signer.capabilities.unpublish(FungibleTokenSwitchboard.PublicPath) 25 | 26 | // Create a public capability to the Switchboard exposing the deposit 27 | // function through the {FungibleToken.Receiver} interface 28 | let receiverCap = signer.capabilities.storage.issue<&{FungibleToken.Receiver}>( 29 | FungibleTokenSwitchboard.StoragePath 30 | ) 31 | signer.capabilities.publish(receiverCap, at: FungibleTokenSwitchboard.ReceiverPublicPath) 32 | 33 | // Create a public capability to the Switchboard exposing both the 34 | // {FungibleTokenSwitchboard.SwitchboardPublic} and the 35 | // {FungibleToken.Receiver} interfaces 36 | let switchboardPublicCap = signer.capabilities.storage.issue<&{FungibleTokenSwitchboard.SwitchboardPublic, FungibleToken.Receiver}>( 37 | FungibleTokenSwitchboard.StoragePath 38 | ) 39 | signer.capabilities.publish(switchboardPublicCap, at: FungibleTokenSwitchboard.PublicPath) 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/tests/test_uint_array_encoding.cdc: -------------------------------------------------------------------------------- 1 | import "EVM" 2 | 3 | transaction(evmContractAddress: String) { 4 | let coa: auth(EVM.Call) &EVM.CadenceOwnedAccount 5 | 6 | prepare(signer: auth(SaveValue, BorrowValue, Capabilities) &Account) { 7 | // Retrieve or create COA 8 | if let coa = signer.storage.borrow(from: /storage/evm) { 9 | self.coa = coa 10 | } else { 11 | signer.storage.save<@EVM.CadenceOwnedAccount>(<- EVM.createCadenceOwnedAccount(), to: /storage/evm) 12 | signer.capabilities.publish( 13 | signer.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(/storage/evm), 14 | at: /public/evm 15 | ) 16 | self.coa = signer.storage.borrow(from: /storage/evm)! 17 | } 18 | } 19 | 20 | execute { 21 | // Test array with various values including edge cases (min and max uint64 values) 22 | let testArray: [UInt64] = [0, 1, 999999, 18446744073709551615] 23 | 24 | // Encode and call 25 | let res = self.coa.call( 26 | to: EVM.addressFromString(evmContractAddress), 27 | data: EVM.encodeABIWithSignature( 28 | "testArrayEncoding(uint256[])", 29 | [testArray] 30 | ), 31 | gasLimit: 100_000, 32 | value: EVM.Balance(attoflow: 0) 33 | ) 34 | 35 | assert(res.status == EVM.Status.successful, message: "Call failed") 36 | 37 | // Decode and verify 38 | let decoded = EVM.decodeABI(types: [Type<[UInt64]>()], data: res.data) 39 | let returnedArray = decoded[0] as! [UInt64] 40 | 41 | // Compare arrays 42 | assert(testArray.length == returnedArray.length, message: "Array length mismatch") 43 | for i, value in testArray { 44 | assert(value == returnedArray[i], 45 | message: "Mismatch at index ".concat(i.toString()) 46 | .concat(": expected ").concat(value.toString()) 47 | .concat(", got ").concat(returnedArray[i].toString()) 48 | ) 49 | } 50 | 51 | log("Array encoding test passed!") 52 | } 53 | } -------------------------------------------------------------------------------- /transactions/market/purchase_moment.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import DapperUtilityCoin from 0xDUCADDRESS 3 | import TopShot from 0xTOPSHOTADDRESS 4 | import Market from 0xMARKETADDRESS 5 | 6 | // This transaction is for a user to purchase a moment that another user 7 | // has for sale in their sale collection 8 | 9 | // Parameters 10 | // 11 | // sellerAddress: the Flow address of the account issuing the sale of a moment 12 | // tokenID: the ID of the moment being purchased 13 | // purchaseAmount: the amount for which the user is paying for the moment; must not be less than the moment's price 14 | 15 | transaction(sellerAddress: Address, tokenID: UInt64, purchaseAmount: UFix64) { 16 | 17 | // Local variables for the topshot collection object and token provider 18 | let collectionRef: &TopShot.Collection 19 | let providerRef: auth(FungibleToken.Withdraw) &DapperUtilityCoin.Vault 20 | 21 | prepare(acct: auth(Storage, Capabilities) &Account) { 22 | 23 | // borrow a reference to the signer's collection 24 | self.collectionRef = acct.storage.borrow<&TopShot.Collection>(from: /storage/MomentCollection) 25 | ?? panic("Could not borrow reference to the Moment Collection") 26 | 27 | // borrow a reference to the signer's fungible token Vault 28 | self.providerRef = acct.storage.borrow(from: /storage/dapperUtilityCoinVault)! 29 | } 30 | 31 | execute { 32 | 33 | // withdraw tokens from the signer's vault 34 | let tokens <- self.providerRef.withdraw(amount: purchaseAmount) as! @DapperUtilityCoin.Vault 35 | 36 | // get the seller's public account object 37 | let seller = getAccount(sellerAddress) 38 | 39 | // borrow a public reference to the seller's sale collection 40 | let topshotSaleCollection = seller.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) 41 | ?? panic("Could not borrow public sale reference") 42 | 43 | // purchase the moment 44 | let purchasedToken <- topshotSaleCollection.purchase(tokenID: tokenID, buyTokens: <-tokens) 45 | 46 | // deposit the purchased moment into the signer's collection 47 | self.collectionRef.deposit(token: <-purchasedToken) 48 | } 49 | } -------------------------------------------------------------------------------- /evm-bridging/src/lib/CrossVMBridgeCallableUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity 0.8.24; 3 | 4 | import {ICrossVMBridgeCallable} from "../interfaces/ICrossVMBridgeCallable.sol"; 5 | import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; 6 | import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; 7 | 8 | /** 9 | * @title CrossVMBridgeCallable 10 | * @dev A base contract intended for use in implementations on Flow, allowing a contract to define 11 | * access to the Cadence X EVM bridge on certain methods. 12 | */ 13 | abstract contract CrossVMBridgeCallableUpgradeable is ICrossVMBridgeCallable, ContextUpgradeable, ERC165Upgradeable { 14 | 15 | address private _vmBridgeAddress; 16 | 17 | /** 18 | * @dev Sets the bridge EVM address such that only the bridge COA can call the privileged methods 19 | */ 20 | function _init_vm_bridge_address(address vmBridgeAddress_) internal { 21 | if (vmBridgeAddress_ == address(0)) { 22 | revert CrossVMBridgeCallableZeroInitialization(); 23 | } 24 | _vmBridgeAddress = vmBridgeAddress_; 25 | } 26 | 27 | /** 28 | * @dev Modifier restricting access to the designated VM bridge EVM address 29 | */ 30 | modifier onlyVMBridge() { 31 | _checkVMBridgeAddress(); 32 | _; 33 | } 34 | 35 | /** 36 | * @dev Returns the designated VM bridge’s EVM address 37 | */ 38 | function vmBridgeAddress() public view virtual returns (address) { 39 | return _vmBridgeAddress; 40 | } 41 | 42 | /** 43 | * @dev Checks that msg.sender is the designated VM bridge address 44 | */ 45 | function _checkVMBridgeAddress() internal view virtual { 46 | if (_vmBridgeAddress != _msgSender()) { 47 | revert CrossVMBridgeCallableUnauthorizedAccount(_msgSender()); 48 | } 49 | } 50 | 51 | /** 52 | * @dev Allows a caller to determine the contract conforms to the `ICrossVMFulfillment` interface 53 | */ 54 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable) returns (bool) { 55 | return interfaceId == type(ICrossVMBridgeCallable).interfaceId || super.supportsInterface(interfaceId); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /evm-bridging/src/interfaces/ICrossVMBridgeERC721Fulfillment.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.24; 2 | 3 | import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; 4 | 5 | /** 6 | * @title ICrossVMBridgeERC721Fulfillment 7 | * @dev Related to https://github.com/onflow/flips/issues/318[FLIP-318] Cross VM NFT implementations 8 | * on Flow in the context of Cadence-native NFTs. The following interface must be implemented to 9 | * integrate with the Flow VM bridge connecting Cadence & EVM implementations so that the canonical 10 | * VM bridge may move the Cadence NFT into EVM in a mint/escrow pattern. 11 | */ 12 | interface ICrossVMBridgeERC721Fulfillment is IERC165 { 13 | 14 | // Encountered when attempting to fulfill a token that has been previously minted and is not 15 | // escrowed in EVM under the VM bridge 16 | error FulfillmentFailedTokenNotEscrowed(uint256 id, address escrowAddress); 17 | 18 | // Emitted when an NFT is moved from Cadence into EVM 19 | event FulfilledToEVM(address indexed recipient, uint256 indexed tokenId); 20 | 21 | /** 22 | * @dev Returns whether the token is currently escrowed under custody of the designated VM bridge 23 | * 24 | * @param _id the ID of the token in question 25 | */ 26 | function isEscrowed(uint256 _id) external view returns (bool); 27 | 28 | function exists(uint256 _id) external view returns (bool); 29 | 30 | /** 31 | * @dev Fulfills the bridge request, minting (if non-existent) or transferring (if escrowed) the 32 | * token with the given ID to the provided address. For dynamic metadata handling between 33 | * Cadence & EVM, implementations should override and assign metadata as encoded from Cadence 34 | * side. If overriding, be sure to preserve the mint/escrow pattern as shown in the default 35 | * implementation. See `_beforeFulfillment` and `_afterFulfillment` hooks to enable pre-and/or 36 | * post-processing without the need to override this function. 37 | * 38 | * @param _to address of the token recipient 39 | * @param _id the id of the token being moved into EVM from Cadence 40 | * @param _data any encoded metadata passed by the corresponding Cadence NFT at the time of 41 | * bridging into EVM 42 | */ 43 | function fulfillToEVM(address _to, uint256 _id, bytes memory _data) external; 44 | } 45 | -------------------------------------------------------------------------------- /lib/go/events/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dapperlabs/nba-smart-contracts/lib/go/events 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/onflow/cadence v1.0.0-preview.42 7 | github.com/onflow/flow-go-sdk v1.0.0-preview.45 8 | github.com/pkg/errors v0.9.1 9 | github.com/stretchr/testify v1.9.0 10 | ) 11 | 12 | require ( 13 | github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc // indirect 14 | github.com/bits-and-blooms/bitset v1.10.0 // indirect 15 | github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 18 | github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c // indirect 19 | github.com/fxamacker/circlehash v0.3.0 // indirect 20 | github.com/golang/protobuf v1.5.4 // indirect 21 | github.com/holiman/uint256 v1.2.4 // indirect 22 | github.com/k0kubun/pp v3.0.1+incompatible // indirect 23 | github.com/klauspost/cpuid/v2 v2.2.6 // indirect 24 | github.com/kr/pretty v0.3.1 // indirect 25 | github.com/kr/text v0.2.0 // indirect 26 | github.com/logrusorgru/aurora/v4 v4.0.0 // indirect 27 | github.com/mattn/go-colorable v0.1.13 // indirect 28 | github.com/mattn/go-isatty v0.0.20 // indirect 29 | github.com/onflow/atree v0.8.0-rc.5 // indirect 30 | github.com/onflow/crypto v0.25.1 // indirect 31 | github.com/onflow/flow/protobuf/go/flow v0.4.3 // indirect 32 | github.com/onflow/go-ethereum v1.13.4 // indirect 33 | github.com/pmezard/go-difflib v1.0.0 // indirect 34 | github.com/rivo/uniseg v0.4.4 // indirect 35 | github.com/rogpeppe/go-internal v1.10.0 // indirect 36 | github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c // indirect 37 | github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d // indirect 38 | github.com/x448/float16 v0.8.4 // indirect 39 | github.com/zeebo/blake3 v0.2.3 // indirect 40 | go.opentelemetry.io/otel v1.24.0 // indirect 41 | go.uber.org/goleak v1.2.1 // indirect 42 | golang.org/x/crypto v0.19.0 // indirect 43 | golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect 44 | golang.org/x/sys v0.17.0 // indirect 45 | golang.org/x/text v0.14.0 // indirect 46 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 47 | gonum.org/v1/gonum v0.14.0 // indirect 48 | google.golang.org/protobuf v1.33.0 // indirect 49 | gopkg.in/yaml.v3 v3.0.1 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/admin/set_up_royalty_management.cdc: -------------------------------------------------------------------------------- 1 | import "EVM" 2 | 3 | /// Sets up royalty management for an ERC721 contract 4 | /// 5 | /// @param erc721C - The EVM address of the ERC721 contract 6 | /// @param validator - The EVM address of the validator contract 7 | /// @param royaltyRecipient - The EVM address of the royalty recipient 8 | /// @param royaltyBasisPoints - The royalty basis points (0-10000) 9 | transaction( 10 | erc721C: String, 11 | validator: String, 12 | royaltyRecipient: String, 13 | royaltyBasisPoints: UInt128, 14 | ) { 15 | prepare(signer: auth(BorrowValue) &Account) { 16 | // Borrow COA from signer's account storage 17 | let coa = signer.storage.borrow(from: /storage/evm) 18 | ?? panic("Could not find coa in signer's account.") 19 | 20 | // Set validator contract 21 | mustCall(coa, EVM.addressFromString(erc721C), 22 | functionSig: "setTransferValidator(address)", 23 | args: [EVM.addressFromString(validator)] 24 | ) 25 | 26 | // Set royalty info 27 | mustCall(coa, EVM.addressFromString(erc721C), 28 | functionSig: "setRoyaltyInfo((address,uint96))", 29 | args: [EVM.addressFromString(royaltyRecipient), royaltyBasisPoints] 30 | ) 31 | } 32 | } 33 | 34 | /// Calls a function on an EVM contract from provided coa 35 | /// 36 | access(all) fun mustCall( 37 | _ coa: auth(EVM.Call) &EVM.CadenceOwnedAccount, 38 | _ contractAddr: EVM.EVMAddress, 39 | functionSig: String, 40 | args: [AnyStruct] 41 | ): EVM.Result { 42 | let res = coa.call( 43 | to: contractAddr, 44 | data: EVM.encodeABIWithSignature(functionSig, args), 45 | gasLimit: 4_000_000, 46 | value: EVM.Balance(attoflow: 0) 47 | ) 48 | 49 | assert(res.status == EVM.Status.successful, 50 | message: "Failed to call '".concat(functionSig) 51 | .concat("\n\t error code: ").concat(res.errorCode.toString()) 52 | .concat("\n\t error message: ").concat(res.errorMessage) 53 | .concat("\n\t gas used: ").concat(res.gasUsed.toString()) 54 | .concat("\n\t args count: ").concat(args.length.toString()) 55 | .concat("\n\t caller address: 0x").concat(coa.address().toString()) 56 | .concat("\n\t contract address: 0x").concat(contractAddr.toString()) 57 | ) 58 | 59 | return res 60 | } 61 | -------------------------------------------------------------------------------- /evm-bridging/script/InitialTestingDeploy.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.24; 3 | 4 | import {Script} from "forge-std/Script.sol"; 5 | import "forge-std/console.sol"; 6 | import {Upgrades} from "openzeppelin-foundry-upgrades/src/Upgrades.sol"; 7 | import {TestNFTContract} from "../src/test-contracts/TestNFTContract.sol"; 8 | 9 | contract InitialTestingDeployScript is Script { 10 | function setUp() public {} 11 | 12 | function run() external returns (address, address) { 13 | // Start broadcast with deployer private key 14 | vm.startBroadcast(vm.envUint("DEPLOYER_PRIVATE_KEY")); 15 | console.log("Deployer address:", msg.sender); 16 | 17 | // Set testnet contract initialization parameters 18 | address owner = msg.sender; 19 | string memory name = "Test NFT"; 20 | string memory symbol = "TEST"; 21 | string memory baseTokenURI = "https://api.cryptokitties.co/tokenuri/"; 22 | string memory cadenceNFTAddress = "abcdef1234567890"; 23 | string memory cadenceNFTIdentifier = "A.abcdef1234567890.TestNFT.NFT"; 24 | string memory contractURI = 'data:application/json;utf8,{"name": "Name of NFT","description":"Description of NFT"}'; 25 | address underlyingNftContractAddress = address(0x12345); 26 | address vmBridgeAddress = address(0x67890); 27 | 28 | // Deploy NFT contract using UUPS proxy for upgradeability 29 | address proxyAddr = Upgrades.deployUUPSProxy( 30 | "TestNFTContract.sol", 31 | abi.encodeCall( 32 | TestNFTContract.initialize, 33 | ( 34 | owner, 35 | underlyingNftContractAddress, 36 | vmBridgeAddress, 37 | name, 38 | symbol, 39 | baseTokenURI, 40 | cadenceNFTAddress, 41 | cadenceNFTIdentifier, 42 | contractURI 43 | ) 44 | ) 45 | ); 46 | console.log("Proxy contract deployed at address:", proxyAddr); 47 | 48 | // Get implementation contract address 49 | address implementationAddr = Upgrades.getImplementationAddress( 50 | proxyAddr 51 | ); 52 | console.log("Implementation contract deployed at address:", implementationAddr); 53 | 54 | // Stop broadcast and return implementation and proxy addresses 55 | vm.stopBroadcast(); 56 | return (implementationAddr, proxyAddr); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /transactions/marketV3/purchase_both_markets.cdc: -------------------------------------------------------------------------------- 1 | import FungibleToken from 0xFUNGIBLETOKENADDRESS 2 | import DapperUtilityCoin from 0xDUCADDRESS 3 | import TopShot from 0xTOPSHOTADDRESS 4 | import Market from 0xMARKETADDRESS 5 | import TopShotMarketV3 from 0xMARKETV3ADDRESS 6 | 7 | // This transaction purchases a moment from the v3 sale collection 8 | // The v3 sale collection will also check the v1 collection for for sale moments as part of the purchase 9 | // If there is no v3 sale collection, the transaction will just purchase it from v1 anyway 10 | 11 | transaction(seller: Address, recipient: Address, momentID: UInt64, purchaseAmount: UFix64) { 12 | 13 | let purchaseTokens: @DapperUtilityCoin.Vault 14 | 15 | prepare(acct: auth(BorrowValue) &Account) { 16 | 17 | // Borrow a provider reference to the buyers vault 18 | let provider = acct.storage.borrow(from: /storage/dapperUtilityCoinVault) 19 | ?? panic("Could not borrow a reference to the buyers FlowToken Vault") 20 | 21 | // withdraw the purchase tokens from the vault 22 | self.purchaseTokens <- provider.withdraw(amount: purchaseAmount) as! @DapperUtilityCoin.Vault 23 | 24 | } 25 | 26 | execute { 27 | 28 | // get the accounts for the seller and recipient 29 | let seller = getAccount(seller) 30 | let recipient = getAccount(recipient) 31 | 32 | // Get the reference for the recipient's nft receiver 33 | let receiverRef = recipient.capabilities.borrow<&TopShot.Collection>(/public/MomentCollection) 34 | ?? panic("Could not borrow a reference to the moment collection") 35 | 36 | if let marketV3CollectionRef = seller.capabilities.borrow<&TopShotMarketV3.SaleCollection>(/public/topshotSalev3Collection) { 37 | 38 | let purchasedToken <- marketV3CollectionRef.purchase(tokenID: momentID, buyTokens: <-self.purchaseTokens) 39 | receiverRef.deposit(token: <-purchasedToken) 40 | 41 | } else if let marketV1CollectionRef = seller.capabilities.borrow<&Market.SaleCollection>(/public/topshotSaleCollection) { 42 | // purchase the moment 43 | let purchasedToken <- marketV1CollectionRef.purchase(tokenID: momentID, buyTokens: <-self.purchaseTokens) 44 | 45 | // deposit the purchased moment into the signer's collection 46 | receiverRef.deposit(token: <-purchasedToken) 47 | 48 | } else { 49 | panic("Could not borrow reference to either Sale collection") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /evm-bridging/cadence/transactions/admin/deploy/create_new_account_with_coa_and_flow_deposit.cdc: -------------------------------------------------------------------------------- 1 | import Crypto 2 | import "FungibleToken" 3 | import "FlowToken" 4 | import "EVM" 5 | 6 | /// Creates a new flow account with single full-weight key, initial FLOW tokens funding, and 7 | /// a COA EVM account in storage. 8 | /// 9 | /// @param pubKey: String - public key to be added to the account 10 | /// @param amount: UFix64 - amount of FLOW tokens to be transferred to the new account 11 | /// 12 | transaction(pubKey: String, amount: UFix64) { 13 | let sentVault: @FlowToken.Vault 14 | let receiverRef: &{FungibleToken.Receiver} 15 | 16 | prepare(signer: auth(BorrowValue) &Account) { 17 | // Create new account 18 | let account = Account(payer: signer) 19 | 20 | // Add public key with full weight, SHA2_256, and ECDSA_P256 21 | account.keys.add( 22 | publicKey: PublicKey( 23 | publicKey: pubKey.decodeHex(), 24 | signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 25 | ), 26 | hashAlgorithm: HashAlgorithm.SHA2_256, 27 | weight: 1000.0 28 | ) 29 | 30 | // Set COA paths 31 | let storagePath = StoragePath(identifier: "evm")! 32 | let publicPath = PublicPath(identifier: "evm")! 33 | 34 | // Create and save new COA in new account's storage 35 | account.storage.save<@EVM.CadenceOwnedAccount>(<- EVM.createCadenceOwnedAccount(), to: storagePath) 36 | 37 | // Issue and publish capability to the COA 38 | account.capabilities.unpublish(publicPath) 39 | account.capabilities.publish(account.capabilities.storage.issue<&EVM.CadenceOwnedAccount>(storagePath), at: publicPath) 40 | 41 | // Borrow reference to the signer's FLOW token vault and withdraw provided amount 42 | let vaultRef = signer.storage.borrow( 43 | from: /storage/flowTokenVault 44 | ) ?? panic("Could not borrow reference to the owner's Vault!") 45 | self.sentVault <- vaultRef.withdraw(amount: amount) as! @FlowToken.Vault 46 | 47 | // Borrow reference to the new account's FLOW tokens receiver 48 | self.receiverRef = account.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 49 | ?? panic("Could not borrow a Receiver reference to the FlowToken Vault in account ") 50 | } 51 | 52 | execute { 53 | // Deposit the withdrawn tokens in the new account's receiver 54 | self.receiverRef.deposit(from: <- self.sentVault) 55 | } 56 | } 57 | --------------------------------------------------------------------------------