Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adds more descriptive and helpful error messages to the FT contracts and txs #170

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules/*
.env
coverage.lcov
coverage.json
*.pkey
71 changes: 60 additions & 11 deletions contracts/FungibleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,10 @@ access(all) contract interface FungibleToken: ViewResolver {
post {
// `result` refers to the return value
result.balance == amount:
"Withdrawal amount must be the same as the balance of the withdrawn Vault"
"FungibleToken.Provider.withdraw: Cannot withdraw tokens!"
.concat("The balance of the withdrawn tokens (").concat(result.balance.toString())
.concat(") is not equal to the amount requested to be withdrawn (")
.concat(amount.toString()).concat(")")
}
}
}
Expand Down Expand Up @@ -178,7 +181,9 @@ access(all) contract interface FungibleToken: ViewResolver {
emit Burned(type: self.getType().identifier, amount: self.balance, fromUUID: self.uuid)
}
post {
self.balance == 0.0: "The balance must be set to zero during the burnCallback method so that it cannot be spammed"
self.balance == 0.0:
"FungibleToken.Vault.burnCallback: Cannot burn this Vault with Burner.burn(). "
.concat("The balance must be set to zero during the burnCallback method so that it cannot be spammed.")
}
self.balance = 0.0
}
Expand Down Expand Up @@ -211,15 +216,29 @@ access(all) contract interface FungibleToken: ViewResolver {
access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
pre {
self.balance >= amount:
"Amount withdrawn must be less than or equal than the balance of the Vault"
"FungibleToken.Vault.withdraw: Cannot withdraw tokens! "
.concat("The amount requested to be withdrawn (").concat(amount.toString())
.concat(") is greater than the balance of the Vault (")
.concat(self.balance.toString()).concat(").")
}
post {
result.getType() == self.getType(): "Must return the same vault type as self"
result.getType() == self.getType():
"FungibleToken.Vault.withdraw: Cannot withdraw tokens! "
.concat("The withdraw method tried to return an incompatible Vault type <")
.concat(result.getType().identifier).concat(">. ")
.concat("It must return a Vault with the same type as self <")
.concat(self.getType().identifier).concat(">.")

// use the special function `before` to get the value of the `balance` field
// at the beginning of the function execution
//
self.balance == before(self.balance) - amount:
"New Vault balance must be the difference of the previous balance and the withdrawn Vault balance"
"FungibleToken.Vault.withdraw: Cannot withdraw tokens! "
.concat("The sender's balance after the withdrawal (")
.concat(self.balance.toString())
.concat(") must be the difference of the previous balance (").concat(before(self.balance.toString()))
.concat(") and the amount withdrawn (").concat(amount.toString()).concat(")")

emit Withdrawn(
type: result.getType().identifier,
amount: amount,
Expand All @@ -238,7 +257,13 @@ access(all) contract interface FungibleToken: ViewResolver {
// as the vault that is accepting the deposit
pre {
from.isInstance(self.getType()):
"Cannot deposit an incompatible token type"
"FungibleToken.Vault.deposit: Cannot deposit tokens! "
.concat("The type of the deposited tokens <")
.concat(from.getType().identifier)
.concat("> has to be the same type as the Vault being deposited into <")
.concat(self.getType().identifier)
.concat(">. Check that you are withdrawing and depositing to the correct paths in the sender and receiver accounts ")
.concat("and that those paths hold the same Vault types.")
}
post {
emit Deposited(
Expand All @@ -250,7 +275,11 @@ access(all) contract interface FungibleToken: ViewResolver {
balanceAfter: self.balance
)
self.balance == before(self.balance) + before(from.balance):
"New Vault balance must be the sum of the previous balance and the deposited Vault"
"FungibleToken.Vault.deposit: Cannot deposit tokens! "
.concat("The receiver's balance after the deposit (")
.concat(self.balance.toString())
.concat(") must be the sum of the previous balance (").concat(before(self.balance.toString()))
.concat(") and the amount deposited (").concat(before(from.balance).toString()).concat(")")
}
}

Expand All @@ -259,8 +288,18 @@ access(all) contract interface FungibleToken: ViewResolver {
/// @return A Vault of the same type that has a balance of zero
access(all) fun createEmptyVault(): @{Vault} {
post {
result.balance == 0.0: "The newly created Vault must have zero balance"
result.getType() == self.getType(): "The newly created Vault must have the same type as the creating vault"
result.balance == 0.0:
"FungibleToken.Vault.createEmptyVault: Empty Vault creation failed! "
.concat("The newly created Vault must have zero balance but it has a balance of ")
.concat(result.balance.toString())

result.getType() == self.getType():
"FungibleToken.Vault.createEmptyVault: Empty Vault creation failed! "
.concat("The type of the new Vault <")
.concat(result.getType().identifier)
.concat("> has to be the same type as the Vault that created it <")
.concat(self.getType().identifier)
.concat(">.")
}
}
}
Expand All @@ -270,8 +309,18 @@ access(all) contract interface FungibleToken: ViewResolver {
/// @return A Vault of the requested type that has a balance of zero
access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault} {
post {
result.getType() == vaultType: "The returned vault does not match the desired type"
result.balance == 0.0: "The newly created Vault must have zero balance"
result.balance == 0.0:
"FungibleToken.createEmptyVault: Empty Vault creation failed! "
.concat("The newly created Vault must have zero balance but it has a balance of (")
.concat(result.balance.toString()).concat(")")

result.getType() == vaultType:
"FungibleToken.Vault.createEmptyVault: Empty Vault creation failed! "
.concat("The type of the new Vault <")
.concat(result.getType().identifier)
.concat("> has to be the same as the type that was requested <")
.concat(vaultType.identifier)
.concat(">.")
}
}
}
10 changes: 8 additions & 2 deletions contracts/FungibleTokenMetadataViews.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,14 @@ access(all) contract FungibleTokenMetadataViews {
createEmptyVaultFunction: fun(): @{FungibleToken.Vault}
) {
pre {
receiverLinkedType.isSubtype(of: Type<&{FungibleToken.Receiver}>()): "Receiver public type must include FungibleToken.Receiver."
metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Vault}>()): "Metadata linked type must be a fungible token vault"
receiverLinkedType.isSubtype(of: Type<&{FungibleToken.Receiver}>()):
"Receiver public type <".concat(receiverLinkedType.identifier)
.concat("> must be a subtype of <").concat(Type<&{FungibleToken.Receiver}>().identifier)
.concat(">.")
metadataLinkedType.isSubtype(of: Type<&{FungibleToken.Vault}>()):
"Metadata linked type <".concat(metadataLinkedType.identifier)
.concat("> must be a subtype of <").concat(Type<&{FungibleToken.Vault}>().identifier)
.concat(">.")
}
self.storagePath = storagePath
self.receiverPath = receiverPath
Expand Down
30 changes: 23 additions & 7 deletions contracts/FungibleTokenSwitchboard.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ access(all) contract FungibleTokenSwitchboard {
// Borrow a reference to the vault pointed to by the capability we
// want to store inside the switchboard
let vaultRef = capability.borrow()
?? panic ("Cannot borrow reference to vault from capability")
// Check if there is a previous capability for this token, if not
?? panic("FungibleTokenSwitchboard.Switchboard.addNewVault: Cannot borrow reference to vault from capability! "
.concat("Make sure that the capability path points to a Vault that has been properly initialized. "))

// Check if there is a previous capability for this token
if (self.receiverCapabilities[vaultRef.getType()] == nil) {
// use the vault reference type as key for storing the
// capability and then
Expand All @@ -80,7 +82,9 @@ access(all) contract FungibleTokenSwitchboard {
capabilityOwner: capability.address)
} else {
// If there was already a capability for that token, panic
panic("There is already a vault in the Switchboard for this token")
panic("FungibleTokenSwitchboard.Switchboard.addNewVault: Cannot add new Vault capability! "
.concat("There is already a vault in the Switchboard for this type <")
.concat(vaultRef.getType().identifier).concat(">."))
}
}

Expand Down Expand Up @@ -135,7 +139,12 @@ access(all) contract FungibleTokenSwitchboard {
access(Owner) fun addNewVaultWrapper(capability: Capability<&{FungibleToken.Receiver}>,
type: Type) {
// Check if the capability is working
assert(capability.check(), message: "The passed capability is not valid")
assert (
capability.check(),
message:
"FungibleTokenSwitchboard.Switchboard.addNewVaultWrapper: Cannot borrow reference to a vault from the provided capability! "
.concat("Make sure that the capability path points to a Vault that has been properly initialized.")
)
// Use the type parameter as key for the capability
self.receiverCapabilities[type] = capability
// emit the event that indicates that a new capability has been
Expand Down Expand Up @@ -192,7 +201,9 @@ access(all) contract FungibleTokenSwitchboard {
// Borrow a reference to the vault pointed to by the capability we
// want to remove from the switchboard
let vaultRef = capability.borrow()
?? panic ("Cannot borrow reference to vault from capability")
?? panic ("FungibleTokenSwitchboard.Switchboard.addNewVaultWrapper: Cannot borrow reference to a vault from the provided capability! "
.concat("Make sure that the capability path points to a Vault that has been properly initialized."))

// Use the vault reference to find the capability to remove
self.receiverCapabilities.remove(key: vaultRef.getType())
// Emit the event that indicates that a new capability has been
Expand All @@ -212,11 +223,16 @@ access(all) contract FungibleTokenSwitchboard {
access(all) fun deposit(from: @{FungibleToken.Vault}) {
// Get the capability from the ones stored at the switchboard
let depositedVaultCapability = self.receiverCapabilities[from.getType()]
?? panic ("The deposited vault is not available on this switchboard")
?? panic ("FungibleTokenSwitchboard.Switchboard.deposit: Cannot deposit Vault! "
.concat("The deposited vault of type <").concat(from.getType().identifier)
.concat("> is not available on this Fungible Token switchboard. ")
.concat("The recipient needs to initialize their account and switchboard to hold and receive the deposited vault type."))

// Borrow the reference to the desired vault
let vaultRef = depositedVaultCapability.borrow()
?? panic ("Can not borrow a reference to the the vault")
?? panic ("FungibleTokenSwitchboard.Switchboard.deposit: Cannot borrow reference to a vault "
.concat("from the type of the deposited Vault <").concat(from.getType().identifier)
.concat(">. Make sure that the capability path points to a Vault that has been properly initialized."))

vaultRef.deposit(from: <-from)
}
Expand Down
Loading
Loading