Skip to content

Commit

Permalink
add more descriptive and helpful error messages to the contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Sep 9, 2024
1 parent 0ce4125 commit 49365e3
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 5,492 deletions.
12 changes: 1 addition & 11 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,4 @@ For contributor use:

- [ ] Targeted PR against `master` branch
- [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
- [ ] Code follows the [standards mentioned here](https://github.com/onflow/flow-nft/blob/master/CONTRIBUTING.md#styleguides).
- [ ] Updated relevant documentation
- [ ] Re-reviewed `Files changed` in the Github PR explorer
- [ ] Added appropriate labels
<!-- Please follow the below standard to update the version in package.json
- Major if there is a new smart contract introduced.
- Major if there is a breaking change that is introduced in the existing contract.
- Minor if there is a new feature addition within the existing smart contracts.
- Patch if there is a non breaking change in the existing smart contracts.
-->
- [ ] Update the version in package.json when there is a change in the smart contracts
- [ ] Re-reviewed `Files changed` in the Github PR explorer
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
test:
$(MAKE) generate -C lib/go
$(MAKE) test -C lib/go
flow-c1 test --cover --covercode="contracts" tests/*.cdc
flow test --cover --covercode="contracts" tests/*.cdc

.PHONY: ci
ci:
$(MAKE) ci -C lib/go
flow-c1 test --cover --covercode="contracts" tests/*.cdc
flow test --cover --covercode="contracts" tests/*.cdc
6 changes: 4 additions & 2 deletions contracts/ExampleNFT.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ access(all) contract ExampleNFT: NonFungibleToken {
resourceType: nil,
viewType: Type<MetadataViews.EVMBridgedMetadata>()
) as! MetadataViews.EVMBridgedMetadata?
?? panic("Could not resolve contract-level EVMBridgedMetadata")
?? panic("Could not resolve contract-level EVMBridgedMetadata.")
// Compose the token-level URI based on a base URI and the token ID, pointing to a JSON file. This
// would be a file you've uploaded and are hosting somewhere - in this case HTTP, but this could be
// IPFS, S3, a data URL containing the JSON directly, etc.
Expand Down Expand Up @@ -182,7 +182,9 @@ access(all) contract ExampleNFT: NonFungibleToken {
/// withdraw removes an NFT from the collection and moves it to the caller
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not withdraw an NFT with the provided ID from the collection")
?? panic("ExampleNFT.Collection.withdraw: Could not withdraw an NFT with the ID="
.concat(withdrawID.toString())
.concat(". Check the submitted ID to make sure it is one that this collection owns"))

return <-token
}
Expand Down
32 changes: 27 additions & 5 deletions contracts/MetadataViews.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,11 @@ access(all) contract MetadataViews {

view init(receiver: Capability<&{FungibleToken.Receiver}>, cut: UFix64, description: String) {
pre {
cut >= 0.0 && cut <= 1.0 : "Cut value should be in valid range i.e [0,1]"
cut >= 0.0 && cut <= 1.0 :
"MetadataViews.Royalty.init: The provided royalty cut value ("
.concat(cut.toString())
.concat(") is invalid.")
.concat(" It should be within the valid range between 0 and 1. i.e [0,1]")
}
self.receiver = receiver
self.cut = cut
Expand All @@ -301,7 +305,13 @@ access(all) contract MetadataViews {
for royalty in cutInfos {
totalCut = totalCut + royalty.cut
}
assert(totalCut <= 1.0, message: "Sum of cutInfos multipliers should not be greater than 1.0")
assert(
totalCut <= 1.0,
message:
"MetadataViews.Royalties.init: Sum of cutInfos multipliers ("
.concat(totalCut.toString())
.concat(") should not be greater than 1.0")
)
// Assign the cutInfos
self.cutInfos = cutInfos
}
Expand Down Expand Up @@ -454,7 +464,15 @@ access(all) contract MetadataViews {

view init(name: String?, number: UInt64, max: UInt64?) {
if max != nil {
assert(number <= max!, message: "The number cannot be greater than the max number!")
assert(
number <= max!,
message:
"MetadataViews.Edition.init: The provided edition number for the NFT ("
.concat(number.toString())
.concat(") cannot be greater than the max edition number (")
.concat(max!.toString())
.concat(")!")
)
}
self.name = name
self.number = number
Expand Down Expand Up @@ -535,7 +553,8 @@ access(all) contract MetadataViews {

view init(score: UFix64?, max: UFix64?, description: String?) {
if score == nil && description == nil {
panic("A Rarity needs to set score, description or both")
panic("MetadataViews.Rarity.init: The provided score and description are both `nil`."
.concat(" A Rarity needs to set score, description, or both"))
}

self.score = score
Expand Down Expand Up @@ -648,7 +667,10 @@ access(all) contract MetadataViews {
createEmptyCollectionFunction: fun(): @{NonFungibleToken.Collection}
) {
pre {
publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.Collection}>()): "Public type must be a subtype of NonFungibleToken.Collection interface."
publicLinkedType.isSubtype(of: Type<&{NonFungibleToken.Collection}>()):
"MetadataViews.NFTCollectionData.init: The Public linked type <"
.concat(publicLinkedType.identifier)
.concat("> is incorrect. It must be a subtype of the NonFungibleToken.Collection interface.")
}
self.storagePath=storagePath
self.publicPath=publicPath
Expand Down
39 changes: 32 additions & 7 deletions contracts/NonFungibleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,14 @@ access(all) contract interface NonFungibleToken: ViewResolver {
///
access(all) fun createEmptyCollection(): @{Collection} {
post {
result.getLength() == 0: "The created collection must be empty!"
result.isSupportedNFTType(type: self.getType()): "The created collection must support this NFT type"
result.getLength() == 0:
"NonFungibleToken.NFT.createEmptyCollection: The created collection has a non-zero length."
.concat(" A newly created collection must be empty!")
result.isSupportedNFTType(type: self.getType()):
"NonFungibleToken.NFT.createEmptyCollection: The created collection does not support NFTs of type="
.concat(self.getType().identifier)
.concat(" The collection must support NFTs of type=")
.concat(self.getType().identifier)
}
}

Expand Down Expand Up @@ -156,7 +162,12 @@ access(all) contract interface NonFungibleToken: ViewResolver {
///
access(Withdraw) fun withdraw(withdrawID: UInt64): @{NFT} {
post {
result.id == withdrawID: "The ID of the withdrawn token must be the same as the requested ID"
result.id == withdrawID:
"NonFungibleToken.Provider.withdraw: The ID of the withdrawn token ("
.concat(result.id.toString())
.concat(") must be the same as the requested ID (")
.concat(withdrawID.toString())
.concat(")")
emit Withdrawn(type: result.getType().identifier, id: result.id, uuid: result.uuid, from: self.owner?.address, providerUUID: self.uuid)
}
}
Expand Down Expand Up @@ -235,7 +246,12 @@ access(all) contract interface NonFungibleToken: ViewResolver {
access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? {
post {
(result == nil) || (result?.id == id):
"Cannot borrow NFT reference: The ID of the returned reference does not match the ID that was specified"
"NonFungibleToken.Collection.borrowNFT: Cannot borrow NFT reference: "
.concat("The ID of the returned reference (")
.concat(result!.id.toString())
.concat(") does not match the ID that was specified (")
.concat(id.toString())
.concat(")")
}
}

Expand All @@ -244,8 +260,15 @@ access(all) contract interface NonFungibleToken: ViewResolver {
/// @return A an empty collection of the same type
access(all) fun createEmptyCollection(): @{Collection} {
post {
result.getType() == self.getType(): "The created collection does not have the same type as this collection"
result.getLength() == 0: "The created collection must be empty!"
result.getType() == self.getType():
"NonFungibleToken.Collection.createEmptyCollection: The created collection type <"
.concat(result.getType().identifier)
.concat("> does not have the same type as the collection that was used to create it <")
.concat(self.getType().identifier)
.concat(">")
result.getLength() == 0:
"NonFungibleToken.Collection.createEmptyCollection: The created collection has a non-zero length."
.concat(" A newly created collection must be empty!")
}
}
}
Expand All @@ -256,7 +279,9 @@ access(all) contract interface NonFungibleToken: ViewResolver {
/// @return An array of NFT Types that the implementing contract defines.
access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} {
post {
result.getIDs().length == 0: "The created collection must be empty!"
result.getIDs().length == 0:
"NonFungibleToken.createEmptyCollection: The created collection has a non-zero length."
.concat(" A newly created collection must be empty!")
}
}
}
4 changes: 3 additions & 1 deletion contracts/test/MaliciousNFT.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ access(all) contract MaliciousNFT: NonFungibleToken {
/// withdraw removes an NFT from the collection and moves it to the caller
access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} {
let token <- self.ownedNFTs.remove(key: withdrawID)
?? panic("Could not withdraw an NFT with the provided ID from the collection")
?? panic("MaliciousNFT.Collection.withdraw: Could not withdraw an NFT with the ID="
.concat(withdrawID.toString())
.concat(". Check the submitted ID to make sure it is one that this collection owns"))

return <-token
}
Expand Down
49 changes: 46 additions & 3 deletions lib/go/contracts/go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,53 @@
module github.com/onflow/flow-nft/lib/go/contracts

go 1.16
go 1.22

toolchain go1.22.4

require (
github.com/kevinburke/go-bindata v3.23.0+incompatible
github.com/onflow/flow-go-sdk v1.0.0-M1
github.com/stretchr/testify v1.8.4
github.com/onflow/flow-go-sdk v1.0.0-preview.54
github.com/stretchr/testify v1.9.0
)

require (
github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c // indirect
github.com/fxamacker/circlehash v0.3.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/k0kubun/pp v3.0.1+incompatible // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/logrusorgru/aurora/v4 v4.0.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/onflow/atree v0.8.0-rc.6 // indirect
github.com/onflow/cadence v1.0.0-preview.51 // indirect
github.com/onflow/crypto v0.25.1 // indirect
github.com/onflow/flow/protobuf/go/flow v0.4.3 // indirect
github.com/onflow/go-ethereum v1.13.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c // indirect
github.com/turbolent/prettier v0.0.0-20220320183459-661cc755135d // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.uber.org/goleak v1.2.1 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 49365e3

Please sign in to comment.