Skip to content

Commit

Permalink
add suport for multiple type definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Jan 22, 2024
1 parent bd00789 commit aec3a27
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 153 deletions.
55 changes: 23 additions & 32 deletions contracts/ExampleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import MetadataViews from "MetadataViews"
import FungibleTokenMetadataViews from "FungibleTokenMetadataViews"
import ViewResolver from "ViewResolver"

access(all) contract ExampleToken: ViewResolver {
access(all) contract ExampleToken: FungibleToken {

/// The event that is emitted when new tokens are minted
access(all) event TokensMinted(amount: UFix64, type: String)
Expand All @@ -14,19 +14,14 @@ access(all) contract ExampleToken: ViewResolver {
/// Admin Path
access(all) let AdminStoragePath: StoragePath

/// User Paths
access(all) let VaultStoragePath: StoragePath
access(all) let VaultPublicPath: PublicPath
access(all) let ReceiverPublicPath: PublicPath

access(all) view fun getContractViews(): [Type] {
access(all) view fun getContractViews(resourceType: Type?): [Type] {
let vaultRef = self.account.capabilities.borrow<&ExampleToken.Vault>(/public/exampleTokenVault)
?? panic("Could not borrow a reference to the vault resolver")

return vaultRef.getViews()
}

access(all) fun resolveContractView(_ view: Type): AnyStruct? {
access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {
let vaultRef = self.account.capabilities.borrow<&ExampleToken.Vault>(/public/exampleTokenVault)
?? panic("Could not borrow a reference to the vault resolver")

Expand All @@ -50,9 +45,9 @@ access(all) contract ExampleToken: ViewResolver {
/// The total balance of this vault
access(all) var balance: UFix64

access(self) var storagePath: StoragePath
access(self) var publicPath: PublicPath
access(self) var receiverPath: PublicPath
access(all) var storagePath: StoragePath
access(all) var publicPath: PublicPath
access(all) var receiverPath: PublicPath

access(all) view fun getViews(): [Type] {
return [
Expand All @@ -63,12 +58,6 @@ access(all) contract ExampleToken: ViewResolver {
]
}

/// Returns the FTVaultData view for this Vault, which contains
/// all relevant paths, types, and create vault function
access(all) fun getFTVaultDataView(): AnyStruct {
return self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>())
}

access(all) fun resolveView(_ view: Type): AnyStruct? {
switch view {
case Type<FungibleTokenMetadataViews.FTView>():
Expand Down Expand Up @@ -150,7 +139,7 @@ access(all) contract ExampleToken: ViewResolver {
/// created Vault to the context that called so it can be deposited
/// elsewhere.
///
access(FungibleToken.Withdrawable) fun withdraw(amount: UFix64): @ExampleToken.Vault {
access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @ExampleToken.Vault {
self.balance = self.balance - amount
return <-create Vault(balance: amount)
}
Expand Down Expand Up @@ -200,14 +189,19 @@ access(all) contract ExampleToken: ViewResolver {
}
}

/// Function to return the types that the contract implements
access(all) view fun getVaultTypes(): [Type] {
return [Type<@ExampleToken.Vault>()]
}

/// createEmptyVault
///
/// Function that creates a new Vault with a balance of zero
/// and returns it to the calling context. A user must call this function
/// and store the returned Vault in their storage in order to allow their
/// account to be able to receive deposits of this token type.
///
access(all) fun createEmptyVault(): @ExampleToken.Vault {
access(all) fun createEmptyVault(vaultType: Type): @ExampleToken.Vault {
return <- create Vault(balance: 0.0)
}

Expand All @@ -217,11 +211,11 @@ access(all) contract ExampleToken: ViewResolver {
///
/// Will need to add an update to total supply
/// See https://github.com/onflow/flips/pull/131
access(all) fun burnTokens(from: @ExampleToken.Vault) {
if from.balance > 0.0 {
ExampleToken.totalSupply = ExampleToken.totalSupply - from.getBalance()
access(all) fun burn(_ vault: @{FungibleToken.Vault}) {
if vault.balance > 0.0 {
ExampleToken.totalSupply = ExampleToken.totalSupply - vault.getBalance()
}
FungibleToken.burn(<-from)
destroy vault
}

init() {
Expand All @@ -232,20 +226,17 @@ access(all) contract ExampleToken: ViewResolver {
// Create the Vault with the total supply of tokens and save it in storage
//
let vault <- create Vault(balance: self.totalSupply)
self.VaultStoragePath = vault.getDefaultStoragePath()!
self.VaultPublicPath = vault.getDefaultPublicPath()!
self.ReceiverPublicPath = vault.getDefaultReceiverPath()!

self.account.storage.save(<-vault, to: self.VaultStoragePath)

// Create a public capability to the stored Vault that exposes
// the `deposit` method and getAcceptedTypes method through the `Receiver` interface
// and the `getBalance()` method through the `Balance` interface
//
let exampleTokenCap = self.account.capabilities.storage.issue<&Vault>(self.VaultStoragePath)
self.account.capabilities.publish(exampleTokenCap, at: self.VaultPublicPath)
let receiverCap = self.account.capabilities.storage.issue<&{FungibleToken.Receiver}>(self.VaultStoragePath)
self.account.capabilities.publish(receiverCap, at: self.ReceiverPublicPath)
let exampleTokenCap = self.account.capabilities.storage.issue<&Vault>(vault.storagePath)
self.account.capabilities.publish(exampleTokenCap, at: vault.publicPath)
let receiverCap = self.account.capabilities.storage.issue<&{FungibleToken.Receiver}>(vault.storagePath)
self.account.capabilities.publish(receiverCap, at: vault.receiverPath)

self.account.storage.save(<-vault, to: /storage/exampleTokenVault)

let admin <- create Minter()
self.account.storage.save(<-admin, to: self.AdminStoragePath)
Expand Down
56 changes: 37 additions & 19 deletions contracts/FungibleToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ import ViewResolver from "ViewResolver"
/// utility methods that many projects will still want to have on their contracts,
/// but they are by no means required. all that is required is that the token
/// implements the `Vault` interface
access(all) contract FungibleToken {
access(all) contract interface FungibleToken: ViewResolver {

// An entitlement for allowing the withdrawal of tokens from a Vault
access(all) entitlement Withdrawable
access(all) entitlement Withdraw

/// The event that is emitted when tokens are withdrawn from a Vault
access(all) event Withdraw(amount: UFix64, type: String, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64)
access(all) event Withdrawn(amount: UFix64, type: String, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64)

/// The event that is emitted when tokens are deposited to a Vault
access(all) event Deposit(amount: UFix64, type: String, to: Address?, toUUID: UInt64, depositedUUID: UInt64)
access(all) event Deposited(amount: UFix64, type: String, to: Address?, toUUID: UInt64, depositedUUID: UInt64)

/// Event that is emitted when the global burn method is called with a non-zero balance
access(all) event Burn(amount: UFix64, type: String, fromUUID: UInt64)
access(all) event Burned(amount: UFix64, type: String, fromUUID: UInt64)

/// Balance
///
Expand All @@ -78,16 +78,16 @@ access(all) contract FungibleToken {
/// withdraw subtracts tokens from the implementing resource
/// and returns a Vault with the removed tokens.
///
/// The function's access level is `access(Withdrawable)`
/// The function's access level is `access(Withdraw)`
/// So in order to access it, one would either need the object itself
/// or an entitled reference with `Withdrawable`.
/// or an entitled reference with `Withdraw`.
///
access(Withdrawable) fun withdraw(amount: UFix64): @{Vault} {
access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
post {
// `result` refers to the return value
result.getBalance() == amount:
"Withdrawal amount must be the same as the balance of the withdrawn Vault"
emit Withdraw(amount: amount, type: self.getType().identifier, from: self.owner?.address, fromUUID: self.uuid, withdrawnUUID: result.uuid)
emit Withdrawn(amount: amount, type: self.getType().identifier, from: self.owner?.address, fromUUID: self.uuid, withdrawnUUID: result.uuid)
}
}
}
Expand Down Expand Up @@ -130,6 +130,9 @@ access(all) contract FungibleToken {
access(all) view fun getBalance(): UFix64

/// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts
/// The default implementation is included here because vaults are expected
/// to only accepted their own type, so they have no need to provide an implementation
/// for this function
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
// Below check is implemented to make sure that run-time type would
// only get returned when the parent resource conforms with `FungibleToken.Vault`.
Expand All @@ -147,14 +150,10 @@ access(all) contract FungibleToken {
return self.getSupportedVaultTypes()[type] ?? false
}

/// Returns the FTVaultData view for this Vault, which contains
/// all relevant paths, types, and create vault function
access(all) fun getFTVaultDataView(): AnyStruct

/// withdraw subtracts `amount` from the Vault's balance
/// and returns a new Vault with the subtracted balance
///
access(Withdrawable) fun withdraw(amount: UFix64): @{Vault} {
access(Withdraw) fun withdraw(amount: UFix64): @{Vault} {
pre {
self.getBalance() >= amount:
"Amount withdrawn must be less than or equal than the balance of the Vault"
Expand All @@ -176,7 +175,7 @@ access(all) contract FungibleToken {
pre {
from.isInstance(self.getType()):
"Cannot deposit an incompatible token type"
emit Deposit(amount: from.getBalance(), type: from.getType().identifier, to: self.owner?.address, toUUID: self.uuid, depositedUUID: from.uuid)
emit Deposited(amount: from.getBalance(), type: from.getType().identifier, to: self.owner?.address, toUUID: self.uuid, depositedUUID: from.uuid)
}
post {
self.getBalance() == before(self.getBalance()) + before(from.getBalance()):
Expand All @@ -193,11 +192,30 @@ access(all) contract FungibleToken {
}
}

/// Global method to burn any FungibleToken Vault
/// Function to return the types that the contract implements
access(all) view fun getVaultTypes(): [Type] {
post {
result.length > 0: "Must indicate what fungible token types this contract defines"
}
}

/// createEmptyVault allows any user to create a new Vault that has a zero balance
///
access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault} {
post {
result.getType() == vaultType: "The returned vault does not match the desired type"
result.getBalance() == 0.0: "The newly created Vault must have zero balance"
}
}

/// Method to burn a FungibleToken Vault
/// contract implementations should provide an implementation for this function
/// that subtracts the vault balance from the token's total supply
access(all) fun burn(_ vault: @{FungibleToken.Vault}) {
if vault.balance > 0.0 {
emit Burn(amount: vault.balance, type: vault.getType().identifier, fromUUID: vault.uuid)
pre {
self.getVaultTypes().contains(vault.getType())
vault.balance > 0.0: "Do not use the burn method unless the vault balance is greater than zero!"
emit Burned(amount: vault.balance, type: vault.getType().identifier, fromUUID: vault.uuid)
}
destroy vault
}
}
25 changes: 0 additions & 25 deletions contracts/MultipleVaults.cdc

This file was deleted.

9 changes: 3 additions & 6 deletions contracts/utility/MetadataViews.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,11 @@ access(all) contract MetadataViews {
/// including standard NFT interfaces and metadataviews interfaces
access(all) let publicPath: PublicPath

/// Public collection type that is expected to provide sufficient read-only access to standard
/// functions (deposit + getIDs + borrowNFT). For new
/// collections, this may be set to be equal to the type specified in `publicLinkedType`.
/// The concrete type of the collection that is exposed to the public
/// now that entitlements exist, it no longer needs to be restricted to a specific interface
access(all) let publicCollection: Type

/// Type that should be linked at the aforementioned public path. This is normally a
/// restricted type with many interfaces. Notably the
/// `NFT.Receiver`, and `ViewResolver.ResolverCollection` interfaces are required.
/// Type that should be linked at the aforementioned public path
access(all) let publicLinkedType: Type

/// Function that allows creation of an empty NFT collection that is intended to store
Expand Down
Loading

0 comments on commit aec3a27

Please sign in to comment.