Skip to content

Commit

Permalink
WIP updates to transactions for stable cadence
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Aug 15, 2023
1 parent b7d2883 commit 95c819f
Show file tree
Hide file tree
Showing 37 changed files with 278 additions and 322 deletions.
75 changes: 46 additions & 29 deletions contracts/ExampleToken-v2.cdc
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
import FungibleToken from "FungibleToken-v2"
import FungibleToken from "FungibleToken"
import MetadataViews from "MetadataViews"
import FungibleTokenMetadataViews from "FungibleTokenMetadataViews"
import MultipleVaults from "MultipleVaults.cdc"
import ViewResolver from "ViewResolver.cdc"
import MultipleVaults from "MultipleVaults"
import ViewResolver from "ViewResolver"

access(all) contract ExampleToken: ViewResolver, MultipleVaults {
access(all) contract ExampleToken: ViewResolver {

/// The event that is emitted when new tokens are minted
access(all) event TokensMinted(amount: UFix64, type: String)

/// Total supply of ExampleTokens in existence
access(contract) var totalSupply: {Type: UFix64}
access(all) var totalSupply: UFix64

/// Admin Path
access(all) let AdminStoragePath: StoragePath

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

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

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

return vaultRef.getViews()
}

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

return vaultRef.resolveView(view)
}

/// Vault
///
/// Each user stores an instance of only the Vault in their storage
Expand Down Expand Up @@ -54,7 +74,8 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
access(all) view fun getViews(): [Type] {
return [Type<FungibleTokenMetadataViews.FTView>(),
Type<FungibleTokenMetadataViews.FTDisplay>(),
Type<FungibleTokenMetadataViews.FTVaultData>()]
Type<FungibleTokenMetadataViews.FTVaultData>(),
Type<FungibleTokenMetadataViews.TotalSupply>()]
}

access(all) fun resolveView(_ view: Type): AnyStruct? {
Expand Down Expand Up @@ -90,10 +111,10 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
receiverPath: self.publicPath,
metadataPath: self.publicPath,
providerPath: /private/exampleTokenVault,
receiverLinkedType: Type<&ExampleToken.Vault{FungibleToken.Receiver}>(),
metadataLinkedType: Type<&ExampleToken.Vault{FungibleToken.Balance, ViewResolver.Resolver}>(),
providerLinkedType: Type<&ExampleToken.Vault{FungibleToken.Provider}>(),
createEmptyVaultFunction: (fun(): @ExampleToken.Vault{FungibleToken.Vault} {
receiverLinkedType: Type<&ExampleToken.Vault>(),
metadataLinkedType: Type<&ExampleToken.Vault>(),
providerLinkedType: Type<&ExampleToken.Vault>(),
createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {
return <-vaultRef.createEmptyVault()
})
)
Expand Down Expand Up @@ -135,7 +156,7 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
/// created Vault to the context that called so it can be deposited
/// elsewhere.
///
access(FungibleToken.Withdrawable) fun withdraw(amount: UFix64): @ExampleToken.Vault{FungibleToken.Vault} {
access(FungibleToken.Withdrawable) fun withdraw(amount: UFix64): @ExampleToken.Vault {
self.balance = self.balance - amount
return <-create Vault(balance: amount)
}
Expand All @@ -156,7 +177,7 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
destroy vault
}

access(all) fun transfer(amount: UFix64, receiver: Capability<&{FungibleToken.Receiver}>) {
access(FungibleToken.Withdrawable) fun transfer(amount: UFix64, receiver: Capability<&{FungibleToken.Receiver}>) {
let transferVault <- self.withdraw(amount: amount)

// Get a reference to the recipient's Receiver
Expand All @@ -174,13 +195,13 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
/// 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{FungibleToken.Vault} {
access(all) fun createEmptyVault(): @ExampleToken.Vault {
return <-create Vault(balance: 0.0)
}

destroy() {
if self.balance > 0.0 {
ExampleToken.totalSupply[self.getType()] = ExampleToken.totalSupply[self.getType()]! - self.balance
ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance
}
}
}
Expand All @@ -196,7 +217,7 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
/// and returns them to the calling context.
///
access(all) fun mintTokens(amount: UFix64): @ExampleToken.Vault {
ExampleToken.totalSupply[self.getType()] = ExampleToken.totalSupply[self.getType()]! + amount
ExampleToken.totalSupply = ExampleToken.totalSupply + amount
emit TokensMinted(amount: amount, type: self.getType().identifier)
return <-create Vault(balance: amount)
}
Expand All @@ -209,33 +230,29 @@ access(all) contract ExampleToken: ViewResolver, MultipleVaults {
/// 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(vaultType: Type): @{FungibleToken.Vault} {
switch vaultType {
case Type<@ExampleToken.Vault>():
return <- create Vault(balance: 0.0)
default:
return <- create Vault(balance: 0.0)
}
access(all) fun createEmptyVault(): @ExampleToken.Vault {
return <- create Vault(balance: 0.0)
}

init() {
self.totalSupply = {}
self.totalSupply[Type<@ExampleToken.Vault>()] = 1000.0
self.totalSupply = 1000.0

self.AdminStoragePath = /storage/exampleTokenAdmin

// Create the Vault with the total supply of tokens and save it in storage
//
let vault <- create Vault(balance: self.totalSupply[Type<@ExampleToken.Vault>()]!)
self.account.save(<-vault, to: /storage/exampleTokenVault)
let vault <- create Vault(balance: self.totalSupply)
self.VaultStoragePath = vault.getDefaultStoragePath()!
self.VaultPublicPath = vault.getDefaultPublicPath()!
self.account.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
//
self.account.link<&{FungibleToken.Receiver, FungibleToken.Balance}>(
/public/exampleTokenVault,
target: /storage/exampleTokenVault
self.account.link<&{FungibleToken.Receiver, FungibleToken.Balance, ViewResolver.Resolver}>(
self.VaultPublicPath,
target: self.VaultStoragePath
)

let admin <- create Minter()
Expand Down
38 changes: 24 additions & 14 deletions contracts/FungibleTokenSwitchboard.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ access(all) contract FungibleTokenSwitchboard {
/// deposit methods to deposit funds on it.
///
access(all) resource interface SwitchboardPublic {
access(all) view fun getVaultTypes(): [Type]
access(all) fun getVaultTypes(): [Type]
access(all) view fun getVaultTypesWithAddress(): {Type: Address}
access(all) view fun getSupportedVaultTypes(): {Type: Bool}
access(all) fun deposit(from: @FungibleToken.Vault)
access(all) fun safeDeposit(from: @FungibleToken.Vault): @FungibleToken.Vault?
access(all) fun deposit(from: @{FungibleToken.Vault})
access(all) fun safeDeposit(from: @{FungibleToken.Vault}): @{FungibleToken.Vault}?
access(all) view fun checkReceiverByType(type: Type): Bool
access(all) view fun safeBorrowByType(type: Type): &{FungibleToken.Receiver}?
}
Expand Down Expand Up @@ -117,7 +117,7 @@ access(all) contract FungibleTokenSwitchboard {
}

/// Adds a new fungible token receiver capability to the switchboard
/// resource specifying which `Type` of `@FungibleToken.Vault` can be
/// resource specifying which `Type` of `@{FungibleToken.Vault}` can be
/// deposited to it. Use it to include in your switchboard "wrapper"
/// receivers such as a `@TokenForwarding.Forwarder`. It can also be
/// used to overwrite the type attached to a certain capability without
Expand Down Expand Up @@ -145,7 +145,7 @@ access(all) contract FungibleTokenSwitchboard {
}

/// Adds zero or more new fungible token receiver capabilities to the
/// switchboard resource specifying which `Type`s of `@FungibleToken.Vault`s
/// switchboard resource specifying which `Type`s of `@{FungibleToken.Vault}`s
/// can be deposited to it. Use it to include in your switchboard "wrapper"
/// receivers such as a `@TokenForwarding.Forwarder`. It can also be
/// used to overwrite the types attached to certain capabilities without
Expand Down Expand Up @@ -207,7 +207,7 @@ access(all) contract FungibleTokenSwitchboard {
///
/// @param from: The deposited fungible token vault resource.
///
access(all) fun deposit(from: @FungibleToken.Vault) {
access(all) fun deposit(from: @{FungibleToken.Vault}) {
// Get the capability from the ones stored at the switchboard
let depositedVaultCapability
= self.receiverCapabilities[from.getType()]
Expand All @@ -230,7 +230,7 @@ access(all) contract FungibleTokenSwitchboard {
/// funds if the deposit was successful, or still containing the funds
/// if the reference to the needed vault was not found.
///
access(all) fun safeDeposit(from: @FungibleToken.Vault): @FungibleToken.Vault? {
access(all) fun safeDeposit(from: @{FungibleToken.Vault}): @{FungibleToken.Vault}? {
// Try to get the proper vault capability from the switchboard
// If the desired vault is present on the switchboard...
if let depositedVaultCapability
Expand All @@ -240,12 +240,12 @@ access(all) contract FungibleTokenSwitchboard {
if let vaultRef =
depositedVaultCapability.borrow() {
// We deposit the funds on said vault
vaultRef.deposit(from: <-from.withdraw(amount: from.balance))
vaultRef.deposit(from: <-from.withdraw(amount: from.getBalance()))
}
}
// if deposit failed for some reason
if from.balance > 0.0 {
emit NotCompletedDeposit(type: from.getType(), amount: from.balance,
if from.getBalance() > 0.0 {
emit NotCompletedDeposit(type: from.getType(), amount: from.getBalance(),
switchboardOwner: self.owner?.address)
return <-from
}
Expand Down Expand Up @@ -289,7 +289,7 @@ access(all) contract FungibleTokenSwitchboard {
/// `{FungibleToken.Receiver}` capabilities that can be effectively
/// borrowed.
///
access(all) view fun getVaultTypes(): [Type] {
access(all) fun getVaultTypes(): [Type] {
let effectiveTypes: [Type] = []
for vaultType in self.receiverCapabilities.keys {
if self.receiverCapabilities[vaultType]!.check() {
Expand Down Expand Up @@ -325,13 +325,23 @@ access(all) contract FungibleTokenSwitchboard {
/// @return Dictionary of FT types that can be deposited.
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
let supportedVaults: {Type: Bool} = {}
let vaultTypes = self.getVaultTypes()
for vaultT in vaultTypes {
supportedVaults.insert(key: vaultT, true)
for vaultType in self.receiverCapabilities.keys {
if self.receiverCapabilities[vaultType]!.check() {
supportedVaults[vaultType] = true
}
}
return supportedVaults
}

/// Returns whether or not the given type is accepted by the Receiver
/// A vault that can accept any type should just return true by default
access(all) view fun isSupportedVaultType(type: Type): Bool {
let supportedVaults = self.getSupportedVaultTypes()
if let supported = supportedVaults[type] {
return supported
} else { return false }
}

init() {
// Initialize the capabilities dictionary
self.receiverCapabilities = {}
Expand Down
2 changes: 1 addition & 1 deletion contracts/MultipleVaults.cdc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


import FungibleToken from "FungibleToken-v2"
import FungibleToken from "FungibleToken"

access(all) contract interface MultipleVaults {

Expand Down
35 changes: 22 additions & 13 deletions contracts/utility/TokenForwarding.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,27 @@ their tokens to.

import FungibleToken from "FungibleToken"

pub contract TokenForwarding {
access(all) contract TokenForwarding {

// Event that is emitted when tokens are deposited to the target receiver
pub event ForwardedDeposit(amount: UFix64, from: Address?)
access(all) event ForwardedDeposit(amount: UFix64, from: Address?)

pub resource interface ForwarderPublic {
access(all) resource interface ForwarderPublic {

/// Helper function to check whether set `recipient` capability
/// is not latent or the capability tied to a type is valid.
pub fun check(): Bool
access(all) fun check(): Bool

/// Gets the receiver assigned to a recipient capability.
/// This is necessary because without it, it is not possible to look under the hood and see if a capability
/// is of an expected type or not. This helps guard against infinitely chained TokenForwarding or other invalid
/// malicious kinds of updates that could prevent listings from being made that are valid on storefronts.
///
/// @return an optional receiver capability for consumers of the TokenForwarding to check/validate on their own
pub fun safeBorrow(): &{FungibleToken.Receiver}?
access(all) fun safeBorrow(): &{FungibleToken.Receiver}?
}

pub resource Forwarder: FungibleToken.Receiver, ForwarderPublic {
access(all) resource Forwarder: FungibleToken.Receiver, ForwarderPublic {

// This is where the deposited tokens will be sent.
// The type indicates that it is a reference to a receiver
Expand All @@ -48,10 +48,10 @@ pub contract TokenForwarding {
// Function that takes a Vault object as an argument and forwards
// it to the recipient's Vault using the stored reference
//
pub fun deposit(from: @FungibleToken.Vault) {
access(all) fun deposit(from: @{FungibleToken.Vault}) {
let receiverRef = self.recipient.borrow<&{FungibleToken.Receiver}>()!

let balance = from.balance
let balance = from.getBalance()

receiverRef.deposit(from: <-from)

Expand All @@ -60,7 +60,7 @@ pub contract TokenForwarding {

/// Helper function to check whether set `recipient` capability
/// is not latent or the capability tied to a type is valid.
pub fun check(): Bool {
access(all) fun check(): Bool {
return self.recipient.check<&{FungibleToken.Receiver}>()
}

Expand All @@ -70,13 +70,13 @@ pub contract TokenForwarding {
/// malicious kinds of updates that could prevent listings from being made that are valid on storefronts.
///
/// @return an optional receiver capability for consumers of the TokenForwarding to check/validate on their own
pub fun safeBorrow(): &{FungibleToken.Receiver}? {
access(all) fun safeBorrow(): &{FungibleToken.Receiver}? {
return self.recipient.borrow<&{FungibleToken.Receiver}>()
}

// changeRecipient changes the recipient of the forwarder to the provided recipient
//
pub fun changeRecipient(_ newRecipient: Capability) {
access(all) fun changeRecipient(_ newRecipient: Capability) {
pre {
newRecipient.borrow<&{FungibleToken.Receiver}>() != nil: "Could not borrow Receiver reference from the Capability"
}
Expand All @@ -87,7 +87,7 @@ pub contract TokenForwarding {
/// which can be deposited using the 'deposit' function.
///
/// @return Array of FT types that can be deposited.
pub fun getSupportedVaultTypes(): {Type: Bool} {
access(all) view fun getSupportedVaultTypes(): {Type: Bool} {
if !self.recipient.check<&{FungibleToken.Receiver}>() {
return {}
}
Expand All @@ -97,6 +97,15 @@ pub contract TokenForwarding {
return supportedVaults
}

/// Returns whether or not the given type is accepted by the Receiver
/// A vault that can accept any type should just return true by default
access(all) view fun isSupportedVaultType(type: Type): Bool {
let supportedVaults = self.getSupportedVaultTypes()
if let supported = supportedVaults[type] {
return supported
} else { return false }
}

init(recipient: Capability) {
pre {
recipient.borrow<&{FungibleToken.Receiver}>() != nil: "Could not borrow Receiver reference from the Capability"
Expand All @@ -107,7 +116,7 @@ pub contract TokenForwarding {

// createNewForwarder creates a new Forwarder reference with the provided recipient
//
pub fun createNewForwarder(recipient: Capability): @Forwarder {
access(all) fun createNewForwarder(recipient: Capability): @Forwarder {
return <-create Forwarder(recipient: recipient)
}
}
Loading

0 comments on commit 95c819f

Please sign in to comment.