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

WIP: V2 FungibleToken Standard #77

Closed
wants to merge 59 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
e7f717c
first real draft of v2 token standard
joshuahannan Jul 18, 2022
db9cc2d
Merge branch 'master' of github.com:onflow/flow-ft into new-standard
joshuahannan Jul 18, 2022
1718595
add transfer
joshuahannan Jul 18, 2022
03527c0
fix total supply field
joshuahannan Jul 18, 2022
5bf65e5
add createEmptyVault to resource definition
joshuahannan Jul 18, 2022
61d4243
PR comments
joshuahannan Jul 18, 2022
b39028c
updating paths
joshuahannan Jul 18, 2022
1bb20b6
change type array to dictionary, add transfer event, and use capabili…
joshuahannan Jul 19, 2022
1ae18bf
add conditional for destroy
joshuahannan Jul 21, 2022
7a102a8
Create FungibleTokenMetadataViews contract, include MetadataViews con…
alilloig Sep 1, 2022
32c0010
Apply suggestions from code review
alilloig Sep 2, 2022
093318d
Fix CI
alilloig Sep 2, 2022
079d367
Remove unnecesary init parameter on ftvaultdata
alilloig Sep 7, 2022
13b5b0d
Fix CI
alilloig Sep 7, 2022
fccd93c
Remove thumbnail and images from FTDisplay, add logo as only image an…
alilloig Sep 7, 2022
ba9bf2e
Merge branch 'alilloig/example-token-metadata-views' of github.com:on…
alilloig Sep 15, 2022
a6fdcb7
Restore files after the testing crisis
alilloig Sep 15, 2022
90395f4
Fix setup account from view test
alilloig Sep 15, 2022
f461448
Add auxiliary function for returning views and the case for returning…
alilloig Sep 15, 2022
ecc811e
Merge remote-tracking branch 'origin/master' into alilloig/ft-metadat…
alilloig Sep 15, 2022
306a1fe
Change logo (media) for logos (medias) at FTView
alilloig Sep 15, 2022
ead9c82
Update from logo to logos
alilloig Sep 15, 2022
e8b7f56
Fix spelling typos
alilloig Sep 16, 2022
58c157b
FT metadata docs section
alilloig Sep 19, 2022
c70204c
Add comments and use of getFTView function instead of resolveView
alilloig Sep 19, 2022
7aee404
Add scripts for read metadata
alilloig Sep 19, 2022
51bfed4
Merge remote-tracking branch 'origin/alilloig/example-token-metadata-…
alilloig Sep 19, 2022
1933db4
Finish docs
alilloig Sep 19, 2022
8d70279
Delete returnview functions
alilloig Sep 20, 2022
8c4a3c4
Switch to MetadataPublicPath
alilloig Sep 20, 2022
089b9fb
Add metadata path
alilloig Sep 20, 2022
7545b02
Merge remote-tracking branch 'origin/alilloig/ft-metadata-views' into…
alilloig Sep 20, 2022
4e5f87d
Change balance for metadata path at transactions. Fix FTVaultData con…
alilloig Sep 20, 2022
a0d375b
Add default implementation for MetadataViews.Resolver methods
alilloig Sep 20, 2022
1082bac
Merge remote-tracking branch 'origin/alilloig/ft-metadata-views' into…
alilloig Sep 20, 2022
0aadea8
Merge remote-tracking branch 'origin/alilloig/example-token-metadata-…
alilloig Sep 20, 2022
5b06fd7
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
21203f2
Update contracts/FungibleToken.cdc
alilloig Sep 30, 2022
5e2328d
Update contracts/FungibleToken.cdc
alilloig Sep 30, 2022
84880ef
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
eb5637a
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
e6de7ef
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
ad86e87
Update contracts/FungibleToken.cdc
alilloig Sep 30, 2022
81b0e9e
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
b58c49d
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
a31fd46
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
541764b
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
f24f5f2
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
50f777c
Update contracts/FungibleTokenMetadataViews.cdc
alilloig Sep 30, 2022
24763ca
Apply review suggestions
alilloig Sep 30, 2022
c5fd8b5
Add consistency about vaultData field name
alilloig Sep 30, 2022
54b8066
Merge
alilloig Sep 30, 2022
abe2080
move everything into the interface
joshuahannan Oct 4, 2022
2045a1d
Merge pull request #93 from onflow/alilloig/docs-metadata-views
alilloig Oct 5, 2022
1f32f2e
Merge pull request #91 from onflow/alilloig/example-token-metadata-views
alilloig Oct 5, 2022
e70eaaf
integrate metadata views
joshuahannan Oct 5, 2022
b924625
add metadata functions and optional returns
joshuahannan Dec 6, 2022
c742c6d
align with FLIP
joshuahannan Dec 16, 2022
051b966
WIP: V2 FungibleToken Standard (#126)
joshuahannan Mar 14, 2023
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
227 changes: 227 additions & 0 deletions contracts/ExampleToken-v2.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import FungibleToken from "./FungibleToken-v2.cdc"
import FungibleTokenInterface from "./FungibleToken-v2-ContractInterface.cdc"

pub contract ExampleToken: FungibleTokenInterface {

/// Total supply of ExampleTokens in existence
pub var totalSupply: UFix64

/// Admin Path
pub let AdminStoragePath: StoragePath

/// EVENTS

/// We would like to be able to define events in the resource

/// TokensWithdrawn
///
/// The event that is emitted when tokens are withdrawn from a Vault
pub event TokensWithdrawn(amount: UFix64, from: Address?, type: Type)

/// TokensDeposited
///
/// The event that is emitted when tokens are deposited to a Vault
pub event TokensDeposited(amount: UFix64, to: Address?, type: Type)

/// TokensMinted
///
/// The event that is emitted when new tokens are minted
pub event TokensMinted(amount: UFix64, type: Type)

/// TokensBurned
///
/// The event that is emitted when tokens are destroyed
pub event TokensBurned(amount: UFix64, type: Type)

/// MinterCreated
///
/// The event that is emitted when a new minter resource is created
pub event MinterCreated(allowedAmount: UFix64, type: Type)

/// Function to return the types that the contract implements
pub fun getVaultTypes(): [FungibleToken.VaultInfo] {
let typeArray: [FungibleToken.VaultInfo] = []

let vault <- create Vault(balance: 0.0)

let vaultInfo = vault.getTypeInfo()

destroy vault

typeArray.append(vaultInfo)
return typeArray
}

/// Vault
///
/// Each user stores an instance of only the Vault in their storage
/// The functions in the Vault and governed by the pre and post conditions
/// in FungibleToken when they are called.
/// The checks happen at runtime whenever a function is called.
///
/// Resources can only be created in the context of the contract that they
/// are defined in, so there is no way for a malicious user to create Vaults
/// out of thin air. A special Minter resource needs to be defined to mint
/// new tokens.
///
pub resource Vault: FungibleToken.Vault, FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance {

/// Storage and Public Paths
pub let VaultStoragePath: StoragePath
pub let ReceiverPublicPath: PublicPath
pub let BalancePublicPath: PublicPath

/// The total balance of this vault
pub var balance: UFix64

// initialize the balance at resource creation time
init(balance: UFix64) {
self.balance = balance
self.VaultStoragePath = /storage/exampleTokenVault
self.ReceiverPublicPath = /public/exampleTokenReceiver
self.BalancePublicPath = /public/exampleTokenBalance
}

/// Return information about the vault's type and paths
pub fun getTypeInfo(): FungibleToken.VaultInfo {
return FungibleToken.VaultInfo(type: self.getType(), VaultStoragePath: self.VaultStoragePath, ReceiverPublicPath: self.ReceiverPublicPath, BalancePublicPath: self.BalancePublicPath)
}

/// Get the balance of the vault
pub fun getBalance(): UFix64 {
return self.balance
}

/// withdraw
///
/// Function that takes an amount as an argument
/// and withdraws that amount from the Vault.
///
/// It creates a new temporary Vault that is used to hold
/// the money that is being transferred. It returns the newly
joshuahannan marked this conversation as resolved.
Show resolved Hide resolved
/// created Vault to the context that called so it can be deposited
/// elsewhere.
///
pub fun withdraw(amount: UFix64): @ExampleToken.Vault{FungibleToken.Vault} {
self.balance = self.balance - amount
emit TokensWithdrawn(amount: amount, from: self.owner?.address, type: self.getType())
return <-create Vault(balance: amount)
}

/// getAcceptedTypes optionally returns a list of vault types that this receiver accepts
pub fun getAcceptedTypes(): [Type]? {
let typeArray: [Type] = []
typeArray.append(Type<@ExampleToken.Vault>())
return typeArray
}

/// deposit
///
/// Function that takes a Vault object as an argument and adds
/// its balance to the balance of the owners Vault.
///
/// It is allowed to destroy the sent Vault because the Vault
/// was a temporary holder of the tokens. The Vault's balance has
/// been consumed and therefore can be destroyed.
///
pub fun deposit(from: @AnyResource{FungibleToken.Vault}) {
let vault <- from as! @ExampleToken.Vault
self.balance = self.balance + vault.balance
emit TokensDeposited(amount: vault.balance, to: self.owner?.address, type: self.getType())
vault.balance = 0.0
destroy vault
}

destroy() {
ExampleToken.totalSupply = ExampleToken.totalSupply - self.balance

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way the FungibleToken interface itself can enforce that this is correct?

Copy link
Member Author

@joshuahannan joshuahannan Jul 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not that I am aware of. @turbolent do you know if it would be possible to implement post conditions for destructors in a resource interface

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would put there something like:
if self.balance>0
99% of the cases it is 0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would that accomplish @bluesign ? Maybe I am misunderstanding.

Copy link

@bluesign bluesign Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flow still thinks registers are updated even update is no-op.

For example, currently FlowToken spams this on every block. ( FlowToken.totalSupply as changed ) Actually it is a bit Flow-go problem. But it is good to not even write something, when it is not changed I guess.

Copy link
Member Author

@joshuahannan joshuahannan Jul 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, that is a good point. I'll change that. That could be changed in the current example contract and flowtoken too

}
}

/// 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.
///
pub fun createEmptyVault(): @Vault {
return <-create Vault(balance: 0.0)
}

pub resource Administrator {

/// createNewMinter
///
/// Function that creates and returns a new minter resource
///
pub fun createNewMinter(allowedAmount: UFix64): @Minter {
emit MinterCreated(allowedAmount: allowedAmount, type: self.getType())
return <-create Minter(allowedAmount: allowedAmount)
}
}

/// Minter
///
/// Resource object that token admin accounts can hold to mint new tokens.
///
pub resource Minter {

/// The amount of tokens that the minter is allowed to mint
pub var allowedAmount: UFix64

/// mintTokens
///
/// Function that mints new tokens, adds them to the total supply,
/// and returns them to the calling context.
///
pub fun mintTokens(amount: UFix64): @ExampleToken.Vault {
pre {
amount > 0.0: "Amount minted must be greater than zero"
amount <= self.allowedAmount: "Amount minted must be less than the allowed amount"
}
ExampleToken.totalSupply = ExampleToken.totalSupply + amount
self.allowedAmount = self.allowedAmount - amount
emit TokensMinted(amount: amount, type: self.getType())
return <-create Vault(balance: amount)
}

init(allowedAmount: UFix64) {
self.allowedAmount = allowedAmount
}
}

init() {
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)

let storagePath = vault.VaultStoragePath
let receiverPath = vault.ReceiverPublicPath
let balancePath = vault.BalancePublicPath

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

// Create a public capability to the stored Vault that only exposes
// the `deposit` method through the `Receiver` interface
//
self.account.link<&{FungibleToken.Receiver}>(
receiverPath,
target: storagePath
)

// Create a public capability to the stored Vault that only exposes
// the `balance` field through the `Balance` interface
//
self.account.link<&ExampleToken.Vault{FungibleToken.Balance}>(
balancePath,
target: storagePath
)

let admin <- create Administrator()
self.account.save(<-admin, to: self.AdminStoragePath)
}
}
14 changes: 14 additions & 0 deletions contracts/FungibleToken-v2-ContractInterface.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import FungibleToken from "./FungibleToken-v2.cdc"

pub contract interface FungibleTokenInterface {
joshuahannan marked this conversation as resolved.
Show resolved Hide resolved

/// Contains the total supply of the fungible token
pub var totalSupply: UFix64
joshuahannan marked this conversation as resolved.
Show resolved Hide resolved

/// Function to return the types that the contract implements
pub fun getVaultTypes(): [FungibleToken.VaultInfo] {
joshuahannan marked this conversation as resolved.
Show resolved Hide resolved
post {
result.length > 0: "Must indicate what fungible token types this contract defines"
}
}
}
Loading