Skip to content

Commit

Permalink
add better error messages to all of the transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Sep 10, 2024
1 parent 49365e3 commit 6180059
Show file tree
Hide file tree
Showing 28 changed files with 248 additions and 178 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
test:
$(MAKE) generate -C lib/go
$(MAKE) test -C lib/go
flow test --cover --covercode="contracts" tests/*.cdc
flow-c1 test --cover --covercode="contracts" tests/*.cdc

.PHONY: ci
ci:
Expand Down
10 changes: 9 additions & 1 deletion flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,15 @@
"aliases": {
"testing": "0x0000000000000007"
}
}
},
"Burner": {
"source": "./contracts/utility/Burner.cdc",
"aliases": {
"emulator": "0xf8d6e0586b0a20c7",
"testing": "0x0000000000000001",
"testnet": "0x9a0766d93b6608b7"
}
}
},
"networks": {
"emulator": "127.0.0.1:3569",
Expand Down
132 changes: 66 additions & 66 deletions lib/go/templates/internal/assets/assets.go

Large diffs are not rendered by default.

47 changes: 0 additions & 47 deletions lib/go/test/nft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,51 +293,4 @@ func TestTransferNFT(t *testing.T) {
)

})

t.Run("Should be able to withdraw an NFT and destroy it, not reducing the supply", func(t *testing.T) {

idsScript := templates.GenerateGetCollectionIDsScript(nftAddress, exampleNFTAddress)
idsResult := executeScriptAndCheck(
t, b,
idsScript,
[][]byte{
jsoncdc.MustEncode(cadence.NewAddress(joshAddress)),
jsoncdc.MustEncode(cadence.Path{Domain: common.PathDomainPublic, Identifier: pathName}),
},
)
mintedID := idsResult.(cadence.Array).Values[0].(cadence.UInt64)

// This transaction withdraws the specifed NFT from the authorizers account
// and calls `destroy NFT`
script := templates.GenerateDestroyNFTScript(nftAddress, exampleNFTAddress, metadataAddress)

tx := createTxWithTemplateAndAuthorizer(b, script, joshAddress)

// Destroy the only NFT in the collection
tx.AddArgument(mintedID)

signAndSubmit(
t, b, tx,
[]flow.Address{
joshAddress,
},
[]crypto.Signer{
joshSigner,
},
false,
)

// Both collections should now be empty since the only NFT was destroyed

assertCollectionLength(t, b, nftAddress, exampleNFTAddress, metadataAddress,
joshAddress,
0,
)

assertCollectionLength(t, b, nftAddress, exampleNFTAddress, metadataAddress,
exampleNFTAddress,
0,
)

})
}
27 changes: 25 additions & 2 deletions tests/example_nft_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ fun testBorrowMissingNFT() {
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "NFT does not exist in the collection!"
errorMessage: "The NFT with id=10 does not exist in the collection!"
)
}

Expand Down Expand Up @@ -326,7 +326,7 @@ fun testGetMissingContractStoragePath() {
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "contract could not be borrowed"
errorMessage: "Could not borrow ViewResolver reference to the contract. Make sure the provided contract name (ContractOne) and address (0x0000000000000007) are correct!"
)
}

Expand Down Expand Up @@ -430,3 +430,26 @@ fun testResolveExampleNFTViews() {
)
Test.expect(scriptResult, Test.beSucceeded())
}

access(all)
fun testBurnNFT() {
var scriptResult = executeScript(
"../transactions/scripts/get_collection_ids.cdc",
[
admin.address,
/public/exampleNFTCollection
]
)
Test.expect(scriptResult, Test.beSucceeded())

let collectionIDs = scriptResult.returnValue! as! [UInt64]

let txResult = executeTransaction(
"../transactions/destroy_nft.cdc",
[
collectionIDs[0]
],
admin
)
Test.expect(txResult, Test.beSucceeded())
}
2 changes: 1 addition & 1 deletion tests/nft_forwarding_test.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ access(all) fun setup() {

access(all) fun testCreateForwarderFails() {

let expectedErrorMessage = "Recipient is not configured with NFT Collection at the given path"
let expectedErrorMessage = "The Recipient has not configured their account with an NFT Collection at the given public path=/public/exampleNFTCollection"
let expectedErrorType = ErrorType.TX_PANIC

// Setup Collection in forwarder
Expand Down
9 changes: 6 additions & 3 deletions transactions/destroy_nft.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import "NonFungibleToken"
import "MetadataViews"
import "ExampleNFT"
import "Burner"

transaction(id: UInt64) {

Expand All @@ -11,12 +12,14 @@ transaction(id: UInt64) {

prepare(signer: auth(BorrowValue) &Account) {
let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
?? panic("ViewResolver does not resolve NFTCollectionData view")
?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction")

// borrow a reference to the owner's collection
self.collectionRef = signer.storage.borrow<auth(NonFungibleToken.Withdraw) &ExampleNFT.Collection>(
from: collectionData.storagePath
) ?? panic("Account does not store an object at the specified path")
) ?? panic("The signer does not store an ExampleNFT.Collection object at the path "
.concat(collectionData.storagePath.toString())
.concat("The signer must initialize their account with this collection first!"))

}

Expand All @@ -25,7 +28,7 @@ transaction(id: UInt64) {
// withdraw the NFT from the owner's collection
let nft <- self.collectionRef.withdraw(withdrawID: id)

destroy nft
Burner.burn(<-nft)
}

post {
Expand Down
24 changes: 19 additions & 5 deletions transactions/generic_transfer_with_address.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,22 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str
// Borrow a reference to the nft contract deployed to the passed account
let resolverRef = getAccount(contractAddress)
.contracts.borrow<&{NonFungibleToken}>(name: contractName)
?? panic("Could not borrow a reference to the non-fungible token contract")
?? panic("Could not borrow NonFungibleToken reference to the contract. Make sure the provided contract name ("
.concat(contractName).concat(") and address (").concat(contractAddress.toString()).concat(") are correct!"))

// Use that reference to retrieve the NFTCollectionData view
self.collectionData = resolverRef.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
?? panic("Could not resolve the NFTCollectionData view for the given non-fungible token contract")
?? panic("Could not resolve NFTCollectionData view. The ".concat(contractName).concat(" contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction"))


// borrow a reference to the signer's NFT collection
let withdrawRef = signer.storage.borrow<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>(
from: self.collectionData.storagePath
) ?? panic("Account does not store a collection object at the specified path")
) ?? panic("The signer does not store a "
.concat(contractName)
.concat(".Collection object at the path ")
.concat(self.collectionData.storagePath.toString())
.concat("The signer must initialize their account with this collection first!"))

self.tempNFT <- withdrawRef.withdraw(withdrawID: id)

Expand All @@ -56,7 +61,11 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str
let type = CompositeType(typeString)
assert(
type != nil,
message: "Could not create a type out of the contract name and address!"
message: "Could not create a type out of the contract name ("
.concat(contractName)
.concat(") and address (")
.concat(addressString)
.concat(")!")
)

assert(
Expand All @@ -71,7 +80,12 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str

// borrow a public reference to the receivers collection
let receiverRef = recipient.capabilities.borrow<&{NonFungibleToken.Receiver}>(self.collectionData.publicPath)
?? panic("Could not borrow reference to the recipient's receiver")
?? panic("The recipient does not have a NonFungibleToken Receiver at "
.concat(self.collectionData.publicPath.toString())
.concat(" that is capable of receiving a ")
.concat(contractName)
.concat(" NFT.")
.concat("The recipient must initialize their account with this collection and receiver first!"))

// Deposit the NFT to the receiver
receiverRef.deposit(token: <-self.tempNFT)
Expand Down
24 changes: 19 additions & 5 deletions transactions/generic_transfer_with_address_and_type.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,22 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str
// Borrow a reference to the nft contract deployed to the passed account
let resolverRef = getAccount(contractAddress)
.contracts.borrow<&{NonFungibleToken}>(name: contractName)
?? panic("Could not borrow a reference to the non-fungible token contract")
?? panic("Could not borrow NonFungibleToken reference to the contract. Make sure the provided contract name ("
.concat(contractName).concat(") and address (").concat(contractAddress.toString()).concat(") are correct!"))

// Use that reference to retrieve the NFTCollectionData view
self.collectionData = resolverRef.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
?? panic("Could not resolve the NFTCollectionData view for the given non-fungible token contract")
?? panic("Could not resolve NFTCollectionData view. The ".concat(contractName).concat(" contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction"))


// borrow a reference to the signer's NFT collection
let withdrawRef = signer.storage.borrow<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>(
from: self.collectionData.storagePath
) ?? panic("Account does not store a collection object at the specified path")
) ?? panic("The signer does not store a "
.concat(contractName)
.concat(".Collection object at the path ")
.concat(self.collectionData.storagePath.toString())
.concat("The signer must initialize their account with this collection first!"))

self.tempNFT <- withdrawRef.withdraw(withdrawID: id)

Expand All @@ -55,7 +60,11 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str
let type = CompositeType(typeString)
assert(
type != nil,
message: "Could not create a type out of the contract name and address!"
message: "Could not create a type out of the contract name ("
.concat(contractName)
.concat(") and address (")
.concat(addressString)
.concat(")!")
)

assert(
Expand All @@ -70,7 +79,12 @@ transaction(to: Address, id: UInt64, contractAddress: Address, contractName: Str

// borrow a public reference to the receivers collection
let receiverRef = recipient.capabilities.borrow<&{NonFungibleToken.Receiver}>(self.collectionData.publicPath)
?? panic("Could not borrow reference to the recipient's receiver")
?? panic("The recipient does not have a NonFungibleToken Receiver at "
.concat(self.collectionData.publicPath.toString())
.concat(" that is capable of receiving a ")
.concat(contractName)
.concat(" NFT.")
.concat("The recipient must initialize their account with this collection and receiver first!"))

// Deposit the NFT to the receiver
receiverRef.deposit(token: <-self.tempNFT)
Expand Down
13 changes: 10 additions & 3 deletions transactions/generic_transfer_with_paths.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,28 @@ transaction(to: Address, id: UInt64, senderPathIdentifier: String, receiverPathI
// borrow a reference to the signer's NFT collection
let withdrawRef = signer.storage.borrow<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>(
from: storagePath
) ?? panic("Account does not store a collection object at the specified path")
) ?? panic("The signer does not store a {NonFungibleToken.Collection} object at the path "
.concat(storagePath.toString())
.concat("The signer must initialize their account with this collection first!"))

self.tempNFT <- withdrawRef.withdraw(withdrawID: id)
}

execute {
let publicPath = PublicPath(identifier: receiverPathIdentifier)
?? panic("Could not construct a public path from the provided path identifier string")
?? panic("Could not construct a public path from the provided path identifier string ("
.concat(receiverPathIdentifier)
.concat(")"))

// get the recipients public account object
let recipient = getAccount(to)

// borrow a public reference to the receivers collection
let receiverRef = recipient.capabilities.borrow<&{NonFungibleToken.Receiver}>(publicPath)
?? panic("Could not borrow reference to the recipient's receiver")
?? panic("The recipient does not have a NonFungibleToken Receiver at "
.concat(publicPath.toString())
.concat(" that is capable of receiving a NFT.")
.concat("The recipient must initialize their account with this collection and receiver first!"))

// Deposit the NFT to the receiver
receiverRef.deposit(token: <-self.tempNFT)
Expand Down
20 changes: 14 additions & 6 deletions transactions/mint_nft.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@ transaction(
prepare(signer: auth(BorrowValue) &Account) {

let collectionData = ExampleNFT.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as! MetadataViews.NFTCollectionData?
?? panic("ViewResolver does not resolve NFTCollectionData view")
?? panic("Could not resolve NFTCollectionData view. The ExampleNFT contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction")

// borrow a reference to the NFTMinter resource in storage
self.minter = signer.storage.borrow<&ExampleNFT.NFTMinter>(from: ExampleNFT.MinterStoragePath)
?? panic("Account does not store an object at the specified path")
?? panic("The signer does not store an ExampleNFT.Minter object at the path "
.concat(ExampleNFT.MinterStoragePath.toString())
.concat("The signer must initialize their account with this minter resource first!"))

// Borrow the recipient's public NFT collection reference
self.recipientCollectionRef = getAccount(recipient).capabilities.borrow<&{NonFungibleToken.Receiver}>(
collectionData.publicPath
) ?? panic("Could not get receiver reference to the NFT Collection")
self.recipientCollectionRef = getAccount(recipient).capabilities.borrow<&{NonFungibleToken.Receiver}>(collectionData.publicPath)
?? panic("The recipient does not have a NonFungibleToken Receiver at "
.concat(collectionData.publicPath.toString())
.concat(" that is capable of receiving an NFT.")
.concat("The recipient must initialize their account with this collection and receiver first!"))
}

pre {
Expand All @@ -56,7 +60,11 @@ transaction(
)

if !beneficiaryCapability.check() {
panic("Beneficiary does not have Receiver configured at RoyaltyReceiverPublicPath")
panic("The royalty beneficiary "
.concat(beneficiary.toString())
.concat(" does not have a FungibleToken Receiver configured at")
.concat(MetadataViews.getRoyaltyReceiverPublicPath().toString())
.concat(". They should set up a FungibleTokenSwitchboard receiver at this path to receive any type of Fungible Token"))
}

royalties.append(
Expand Down
2 changes: 1 addition & 1 deletion transactions/nft-forwarding/change_forwarder_recipient.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ transaction(newRecipientAddress: Address, collectionPublicPath: PublicPath) {
// borrow reference to NFTForwarder resource
self.forwarderRef = signer.storage.borrow<auth(NFTForwarding.Mutable) &NFTForwarding.NFTForwarder>(
from: NFTForwarding.StoragePath
) ?? panic("Could not borrow reference to NFTForwarder")
) ?? panic("Could not borrow reference to NFTForwarder in the signer's account at path=".concat(NFTForwarding.StoragePath.toString()))

// get Collection Capability from the recipientAddress account
self.newRecipientCollection = getAccount(newRecipientAddress).capabilities.get<&{NonFungibleToken.Collection}>(
Expand Down
2 changes: 1 addition & 1 deletion transactions/nft-forwarding/create_forwarder.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ transaction(recipientAddress: Address, collectionPublicPath: PublicPath) {
)

if !recipientCollectionCap.check() {
panic("Recipient is not configured with NFT Collection at the given path")
panic("The Recipient has not configured their account with an NFT Collection at the given public path=".concat(collectionPublicPath.toString()))
}

// create a new NFTForwarder resource & save in storage, forwarding to the recipient's Collection
Expand Down
18 changes: 14 additions & 4 deletions transactions/nft-forwarding/transfer_nft_to_receiver.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,30 @@ transaction(

// get the collection data from the NFT contract
let nftContract = getAccount(contractAddress).contracts.borrow<&{ViewResolver}>(name: contractName)
?? panic("Could not borrow ViewResolver reference to the contract")
?? panic("Could not borrow ViewResolver reference to the contract. Make sure the provided contract name ("
.concat(contractName).concat(") and address (").concat(contractAddress.toString()).concat(") are correct!"))

let collectionData = nftContract.resolveContractView(resourceType: nil, viewType: Type<MetadataViews.NFTCollectionData>()) as MetadataViews.NFTCollectionData?
?? panic("Could not resolve NFTCollectionData view")
?? panic("Could not resolve NFTCollectionData view. The ".concat(contractName).concat(" contract needs to implement the NFTCollectionData Metadata view in order to execute this transaction"))

// borrow a reference to the signer's NFT collection
self.withdrawRef = signer.storage.borrow<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Collection}>(
from: collectionData.storagePath
) ?? panic("Account does not store an object at the specified path")
) ?? panic("The signer does not store a "
.concat(contractName)
.concat(".Collection object at the path ")
.concat(collectionData.storagePath.toString())
.concat("The signer must initialize their account with this collection first!"))

// borrow a public reference to the recipient's Receiver
self.depositRef = getAccount(recipient).capabilities.borrow<&{NonFungibleToken.Receiver}>(
collectionData.publicPath
) ?? panic("Could not borrow a reference to the recipient's Receiver")
) ?? panic("The recipient does not have a NonFungibleToken Receiver at "
.concat(collectionData.publicPath.toString())
.concat(" that is capable of receiving a ")
.concat(contractName)
.concat(" NFT.")
.concat("The recipient must initialize their account with this collection and receiver first!"))
}

execute {
Expand Down
Loading

0 comments on commit 6180059

Please sign in to comment.