Coder Social home page Coder Social logo

onflow / flow-ft Goto Github PK

View Code? Open in Web Editor NEW
141.0 37.0 56.0 3.27 MB

The Fungible Token standard on the Flow Blockchain

Home Page: https://onflow.org

License: The Unlicense

Go 18.33% Makefile 0.37% Cadence 81.30%
blockchain smart-contracts linear-types fungible-tokens onflow

flow-ft's Introduction

Fungible Token Standard

This is a description of the Flow standard for fungible token contracts. It is meant to contain the minimum requirements to implement a safe, secure, easy to understand, and easy to use fungible token contract. It also includes an example implementation to show how a concrete smart contract would actually implement the interface.

The version of the contracts in the master branch is the Cadence 1.0 version of the contracts and is not the same as the ones that are currently deployed to testnet and mainnet. See the cadence-0.42 branch for the currently deployed versions.

What is Flow?

Flow is a new blockchain for open worlds. Read more about it here.

What is Cadence?

Cadence is a new Resource-oriented programming language for developing smart contracts for the Flow Blockchain. Read more about it here and see its implementation here

We recommend that anyone who is reading this should have already completed the Cadence Tutorials so they can build a basic understanding of the programming language.

Resource-oriented programming, and by extension Cadence, is the perfect programming environment for currencies, because users are able to store their tokens directly in their accounts and transact peer-to-peer. Please see the blog post about resources to understand why they are perfect for digital assets.

Import Addresses

The FungibleToken, FungibleTokenMetadataViews, and FungibleTokenSwitchboard contracts are already deployed on various networks. You can import them in your contracts from these addresses. There is no need to deploy them yourself.

Network Contract Address
Emulator 0xee82856bf20e2aa6
PreviewNet 0xa0225e7000ac82a9
Testnet/Crescendo 0x9a0766d93b6608b7
Sandboxnet 0xe20612a0776ca4bf
Mainnet 0xf233dcee88fe0abe

The Burner contract is also deployed to these addresses, but should only be used in Cadence 1.0 FungibleToken implementations of the standard.

Basics of the Standard:

The code for the standard is in contracts/FungibleToken.cdc. An example implementation of the standard that simulates what a simple token would be like is in contracts/ExampleToken.cdc.

The exact smart contract that is used for the official Flow Network Token (FlowToken) is in the flow-core-contracts repository.

Example transactions that users could use to interact with fungible tokens are located in the transactions/ directory. These templates are mostly generic and can be used with any fungible token implementation by providing the correct addresses, names, and values.

The standard consists of a contract interface called FungibleToken that defines important functionality for token implementations. Contracts are expected to define a resource that implement the FungibleToken.Vault resource interface. A Vault represents the tokens that an account owns. Each account that owns tokens will have a Vault stored in its account storage. Users call functions on each other's Vaults to send and receive tokens.

The standard uses unsigned 64-bit fixed point numbers UFix64 as the type to represent token balance information. This type has 8 decimal places and cannot represent negative numbers.

Core Features (All contained in the main FungibleToken interface)

Balance Interface

Specifies that the implementing type must have a UFix64 balance field.

  • access(all) var balance: UFix64

Provider Interface

Defines a withdraw function for withdrawing a specific amount of tokens as amount.

  • access(all) fun withdraw(amount: UFix64): @{FungibleToken.Vault}
    • Conditions
      • the returned Vault's balance must equal the amount withdrawn
      • The amount withdrawn must be less than or equal to the balance
      • The resulting balance must equal the initial balance - amount
  • Users can give other accounts a persistent Capability or ephemeral reference to their Vault cast as a Provider to allow them to withdraw and send tokens for them. A contract can define any custom logic to govern the amount of tokens that can be withdrawn at a time with a Provider. This can mimic the approve, transferFrom functionality of ERC20.
  • FungibleToken.Withdrawn event
    • Event that is emitted automatically to indicate how much was withdrawn and from what account the Vault is stored in. If the Vault is not in account storage when the event is emitted, from will be nil.
    • Contracts do not have to emit their own events, the standard events will automatically be emitted from the interface contract with values identifying the relevant Vault.

Defines an isAvailableToWithdraw() function to ask a Provider if the specified number of tokens can be withdrawn from the implementing type.

Receiver Interface

Defines functionality to depositing fungible tokens into a resource object.

  • deposit() function:
    • access(all) fun deposit(from: @{FungibleToken.Vault})
    • Conditions
      • from balance must be non-zero
      • The resulting balance must be equal to the initial balance + the balance of from
    • It is important that if you are making your own implementation of the fungible token interface that you cast the input to deposit as the type of your token. let vault <- from as! @ExampleToken.Vault The interface specifies the argument as @{FungibleToken.Vault}, any resource that satisfies this can be sent to the deposit function. The interface checks that the concrete types match, but you'll still need to cast the Vault before incrementing the receiving Vault's balance.
  • deposit event
    • FungibleToken.Deposited event from the standard that indicates how much was deposited and to what account the Vault is stored in.
      • If the Vault is not in account storage when the event is emitted, to will be nil.
      • This event is emitted from the interface contract automatically on any deposit, so projects do not need to define and emit their own events.

Defines Functionality for Getting Supported Vault Types

  • Some resource types can accept multiple different vault types in their deposit functions, so the getSupportedVaultTypes() and isSupportedVaultType() functions allow callers to query a resource that implements Receiver to see if the Receiver accepts their desired Vault type in its deposit function.

Users could create custom Receivers to trigger special code when transfers to them happen, like forwarding the tokens to another account, splitting them up, and much more.

Vault Interface

Interface that inherits from Provider, Receiver, Balance, ViewResolver.Resolver, and Burner.Burnable and provides additional pre and post conditions.

The ViewResolver.Resolver interface defines functionality for retrieving metadata about a particular resource object. Fungible Token metadata is described below.

See the comments in the Burner contract for context about it. Basically, it defines functionality for tokens to have custom logic when those tokens are destroyed.

Creating an empty Vault resource

Defines functionality in the contract to create a new empty vault of of the contract's defined type.

  • access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault}
  • Defined in the contract
  • To create an empty Vault, the caller calls the function and provides the Vault Type that they want. They get a vault back and can store it in their storage.
  • Conditions:
    • the balance of the returned Vault must be 0

Comparison to Similar Standards in Ethereum

This spec covers much of the same ground that a spec like ERC-20 covers, but without most of the downsides.

  • Tokens cannot be sent to accounts or contracts that don't have owners or don't understand how to use them, because an account has to have a Vault in its storage to receive tokens. No safetransfer is needed.
  • If the recipient is a contract that has a stored Vault, the tokens can just be deposited to that Vault without having to do a clunky approve, transferFrom
  • Events are defined in the contract for withdrawing and depositing, so a recipient will always be notified that someone has sent them tokens with the deposit event.
  • The approve, transferFrom pattern is not included, so double spends are not permitted
  • Transfers can trigger actions because users can define custom Receivers to execute certain code when a token is sent.
  • Cadence integer types protect against overflow and underflow, so a SafeMath-equivalent library is not needed.

FT Metadata

FT Metadata is represented in a flexible and modular way using both the standard proposed in FLIP-0636 and the standard proposed in FLIP-1087.

A guide for NFT metadata is provided on the docs site. Many of the concepts described there also apply to fungible tokens, so it is useful to read for any Cadence developer.

When writing an FT contract interface, your contract will implement the FungibleToken contract interface which already inherits from the ViewResolver contract interface, so you will be required to implement the metadata functions. Additionally, your Vault will also implement the ViewResolver.Resolver by default, which allows your Vault resource to implement one or more metadata types called views.

Views do not specify or require how to store your metadata, they only specify the format to query and return them, so projects can still be flexible with how they store their data.

Fungible token Metadata Views

The FungibleTokenMetadataViews contract defines four new views that can used to communicate any fungible token information:

  1. FTView: A view that wraps the two other views that actually contain the data.
  2. FTDisplay: The view that contains all the information that will be needed by other dApps to display the fungible token: name, symbol, description, external URL, logos and links to social media.
  3. FTVaultData: The view that can be used by other dApps to interact programmatically with the fungible token, providing the information about the public and storage paths used by default by the token, the public linked types for exposing capabilities and the function for creating new empty vaults. You can use this view to setup an account using the vault stored in other account without the need of importing the actual token contract.
  4. TotalSupply: Specifies the total supply of the given token.

How to implement metadata

The Example Token contract shows how to implement metadata views for fungible tokens.

How to read metadata

In this repository you can find examples on how to read metadata, accessing the ExampleToken display (name, symbol, logos, etc.) and its vault data (paths, linked types and the method to create a new vault).

Latter using that reference you can call methods defined in the Fungible Token Metadata Views contract that will return you the structure containing the desired information:

Bonus Features

The following features could each be defined as a separate standard. It would be good to make standards for these, but not necessary to include in the main standard interface and are not currently defined in this example.

  • Scoped Provider This refers to restricting a Provider capability to only be able to withdraw a specific amount of tokens from someone else's Vault This is currently being worked on.

  • Pausing Token transfers (maybe a way to prevent the contract from being imported)

  • Cloning the token to create a new token with the same distribution

  • Restricted ownership (For accredited investors and such)

  • allowlisting

  • denylisting

How to use the Fungible Token contract

To use the Flow Token contract as is, you need to follow these steps:

  1. If you are using any network or the playground, there is no need to deploy the FungibleToken definition to accounts yourself. It is a pre-deployed interface in the emulator, testnet, mainnet, and playground and you can import definition from those accounts:
    • 0xee82856bf20e2aa6 on emulator
    • 0xa0225e7000ac82a9 on previewnet
    • 0x9a0766d93b6608b7 on testnet/crescendo
    • 0xf233dcee88fe0abe on mainnet
  2. Deploy the ExampleToken definition, making sure to import the FungibleToken interface.
  3. You can use the get_balance.cdc or get_supply.cdc scripts to read the balance of a user's Vault or the total supply of all tokens, respectively.
  4. Use the setup_account.cdc on any account to set up the account to be able to use ExampleToken.
  5. Use the transfer_tokens.cdc transaction file to send tokens from one user with a Vault in their account storage to another user with a Vault in their account storage.
  6. Use the mint_tokens.cdc transaction with the admin account to mint new tokens.
  7. Use the burn_tokens.cdc transaction with the admin account to burn tokens.
  8. Use the create_minter.cdc transaction to create a new MintandBurn resource and store it in a new Admin's account.

Fungible Token Switchboard

FungibleTokenSwitchboard.cdc, allows users to receive payments in different fungible tokens using a single &{FungibleToken.Receiver} placed in a standard receiver path /public/GenericFTReceiver.

How to use it

Users willing to use the Fungible Token Switchboard will need to setup their accounts by creating a new FungibleTokenSwitchboard.Switchboard resource and saving it to their accounts at the FungibleTokenSwitchboard.StoragePath path.

This can be accomplished by executing the transaction found in this repository transactions/switchboard/setup_account.cdc. This transaction will create and save a Switchboard resource to the signer's account, and it also will create the needed public capabilities to access it. After setting up their switchboard, in order to make it support receiving a certain token, users will need to add the desired token's receiver capability to their switchboard resource.

Adding a new vault to the switchboard

When a user wants to receive a new fungible token through their switchboard, they will need to add a new public capability linked to said FT to their switchboard resource. This can be accomplished in two different ways:

  1. Adding a single capability using addNewVault(capability: Capability<&{FungibleToken.Receiver}>)

    This function will panic if is not possible to .borrow() a reference to a &{FungibleToken.Receiver} from the passed capability.

  2. Adding one or more capabilities using the paths where they are stored using addNewVaultsByPath(paths: [PublicPath], address: Address). This is shown in the batch_add_vault_wrapper_capabilities.cdc transaction

    • When using this method, an array of PublicPath objects should be passed along with the Address of the account from where the vaults' capabilities should be retrieved.

    This function won't panic, instead it will just not add to the @Switchboard any capability which can not be retrieved from any of the provided PublicPaths. It will also ignore any type of &{FungibleToken.Receiver} that is already present on the @Switchboard

  3. Adding a capability to a receiver specifying which type of token will be deposited there using addNewVaultWrapper(capability: Capability<&{FungibleToken.Receiver}>, type: Type). This method can be used to link a token forwarder or any other wrapper to the switchboard. Once the Forwarder has been properly created containing the capability to an actual @FungibleToken.Vault, this method can be used to link the @Forwarder to the switchboard to deposit the specified type of Fungible Token. In the template transaction switchboard/add_vault_wrapper_capability.cdc, we assume that the signer has a forwarder containing a capability to an @ExampleToken.Vault resource:

Removing a vault from the switchboard

If a user no longer wants to be able to receive deposits from a certain FT, or if they want to update the provided capability for one of them, they will need to remove the vault from the switchboard. This can be accomplished by using removeVault(capability: Capability<&{FungibleToken.Receiver}>). This can be observed in the template transaction transactions/switchboard/remove_vault_capability.cdc:

This function will panic if is not possible to .borrow() a reference to a &{FungibleToken.Receiver} from the passed capability.

Transferring tokens through the switchboard

The Fungible Token Switchboard provides two different ways of depositing tokens to it, using the deposit(from: @{FungibleToken.Vault}) method enforced by the {FungibleToken.Receiver} or using the safeDeposit(from: @FungibleToken.Vault): @FungibleToken:

  1. Using the first method will be just the same as depositing to &{FungibleToken.Receiver}. The path for the Switchboard receiver is defined in FungibleTokenSwitchboard.ReceiverPublicPath, the generic receiver path /public/GenericFTReceiver that can also be obtained from the NFT MetadataViews contract. An example of how to do this can be found in the transaction template on this repo transactions/switchboard/transfer_tokens.cdc.

  2. The safeDeposit(from: @FungibleToken.Vault): @FungibleToken works in a similar way, with the difference that it will not panic if the desired FT Vault can not be obtained from the Switchboard. The method will return the passed vault, empty if the funds were deposited successfully or still containing the funds if the transfer of the funds was not possible. Keep in mind that when using this method on a transaction you will always have to deal with the returned resource. An example of this can be found on transactions/switchboard/safe_transfer_tokens.cdc:

Running Automated Tests

There are two sets of tests in the repo, Cadence tests and Go tests. The Cadence tests are much more straightforward and are all written in Cadence, so we recommend following those.

Cadence Testing Framework

The Cadence tests are located in the tests/ repository. They are written in Cadence and can be run directly from the command line using the Flow CLI. Make sure you are using the latest Cadence 1.0 CLI verion.

flow test --cover --covercode="contracts" tests/*.cdc

Go tests

You can find automated tests in the lib/go/test/token_test.go file. It uses the transaction templates that are contained in the lib/go/templates/transaction_templates.go file. You can run them by navigating to the lib/go/test/ directory and running go test -v. If you make changes to the contracts or transactions in between running tests, you will need to run make generate from the lib/go/ directory to generate the assets used in the tests.

License

The works in these folders are under the Unlicense:

flow-ft's People

Contributors

10thfloor avatar alilloig avatar benjaminkvm avatar brianastrove avatar brodieve avatar codingone21 avatar dependabot[bot] avatar franklywatson avatar j1010001 avatar janezpodhostnik avatar jkan2 avatar joshuahannan avatar kay-zee avatar m-peter avatar mastereye-07 avatar nialexsan avatar psiemens avatar satyamakgec avatar timmymmit avatar turbolent avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flow-ft's Issues

Test type checking in FungibleToken interface

Issue To Be Solved

There might be a solution to not being able to assert the type of a token vault with the interface.

(Optional): Suggest A Solution

  • Use pre-condition in #cadence channel

Incorrect attribute when emitting `VaultCapabilityAdded` in FungibleTokenSwitchboard

Instructions

Within FungibleTokenSwitchboard contract, there is an Incorrect emission of switchboardOwner attribute in event emission.

In this line: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleTokenSwitchboard.cdc#L108
The event VaultCapabilityAdded should emit self.owner?.address for the switchboardOwner attribute.
It currently points to the address of the capability owner rather than the switchboard owner.

This line in the FungibleTokenSwitchboard emits the correct switchboardOwner attribute: https://github.com/onflow/flow-ft/blob/master/contracts/FungibleTokenSwitchboard.cdc#L74

Update contract import schema

Issue To Be Solved

With flow-cli super commands came a new import syntax. The imports in this repo need to be reformatted as an example of up-to-date Cadence implementation consistent and compatible with other dev tools on Flow.

Suggest A Solution

Update contract imports as described in Flow CLI super commands docs and restructure directories in compliance with super commands setup.

Specifically, this means replacing the relative import schema of

import FungibleToken from "./FungibleToken.cdc"

To

import "FungibleToken"

Context

This repo will be used as an example implementation for an upcoming grant wishlist item.

Update Contract Import Syntax to the new Standard

Issue To Be Solved

We have a new standard for contract imports: https://github.com/onflow/flow/blob/master/flips/2022-03-23-contract-imports-syntax.md

Suggest A Solution

Create design proposal for the FT Metadata Views

This belongs to #73 epic

Issue To Be Solved

We need to develop a design document showing the plan for implementing Metadata Views for fungible tokens.

Suggest A Solution

Create a design document similar to a FLIP document with motivation, design summary, tradeoffs, docs plans, etc

Draft proposal (will be posted in #73 when complete)

FungibleTokenSwitchboard is not interoperable with TokenForwarding

Problem

If there are multiple &TokenForwarding{FungibleToken.Receiver} Forwarders on an account, such as
one for DapperUtilityCoin and one for FlowUtilityToken, which is currently used for Dapper Wallet accounts,
trying to add both, will result in only a single one being remembered by TokenForwarding,
because after .borrowing they resolve to the same Type.
Neither would be unusable anyway, because the type does not match the input (deposit) vault type.

It is impossible to tell which token is handled by a Forwarder, as that information is not accessible from the resource.

Proposed solution

An (optional?) parameter for addNewVault method, to override the deposit Type that the capability handles.

Reproduction

Setups:

/// Setup Switchboard 

transaction {

    prepare(acct: AuthAccount) {

        if acct.borrow<&FungibleTokenSwitchboard.Switchboard>
          (from: FungibleTokenSwitchboard.StoragePath) == nil {

            acct.save(
                <- FungibleTokenSwitchboard.createSwitchboard(),
                to: FungibleTokenSwitchboard.StoragePath)

            acct.link<&FungibleTokenSwitchboard.Switchboard{FungibleToken.Receiver}>(
                FungibleTokenSwitchboard.ReceiverPublicPath,
                target: FungibleTokenSwitchboard.StoragePath
            )

            acct.link<&FungibleTokenSwitchboard.Switchboard{FungibleTokenSwitchboard.SwitchboardPublic/*, FungibleToken.Receiver*/}>(
                FungibleTokenSwitchboard.PublicPath,
                target: FungibleTokenSwitchboard.StoragePath
            )
        }
    }
}

/// Setup forwarders

transaction(dapperAddress: Address) {

    prepare(signer: AuthAccount) {
        let dapper = getAccount(dapperAddress)

        let ducReceiverPublicPath = /public/dapperUtilityCoinReceiver
        let ducReceiverStoragePath = /storage/dapperUtilityCoinReceiver
        let ducReceiver = signer.getCapability<&{FungibleToken.Receiver}>(ducReceiverPublicPath)!

        let futReceiverPublicPath = /public/flowUtilityTokenReceiver
        let futReceiverStoragePath = /storage/flowUtilityTokenReceiver
        let futReceiver = signer.getCapability<&{FungibleToken.Receiver}>(futReceiverPublicPath)!

        if ducReceiver.borrow() == nil {
            let dapperDUCReceiver = dapper.getCapability(ducReceiverPublicPath)!
            let ducForwarder <- TokenForwarding.createNewForwarder(recipient: dapperDUCReceiver)
            signer.save(<-ducForwarder, to: ducReceiverStoragePath)

            signer.link<&DapperUtilityCoin.Vault{FungibleToken.Receiver}>(
                ducReceiverPublicPath,
                target: ducReceiverStoragePath
            )
        }

        if futReceiver.borrow() == nil {
            let dapperFUTReceiver = dapper.getCapability(futReceiverPublicPath)!
            let futForwarder <- TokenForwarding.createNewForwarder(recipient: dapperFUTReceiver)
            signer.save(<-futForwarder, to: futReceiverStoragePath)

            signer.link<&FlowUtilityToken.Vault{FungibleToken.Receiver}>(
                futReceiverPublicPath,
                target: futReceiverStoragePath
            )
        }
    }
}

/// Attach to Switchboard

transaction {
    let switchboardRef:  &FungibleTokenSwitchboard.Switchboard
    let vaultPaths: [PublicPath]
    let address: Address

    prepare(signer: AuthAccount) {
        self.address = signer.address

        let ducReceiverPublicPath = /public/dapperUtilityCoinReceiver
        let futReceiverPublicPath = /public/flowUtilityTokenReceiver

        self.vaultPaths = []
        self.vaultPaths.append(ducReceiverPublicPath)
        self.vaultPaths.append(futReceiverPublicPath)

        let ducTokenVaultCapabilty = signer.getCapability<&{FungibleToken.Receiver}>(ducReceiverPublicPath)
        assert(ducTokenVaultCapabilty.check(), message: "Signer does not have a DUC receiver capability")

        let futTokenVaultCapabilty = signer.getCapability<&{FungibleToken.Receiver}>(futReceiverPublicPath)
        assert(futTokenVaultCapabilty.check(), message: "Signer does not have a FUT receiver capability")

        self.switchboardRef = signer.borrow<&FungibleTokenSwitchboard.Switchboard>
            (from: FungibleTokenSwitchboard.StoragePath)
            ?? panic("Could not borrow reference to switchboard")
    }

    execute {
        self.switchboardRef.addNewVaultsByPath(paths: self.vaultPaths, address: self.address)
    }
}

Script:

      pub fun main(addr: Address): [Type] {
        let sbC = getAccount(addr)!
          .getCapability<&{FungibleTokenSwitchboard.SwitchboardPublic/*, FungibleToken.Receiver*/}>(FungibleTokenSwitchboard.PublicPath)
        
        //let sbR = sbC.borrow()! 
        let sbS = (sbC as? Capability<&{FungibleTokenSwitchboard.SwitchboardPublic}>)!.borrow()!
        
        return sbS.getVaultTypes()
   }

Result: (one entry, while two capabilities were added earlier, so one overwrites the other)

  console.log
    [        
      {      
        kind: 'Resource',
        typeID: 'A.179b6b1cb6755e31.TokenForwarding.Forwarder',
        fields: [ [Object], [Object] ],
        initializers: [],
        type: ''
      }
    ]

Update flow-ft contracts to comply with Stable Cadence changes

Problem

Changes to Cadence are coming and we'll want to make sure that the contracts function beyond the upcoming breaking changes.

See this epic for additional context.

Acceptance Criteria

Assignee should refer to the merged and upcoming changes in this forum post and cover all occurrences of incompatible syntax in existing repo contracts. All contracts should be compatible with the soon to be updated version of Cadence.

feat: Allow public Switchboard to be used as FT Receiver

Issue To Be Solved

FungibleTokenSwitchboard.PublicPath exposes {FungibleTokenSwitchboard.SwitchboardPublic} which includes the deposit method,
but is not usable as &{FungibleToken.Receiver}

Suggest A Solution

Change the linked type to <&FungibleTokenSwitchboard.Switchboard{FungibleTokenSwitchboard.SwitchboardPublic, FungibleToken.Receiver}> on

acct.link<&FungibleTokenSwitchboard.Switchboard{FungibleTokenSwitchboard.SwitchboardPublic}>(

Put assertions and post-conditions in the generic transactions

Issue To Be Solved

If we are going to encourage projects to use the generic token transactions, we should add assertions and/or post-conditions to them to make sure that they are behaving properly.

Suggest A Solution

Add these conditions:

  • Make sure the withdrawn tokens are the same type that is defined by the specified contract
  • Make sure it has the correct balance
  • Think of others

Enhancement: Make Templates Customizable

Issue To Be Solved

The transaction templates in the fungible_test.go file are currently specifically tailored to the FlowToken.cdc example token. We want them to be able to be imported and uses for other fungible tokens so the code doesn't need to be copied and pasted.

(Optional): Suggest A Solution

  • Replace the parts of the transaction templates that are specific to the FlowToken example with string fields that can be filled with any contract
  • Update fungible_test.go to use these new templates correctly

Create NPM package

Context:

dApp developers need smart contract access to interact with it, It is hard to keep a copy of the contract and transactions within the frontend codebase and it comes with operation overhead if anything gets changed within the contract or transaction.

Solution
Create an npm package for better interaction with the dApp and easy to maintain.

cannot borrowViewResolver for some NFT

Instructions

Please fill out the template below to the best of your ability and include a label indicating which tool/service you were working with when you encountered the problem.

Problem

Failed to execute script to obtain NFT View.

Steps to Reproduce

Use go test:

package flow_contracts

import (
	"context"
	"github.com/onflow/cadence"
	"github.com/onflow/flow-go-sdk"
	"github.com/onflow/flow-go-sdk/access/http"
	"github.com/onflow/flow-nft/lib/go/templates"
	"strings"
	"testing"
)

func TestGetURI(t *testing.T) {
	contracts := map[string]uint64{
		"A.9e6cdb88e34fa1f3.Player":                 5,
		"A.cbf10523da1a9ee9.NewFunThingPack":        121,
		"A.9847e3f695f053d0.SilverlightLocationNFT": 112490708,
	}
	for contract, tokenId := range contracts {
		splits := strings.Split(contract, ".")
		contractAddr := flow.HexToAddress(splits[1])
		contractName := splits[2]
		fetchMetadata(t, contractAddr, contractName, tokenId)
	}
}
func fetchMetadata(t *testing.T, contractAddr flow.Address, contractName string, tokenId uint64) {
	nftAddr := flow.HexToAddress("0x631e88ae7f1d7c20")
	metadataAddr := flow.HexToAddress("0x631e88ae7f1d7c20")
	script := templates.GenerateGetNFTViewScript(nftAddr, contractAddr, metadataAddr)
	script = []byte(strings.ReplaceAll(string(script), "ExampleNFT", contractName))
	flowClient, err := http.NewClient("https://rest-testnet.onflow.org/v1")
	if err != nil {
		t.Error(err)
		return
	}
	args := []cadence.Value{
		cadence.NewAddress(contractAddr),
		cadence.NewUInt64(tokenId),
	}
	value, err := flowClient.ExecuteScriptAtLatestBlock(context.Background(), script, args)
	if err != nil {
		t.Error(err)
		return
	}
	t.Log(value)
}

The output is:

=== RUN   TestGetURI
    uri_test.go:45: s.ee9e7e2845a585e7933248d551ceb7ccdf6add5f7b2d8895b3ccf1e387fd1a18.NFTView(id: 5, uuid: 109165795, name: "name box", description: "description NFT", thumbnail: "https://www.gamifylabs.io/img/hero140.fb6636ad.png", royalties: [], externalURL: "https://example-nft.onflow.org/5", collectionPublicPath: /public/PlayerCollection, collectionStoragePath: /storage/PlayerCollection, collectionProviderPath: /private/PlayerCollection, collectionPublic: "&A.9e6cdb88e34fa1f3.Player.Collection{A.9e6cdb88e34fa1f3.Player.PlayerCollectionPublic}", collectionPublicLinkedType: "&A.9e6cdb88e34fa1f3.Player.Collection{A.9e6cdb88e34fa1f3.Player.PlayerCollectionPublic,A.631e88ae7f1d7c20.NonFungibleToken.CollectionPublic,A.631e88ae7f1d7c20.NonFungibleToken.Receiver,A.631e88ae7f1d7c20.MetadataViews.ResolverCollection}", collectionProviderLinkedType: "&A.9e6cdb88e34fa1f3.Player.Collection{A.9e6cdb88e34fa1f3.Player.PlayerCollectionPublic,A.631e88ae7f1d7c20.NonFungibleToken.CollectionPublic,A.631e88ae7f1d7c20.NonFungibleToken.Provider,A.631e88ae7f1d7c20.MetadataViews.ResolverCollection}", collectionName: "The Example Collection", collectionDescription: "This collection is used as an example to help you develop your next Flow NFT.", collectionExternalURL: "https://example-nft.onflow.org/", collectionSquareImage: "https://www.gamifylabs.io/img/index_nav_logo.6a9cf2d9.png", collectionBannerImage: "https://www.gamifylabs.io/img/index_nav_logo.6a9cf2d9.png", collectionSocials: {"twitter": "https://twitter.com/flow_blockchain"}, traits: A.631e88ae7f1d7c20.MetadataViews.Traits(traits: [A.631e88ae7f1d7c20.MetadataViews.Trait(name: "mintedBlock", value: 79065448, displayType: nil, rarity: nil), A.631e88ae7f1d7c20.MetadataViews.Trait(name: "mintedTime", value: 1662689706.00000000, displayType: "Date", rarity: nil), A.631e88ae7f1d7c20.MetadataViews.Trait(name: "batch", value: "A01", displayType: "batch", rarity: nil), A.631e88ae7f1d7c20.MetadataViews.Trait(name: "dna", value: "?002211146065020681109288090821135208772072450908210320074300928811", displayType: nil, rarity: A.631e88ae7f1d7c20.MetadataViews.Rarity(score: 10.00000000, max: 100.00000000, description: "origin data"))]))
    uri_test.go:42: executing script aW1wb3J0IE5ld0Z1blRoaW5nUGFjayBmcm9tIDB4Y2JmMTA1MjNkYTFhOWVlOQppbXBvcnQgTWV0YWRhdGFWaWV3cyBmcm9tIDB4NjMxZTg4YWU3ZjFkN2MyMAoKcHViIHN0cnVjdCBORlRWaWV3IHsKICAgIHB1YiBsZXQgaWQ6IFVJbnQ2NAogICAgcHViIGxldCB1dWlkOiBVSW50NjQKICAgIHB1YiBsZXQgbmFtZTogU3RyaW5nCiAgICBwdWIgbGV0IGRlc2NyaXB0aW9uOiBTdHJpbmcKICAgIHB1YiBsZXQgdGh1bWJuYWlsOiBTdHJpbmcKICAgIHB1YiBsZXQgcm95YWx0aWVzOiBbTWV0YWRhdGFWaWV3cy5Sb3lhbHR5XQogICAgcHViIGxldCBleHRlcm5hbFVSTDogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25QdWJsaWNQYXRoOiBQdWJsaWNQYXRoCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25TdG9yYWdlUGF0aDogU3RvcmFnZVBhdGgKICAgIHB1YiBsZXQgY29sbGVjdGlvblByb3ZpZGVyUGF0aDogUHJpdmF0ZVBhdGgKICAgIHB1YiBsZXQgY29sbGVjdGlvblB1YmxpYzogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25QdWJsaWNMaW5rZWRUeXBlOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvblByb3ZpZGVyTGlua2VkVHlwZTogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25OYW1lOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvbkRlc2NyaXB0aW9uOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvbkV4dGVybmFsVVJMOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvblNxdWFyZUltYWdlOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvbkJhbm5lckltYWdlOiBTdHJpbmcKICAgIHB1YiBsZXQgY29sbGVjdGlvblNvY2lhbHM6IHtTdHJpbmc6IFN0cmluZ30KICAgIHB1YiBsZXQgdHJhaXRzOiBNZXRhZGF0YVZpZXdzLlRyYWl0cwoKICAgIGluaXQoCiAgICAgICAgaWQ6IFVJbnQ2NCwKICAgICAgICB1dWlkOiBVSW50NjQsCiAgICAgICAgbmFtZTogU3RyaW5nLAogICAgICAgIGRlc2NyaXB0aW9uOiBTdHJpbmcsCiAgICAgICAgdGh1bWJuYWlsOiBTdHJpbmcsCiAgICAgICAgcm95YWx0aWVzOiBbTWV0YWRhdGFWaWV3cy5Sb3lhbHR5XSwKICAgICAgICBleHRlcm5hbFVSTDogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25QdWJsaWNQYXRoOiBQdWJsaWNQYXRoLAogICAgICAgIGNvbGxlY3Rpb25TdG9yYWdlUGF0aDogU3RvcmFnZVBhdGgsCiAgICAgICAgY29sbGVjdGlvblByb3ZpZGVyUGF0aDogUHJpdmF0ZVBhdGgsCiAgICAgICAgY29sbGVjdGlvblB1YmxpYzogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25QdWJsaWNMaW5rZWRUeXBlOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvblByb3ZpZGVyTGlua2VkVHlwZTogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25OYW1lOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvbkRlc2NyaXB0aW9uOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvbkV4dGVybmFsVVJMOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvblNxdWFyZUltYWdlOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvbkJhbm5lckltYWdlOiBTdHJpbmcsCiAgICAgICAgY29sbGVjdGlvblNvY2lhbHM6IHtTdHJpbmc6IFN0cmluZ30sCiAgICAgICAgdHJhaXRzOiBNZXRhZGF0YVZpZXdzLlRyYWl0cwogICAgKSB7CiAgICAgICAgc2VsZi5pZCA9IGlkCiAgICAgICAgc2VsZi51dWlkID0gdXVpZAogICAgICAgIHNlbGYubmFtZSA9IG5hbWUKICAgICAgICBzZWxmLmRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb24KICAgICAgICBzZWxmLnRodW1ibmFpbCA9IHRodW1ibmFpbAogICAgICAgIHNlbGYucm95YWx0aWVzID0gcm95YWx0aWVzCiAgICAgICAgc2VsZi5leHRlcm5hbFVSTCA9IGV4dGVybmFsVVJMCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uUHVibGljUGF0aCA9IGNvbGxlY3Rpb25QdWJsaWNQYXRoCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uU3RvcmFnZVBhdGggPSBjb2xsZWN0aW9uU3RvcmFnZVBhdGgKICAgICAgICBzZWxmLmNvbGxlY3Rpb25Qcm92aWRlclBhdGggPSBjb2xsZWN0aW9uUHJvdmlkZXJQYXRoCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uUHVibGljID0gY29sbGVjdGlvblB1YmxpYwogICAgICAgIHNlbGYuY29sbGVjdGlvblB1YmxpY0xpbmtlZFR5cGUgPSBjb2xsZWN0aW9uUHVibGljTGlua2VkVHlwZQogICAgICAgIHNlbGYuY29sbGVjdGlvblByb3ZpZGVyTGlua2VkVHlwZSA9IGNvbGxlY3Rpb25Qcm92aWRlckxpbmtlZFR5cGUKICAgICAgICBzZWxmLmNvbGxlY3Rpb25OYW1lID0gY29sbGVjdGlvbk5hbWUKICAgICAgICBzZWxmLmNvbGxlY3Rpb25EZXNjcmlwdGlvbiA9IGNvbGxlY3Rpb25EZXNjcmlwdGlvbgogICAgICAgIHNlbGYuY29sbGVjdGlvbkV4dGVybmFsVVJMID0gY29sbGVjdGlvbkV4dGVybmFsVVJMCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uU3F1YXJlSW1hZ2UgPSBjb2xsZWN0aW9uU3F1YXJlSW1hZ2UKICAgICAgICBzZWxmLmNvbGxlY3Rpb25CYW5uZXJJbWFnZSA9IGNvbGxlY3Rpb25CYW5uZXJJbWFnZQogICAgICAgIHNlbGYuY29sbGVjdGlvblNvY2lhbHMgPSBjb2xsZWN0aW9uU29jaWFscwogICAgICAgIHNlbGYudHJhaXRzID0gdHJhaXRzCiAgICB9Cn0KCnB1YiBmdW4gbWFpbihhZGRyZXNzOiBBZGRyZXNzLCBpZDogVUludDY0KTogTkZUVmlldyB7CiAgICBsZXQgYWNjb3VudCA9IGdldEFjY291bnQoYWRkcmVzcykKCiAgICBsZXQgY29sbGVjdGlvbiA9IGFjY291bnQKICAgICAgICAuZ2V0Q2FwYWJpbGl0eShOZXdGdW5UaGluZ1BhY2suQ29sbGVjdGlvblB1YmxpY1BhdGgpCiAgICAgICAgLmJvcnJvdzwme01ldGFkYXRhVmlld3MuUmVzb2x2ZXJDb2xsZWN0aW9ufT4oKQogICAgICAgID8/IHBhbmljKCJDb3VsZCBub3QgYm9ycm93IGEgcmVmZXJlbmNlIHRvIHRoZSBjb2xsZWN0aW9uIikKCiAgICBsZXQgdmlld1Jlc29sdmVyID0gY29sbGVjdGlvbi5ib3Jyb3dWaWV3UmVzb2x2ZXIoaWQ6IGlkKSEKCiAgICBsZXQgbmZ0VmlldyA9IE1ldGFkYXRhVmlld3MuZ2V0TkZUVmlldyhpZDogaWQsIHZpZXdSZXNvbHZlciA6IHZpZXdSZXNvbHZlcikKCiAgICBsZXQgY29sbGVjdGlvblNvY2lhbHM6IHtTdHJpbmc6IFN0cmluZ30gPSB7fQogICAgZm9yIGtleSBpbiBuZnRWaWV3LmNvbGxlY3Rpb25EaXNwbGF5IS5zb2NpYWxzLmtleXMgewogICAgICAgIGNvbGxlY3Rpb25Tb2NpYWxzW2tleV0gPSBuZnRWaWV3LmNvbGxlY3Rpb25EaXNwbGF5IS5zb2NpYWxzW2tleV0hLnVybAogICAgfQoKCiAgICByZXR1cm4gTkZUVmlldygKICAgICAgICBpZDogbmZ0Vmlldy5pZCwKICAgICAgICB1dWlkOiBuZnRWaWV3LnV1aWQsCiAgICAgICAgbmFtZTogbmZ0Vmlldy5kaXNwbGF5IS5uYW1lLAogICAgICAgIGRlc2NyaXB0aW9uOiBuZnRWaWV3LmRpc3BsYXkhLmRlc2NyaXB0aW9uLAogICAgICAgIHRodW1ibmFpbDogbmZ0Vmlldy5kaXNwbGF5IS50aHVtYm5haWwudXJpKCksCiAgICAgICAgcm95YWx0aWVzOiBuZnRWaWV3LnJveWFsdGllcyEuZ2V0Um95YWx0aWVzKCksCiAgICAgICAgZXh0ZXJuYWxVUkw6IG5mdFZpZXcuZXh0ZXJuYWxVUkwhLnVybCwKICAgICAgICBjb2xsZWN0aW9uUHVibGljUGF0aDogbmZ0Vmlldy5jb2xsZWN0aW9uRGF0YSEucHVibGljUGF0aCwKICAgICAgICBjb2xsZWN0aW9uU3RvcmFnZVBhdGg6IG5mdFZpZXcuY29sbGVjdGlvbkRhdGEhLnN0b3JhZ2VQYXRoLAogICAgICAgIGNvbGxlY3Rpb25Qcm92aWRlclBhdGg6IG5mdFZpZXcuY29sbGVjdGlvbkRhdGEhLnByb3ZpZGVyUGF0aCwKICAgICAgICBjb2xsZWN0aW9uUHVibGljOiBuZnRWaWV3LmNvbGxlY3Rpb25EYXRhIS5wdWJsaWNDb2xsZWN0aW9uLmlkZW50aWZpZXIsCiAgICAgICAgY29sbGVjdGlvblB1YmxpY0xpbmtlZFR5cGU6IG5mdFZpZXcuY29sbGVjdGlvbkRhdGEhLnB1YmxpY0xpbmtlZFR5cGUuaWRlbnRpZmllciwKICAgICAgICBjb2xsZWN0aW9uUHJvdmlkZXJMaW5rZWRUeXBlOiBuZnRWaWV3LmNvbGxlY3Rpb25EYXRhIS5wcm92aWRlckxpbmtlZFR5cGUuaWRlbnRpZmllciwKICAgICAgICBjb2xsZWN0aW9uTmFtZTogbmZ0Vmlldy5jb2xsZWN0aW9uRGlzcGxheSEubmFtZSwKICAgICAgICBjb2xsZWN0aW9uRGVzY3JpcHRpb246IG5mdFZpZXcuY29sbGVjdGlvbkRpc3BsYXkhLmRlc2NyaXB0aW9uLAogICAgICAgIGNvbGxlY3Rpb25FeHRlcm5hbFVSTDogbmZ0Vmlldy5jb2xsZWN0aW9uRGlzcGxheSEuZXh0ZXJuYWxVUkwudXJsLAogICAgICAgIGNvbGxlY3Rpb25TcXVhcmVJbWFnZTogbmZ0Vmlldy5jb2xsZWN0aW9uRGlzcGxheSEuc3F1YXJlSW1hZ2UuZmlsZS51cmkoKSwKICAgICAgICBjb2xsZWN0aW9uQmFubmVySW1hZ2U6IG5mdFZpZXcuY29sbGVjdGlvbkRpc3BsYXkhLmJhbm5lckltYWdlLmZpbGUudXJpKCksCiAgICAgICAgY29sbGVjdGlvblNvY2lhbHM6IGNvbGxlY3Rpb25Tb2NpYWxzLAogICAgICAgIHRyYWl0czogbmZ0Vmlldy50cmFpdHMhLAogICAgKQp9 failed: Invalid Flow argument: failed to execute the script on the execution node execution-5f6c73a22445d7d958c6a37c1f3be99c72cacd39894a3e46d6647a9adb007b4d@execution-001.devnet38.nodes.onflow.org:3569=100: rpc error: code = InvalidArgument desc = failed to execute script: failed to execute script at block (b131b77a08944b3d1d22d957356bb05c2bded7e1d4eca16695b114d93b179473): [Error Code: 1101] error caused by: [Error Code: 1101] cadence runtime error: Execution failed:
          --> 0043e09ce0df95225ae680d81b5e0c7a5ca38636a34c23244d479bc666eb72ff:79:23
           |
        79 |     let viewResolver = collection.borrowViewResolver(id: id)!
           |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
        error: unexpectedly found nil while forcing an Optional value
           --> cbf10523da1a9ee9.NewFunThingPack:206:23
            |
        206 |             let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
            |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
    uri_test.go:42: executing script aW1wb3J0IFNpbHZlcmxpZ2h0TG9jYXRpb25ORlQgZnJvbSAweDk4NDdlM2Y2OTVmMDUzZDAKaW1wb3J0IE1ldGFkYXRhVmlld3MgZnJvbSAweDYzMWU4OGFlN2YxZDdjMjAKCnB1YiBzdHJ1Y3QgTkZUVmlldyB7CiAgICBwdWIgbGV0IGlkOiBVSW50NjQKICAgIHB1YiBsZXQgdXVpZDogVUludDY0CiAgICBwdWIgbGV0IG5hbWU6IFN0cmluZwogICAgcHViIGxldCBkZXNjcmlwdGlvbjogU3RyaW5nCiAgICBwdWIgbGV0IHRodW1ibmFpbDogU3RyaW5nCiAgICBwdWIgbGV0IHJveWFsdGllczogW01ldGFkYXRhVmlld3MuUm95YWx0eV0KICAgIHB1YiBsZXQgZXh0ZXJuYWxVUkw6IFN0cmluZwogICAgcHViIGxldCBjb2xsZWN0aW9uUHVibGljUGF0aDogUHVibGljUGF0aAogICAgcHViIGxldCBjb2xsZWN0aW9uU3RvcmFnZVBhdGg6IFN0b3JhZ2VQYXRoCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25Qcm92aWRlclBhdGg6IFByaXZhdGVQYXRoCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25QdWJsaWM6IFN0cmluZwogICAgcHViIGxldCBjb2xsZWN0aW9uUHVibGljTGlua2VkVHlwZTogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25Qcm92aWRlckxpbmtlZFR5cGU6IFN0cmluZwogICAgcHViIGxldCBjb2xsZWN0aW9uTmFtZTogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25EZXNjcmlwdGlvbjogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25FeHRlcm5hbFVSTDogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25TcXVhcmVJbWFnZTogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25CYW5uZXJJbWFnZTogU3RyaW5nCiAgICBwdWIgbGV0IGNvbGxlY3Rpb25Tb2NpYWxzOiB7U3RyaW5nOiBTdHJpbmd9CiAgICBwdWIgbGV0IHRyYWl0czogTWV0YWRhdGFWaWV3cy5UcmFpdHMKCiAgICBpbml0KAogICAgICAgIGlkOiBVSW50NjQsCiAgICAgICAgdXVpZDogVUludDY0LAogICAgICAgIG5hbWU6IFN0cmluZywKICAgICAgICBkZXNjcmlwdGlvbjogU3RyaW5nLAogICAgICAgIHRodW1ibmFpbDogU3RyaW5nLAogICAgICAgIHJveWFsdGllczogW01ldGFkYXRhVmlld3MuUm95YWx0eV0sCiAgICAgICAgZXh0ZXJuYWxVUkw6IFN0cmluZywKICAgICAgICBjb2xsZWN0aW9uUHVibGljUGF0aDogUHVibGljUGF0aCwKICAgICAgICBjb2xsZWN0aW9uU3RvcmFnZVBhdGg6IFN0b3JhZ2VQYXRoLAogICAgICAgIGNvbGxlY3Rpb25Qcm92aWRlclBhdGg6IFByaXZhdGVQYXRoLAogICAgICAgIGNvbGxlY3Rpb25QdWJsaWM6IFN0cmluZywKICAgICAgICBjb2xsZWN0aW9uUHVibGljTGlua2VkVHlwZTogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25Qcm92aWRlckxpbmtlZFR5cGU6IFN0cmluZywKICAgICAgICBjb2xsZWN0aW9uTmFtZTogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25EZXNjcmlwdGlvbjogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25FeHRlcm5hbFVSTDogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25TcXVhcmVJbWFnZTogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25CYW5uZXJJbWFnZTogU3RyaW5nLAogICAgICAgIGNvbGxlY3Rpb25Tb2NpYWxzOiB7U3RyaW5nOiBTdHJpbmd9LAogICAgICAgIHRyYWl0czogTWV0YWRhdGFWaWV3cy5UcmFpdHMKICAgICkgewogICAgICAgIHNlbGYuaWQgPSBpZAogICAgICAgIHNlbGYudXVpZCA9IHV1aWQKICAgICAgICBzZWxmLm5hbWUgPSBuYW1lCiAgICAgICAgc2VsZi5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uCiAgICAgICAgc2VsZi50aHVtYm5haWwgPSB0aHVtYm5haWwKICAgICAgICBzZWxmLnJveWFsdGllcyA9IHJveWFsdGllcwogICAgICAgIHNlbGYuZXh0ZXJuYWxVUkwgPSBleHRlcm5hbFVSTAogICAgICAgIHNlbGYuY29sbGVjdGlvblB1YmxpY1BhdGggPSBjb2xsZWN0aW9uUHVibGljUGF0aAogICAgICAgIHNlbGYuY29sbGVjdGlvblN0b3JhZ2VQYXRoID0gY29sbGVjdGlvblN0b3JhZ2VQYXRoCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uUHJvdmlkZXJQYXRoID0gY29sbGVjdGlvblByb3ZpZGVyUGF0aAogICAgICAgIHNlbGYuY29sbGVjdGlvblB1YmxpYyA9IGNvbGxlY3Rpb25QdWJsaWMKICAgICAgICBzZWxmLmNvbGxlY3Rpb25QdWJsaWNMaW5rZWRUeXBlID0gY29sbGVjdGlvblB1YmxpY0xpbmtlZFR5cGUKICAgICAgICBzZWxmLmNvbGxlY3Rpb25Qcm92aWRlckxpbmtlZFR5cGUgPSBjb2xsZWN0aW9uUHJvdmlkZXJMaW5rZWRUeXBlCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uTmFtZSA9IGNvbGxlY3Rpb25OYW1lCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uRGVzY3JpcHRpb24gPSBjb2xsZWN0aW9uRGVzY3JpcHRpb24KICAgICAgICBzZWxmLmNvbGxlY3Rpb25FeHRlcm5hbFVSTCA9IGNvbGxlY3Rpb25FeHRlcm5hbFVSTAogICAgICAgIHNlbGYuY29sbGVjdGlvblNxdWFyZUltYWdlID0gY29sbGVjdGlvblNxdWFyZUltYWdlCiAgICAgICAgc2VsZi5jb2xsZWN0aW9uQmFubmVySW1hZ2UgPSBjb2xsZWN0aW9uQmFubmVySW1hZ2UKICAgICAgICBzZWxmLmNvbGxlY3Rpb25Tb2NpYWxzID0gY29sbGVjdGlvblNvY2lhbHMKICAgICAgICBzZWxmLnRyYWl0cyA9IHRyYWl0cwogICAgfQp9CgpwdWIgZnVuIG1haW4oYWRkcmVzczogQWRkcmVzcywgaWQ6IFVJbnQ2NCk6IE5GVFZpZXcgewogICAgbGV0IGFjY291bnQgPSBnZXRBY2NvdW50KGFkZHJlc3MpCgogICAgbGV0IGNvbGxlY3Rpb24gPSBhY2NvdW50CiAgICAgICAgLmdldENhcGFiaWxpdHkoU2lsdmVybGlnaHRMb2NhdGlvbk5GVC5Db2xsZWN0aW9uUHVibGljUGF0aCkKICAgICAgICAuYm9ycm93PCZ7TWV0YWRhdGFWaWV3cy5SZXNvbHZlckNvbGxlY3Rpb259PigpCiAgICAgICAgPz8gcGFuaWMoIkNvdWxkIG5vdCBib3Jyb3cgYSByZWZlcmVuY2UgdG8gdGhlIGNvbGxlY3Rpb24iKQoKICAgIGxldCB2aWV3UmVzb2x2ZXIgPSBjb2xsZWN0aW9uLmJvcnJvd1ZpZXdSZXNvbHZlcihpZDogaWQpIQoKICAgIGxldCBuZnRWaWV3ID0gTWV0YWRhdGFWaWV3cy5nZXRORlRWaWV3KGlkOiBpZCwgdmlld1Jlc29sdmVyIDogdmlld1Jlc29sdmVyKQoKICAgIGxldCBjb2xsZWN0aW9uU29jaWFsczoge1N0cmluZzogU3RyaW5nfSA9IHt9CiAgICBmb3Iga2V5IGluIG5mdFZpZXcuY29sbGVjdGlvbkRpc3BsYXkhLnNvY2lhbHMua2V5cyB7CiAgICAgICAgY29sbGVjdGlvblNvY2lhbHNba2V5XSA9IG5mdFZpZXcuY29sbGVjdGlvbkRpc3BsYXkhLnNvY2lhbHNba2V5XSEudXJsCiAgICB9CgoKICAgIHJldHVybiBORlRWaWV3KAogICAgICAgIGlkOiBuZnRWaWV3LmlkLAogICAgICAgIHV1aWQ6IG5mdFZpZXcudXVpZCwKICAgICAgICBuYW1lOiBuZnRWaWV3LmRpc3BsYXkhLm5hbWUsCiAgICAgICAgZGVzY3JpcHRpb246IG5mdFZpZXcuZGlzcGxheSEuZGVzY3JpcHRpb24sCiAgICAgICAgdGh1bWJuYWlsOiBuZnRWaWV3LmRpc3BsYXkhLnRodW1ibmFpbC51cmkoKSwKICAgICAgICByb3lhbHRpZXM6IG5mdFZpZXcucm95YWx0aWVzIS5nZXRSb3lhbHRpZXMoKSwKICAgICAgICBleHRlcm5hbFVSTDogbmZ0Vmlldy5leHRlcm5hbFVSTCEudXJsLAogICAgICAgIGNvbGxlY3Rpb25QdWJsaWNQYXRoOiBuZnRWaWV3LmNvbGxlY3Rpb25EYXRhIS5wdWJsaWNQYXRoLAogICAgICAgIGNvbGxlY3Rpb25TdG9yYWdlUGF0aDogbmZ0Vmlldy5jb2xsZWN0aW9uRGF0YSEuc3RvcmFnZVBhdGgsCiAgICAgICAgY29sbGVjdGlvblByb3ZpZGVyUGF0aDogbmZ0Vmlldy5jb2xsZWN0aW9uRGF0YSEucHJvdmlkZXJQYXRoLAogICAgICAgIGNvbGxlY3Rpb25QdWJsaWM6IG5mdFZpZXcuY29sbGVjdGlvbkRhdGEhLnB1YmxpY0NvbGxlY3Rpb24uaWRlbnRpZmllciwKICAgICAgICBjb2xsZWN0aW9uUHVibGljTGlua2VkVHlwZTogbmZ0Vmlldy5jb2xsZWN0aW9uRGF0YSEucHVibGljTGlua2VkVHlwZS5pZGVudGlmaWVyLAogICAgICAgIGNvbGxlY3Rpb25Qcm92aWRlckxpbmtlZFR5cGU6IG5mdFZpZXcuY29sbGVjdGlvbkRhdGEhLnByb3ZpZGVyTGlua2VkVHlwZS5pZGVudGlmaWVyLAogICAgICAgIGNvbGxlY3Rpb25OYW1lOiBuZnRWaWV3LmNvbGxlY3Rpb25EaXNwbGF5IS5uYW1lLAogICAgICAgIGNvbGxlY3Rpb25EZXNjcmlwdGlvbjogbmZ0Vmlldy5jb2xsZWN0aW9uRGlzcGxheSEuZGVzY3JpcHRpb24sCiAgICAgICAgY29sbGVjdGlvbkV4dGVybmFsVVJMOiBuZnRWaWV3LmNvbGxlY3Rpb25EaXNwbGF5IS5leHRlcm5hbFVSTC51cmwsCiAgICAgICAgY29sbGVjdGlvblNxdWFyZUltYWdlOiBuZnRWaWV3LmNvbGxlY3Rpb25EaXNwbGF5IS5zcXVhcmVJbWFnZS5maWxlLnVyaSgpLAogICAgICAgIGNvbGxlY3Rpb25CYW5uZXJJbWFnZTogbmZ0Vmlldy5jb2xsZWN0aW9uRGlzcGxheSEuYmFubmVySW1hZ2UuZmlsZS51cmkoKSwKICAgICAgICBjb2xsZWN0aW9uU29jaWFsczogY29sbGVjdGlvblNvY2lhbHMsCiAgICAgICAgdHJhaXRzOiBuZnRWaWV3LnRyYWl0cyEsCiAgICApCn0= failed: Invalid Flow argument: failed to execute the script on the execution node execution-48601e8a1568f18ca4a574a656710342857f939f598679280a6941cc7b800b6f@execution-003.devnet38.nodes.onflow.org:3569=100: rpc error: code = InvalidArgument desc = failed to execute script: failed to execute script at block (b131b77a08944b3d1d22d957356bb05c2bded7e1d4eca16695b114d93b179473): [Error Code: 1101] error caused by: [Error Code: 1101] cadence runtime error: Execution failed:
          --> 33c90a4efd1d84d8018dd0814ca3ccfb5ee5e62631bb8ff2eeb6b0907d8c4b4e:79:23
           |
        79 |     let viewResolver = collection.borrowViewResolver(id: id)!
           |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
        error: unexpectedly found nil while forcing an Optional value
           --> 9847e3f695f053d0.SilverlightLocationNFT:405:23
            |
        405 |             let nft = (&self.ownedNFTs[id] as auth &NonFungibleToken.NFT?)!
            |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
--- FAIL: TestGetURI (1.05s)

FAIL


Process finished with the exit code 1

Acceptance Criteria

Context

<what are you currently working on that this is blocking?>

Add a javascript templates package

Issue To Be Solved

We want to be able to release packages in other language so that apps written in those languages can import them to get the transaction code defined in the repo, similar to how it is done in Go.

Suggest A Solution

  • Design and write a package in lib/javascript/ for getting template transactions.
  • Use the templates in transactions/ and replace them with arguments that the caller provides.
  • Write tests to make sure the templates are created correctly.

Flow CLI: Transaction Error [Error Code: 1101] pre-condition failed: Cannot deposit an incompatible token type

Instructions

Please fill out the template below to the best of your ability and include a label indicating which tool/service you were working with when you encountered the problem.

Problem

Can't transfer FLOW native tokens between 2 testnet accounts

[Error Code: 1101] error caused by: 1 error occurred:
	* transaction execute failed: [Error Code: 1101] cadence runtime error: Execution failed:
error: pre-condition failed: Cannot deposit an incompatible token type
   --> 9a0766d93b6608b7.FungibleToken:218:16

Steps to Reproduce

Doing build, sign and send-signed commands for deploying send transaction on testnet using Flow CLI v1.3.1
Transaction info on testnet: https://flow-view-source.com/testnet/tx/21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61

Console log:

flow transactions build ./cadence/transactions/transfer_tokens.cdc --args-json '[{"type":"UFix64","value":"420.0"},{"type":"Address","value":"0x31bbb88233df243d"}]' --proposer ihor-testnet --payer ihor-testnet --authorizer ihor-testnet --filter payload --save ./cadence/transactions/tx_unsigned.rlp --network=testnet --log=error

ID      da7b40f1adb5873e9d0ce97fb112dbfb428e780e1bb15f64b7e3bde303790949
Payer   b0fccd76cf57f42c
Authorizers     [b0fccd76cf57f42c]

Proposal Key:
    Address     b0fccd76cf57f42c
    Index       0
    Sequence    3

No Payload Signatures

No Envelope Signatures


Arguments (2):
    - Argument 0: {"value":"420.00000000","type":"UFix64"}
    - Argument 1: {"value":"0x31bbb88233df243d","type":"Address"}

Code

// This transaction is a template for a transaction that
// could be used by anyone to send tokens to another account
// that has been set up to receive tokens.
//
// The withdraw amount and the account from getAccount
// would be the parameters to the transaction

import FungibleToken from 0x9a0766d93b6608b7
import ExampleToken from 0xb0fccd76cf57f42c

transaction(amount: UFix64, to: Address) {

    // The Vault resource that holds the tokens that are being transferred
    let sentVault: @FungibleToken.Vault

    prepare(signer: AuthAccount) {

        // Get a reference to the signer's stored vault
        let vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath)
            ?? panic("Could not borrow reference to the owner's Vault!")

        // Withdraw tokens from the signer's stored vault
        self.sentVault <- vaultRef.withdraw(amount: amount)
    }

    execute {

        // Get the recipient's public account object
        let recipient = getAccount(to)

        // Get a reference to the recipient's Receiver
        let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath)
            .borrow<&{FungibleToken.Receiver}>()
            ?? panic("Could not borrow receiver reference to the recipient's Vault")

💾 result saved to: ./cadence/transactions/tx_unsigned.rlp 
flow transactions sign ./cadence/transactions/tx_unsigned.rlp --signer ihor-testnet --filter payload --save ./cadence/transactions/tx_signed.rlp --network=testnet --log=error

ID      da7b40f1adb5873e9d0ce97fb112dbfb428e780e1bb15f64b7e3bde303790949
Payer   b0fccd76cf57f42c
Authorizers     [b0fccd76cf57f42c]

Proposal Key:
    Address     b0fccd76cf57f42c
    Index       0
    Sequence    3

No Payload Signatures

No Envelope Signatures


Arguments (2):
    - Argument 0: {"value":"420.00000000","type":"UFix64"}
    - Argument 1: {"value":"0x31bbb88233df243d","type":"Address"}

Code

// This transaction is a template for a transaction that
// could be used by anyone to send tokens to another account
// that has been set up to receive tokens.
//
// The withdraw amount and the account from getAccount
// would be the parameters to the transaction

import FungibleToken from 0x9a0766d93b6608b7
import ExampleToken from 0xb0fccd76cf57f42c

transaction(amount: UFix64, to: Address) {

    // The Vault resource that holds the tokens that are being transferred
    let sentVault: @FungibleToken.Vault

    prepare(signer: AuthAccount) {

        // Get a reference to the signer's stored vault
        let vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath)
            ?? panic("Could not borrow reference to the owner's Vault!")

        // Withdraw tokens from the signer's stored vault
        self.sentVault <- vaultRef.withdraw(amount: amount)
    }

    execute {

        // Get the recipient's public account object
        let recipient = getAccount(to)

        // Get a reference to the recipient's Receiver
        let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath)
            .borrow<&{FungibleToken.Receiver}>()
            ?? panic("Could not borrow receiver reference to the recipient's Vault")

💾 result saved to: ./cadence/transactions/tx_signed.rlp 
flow transactions send-signed ./cadence/transactions/tx_signed.rlp --network=testnet --log=error

ID      21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61
Payer   b0fccd76cf57f42c
Authorizers     [b0fccd76cf57f42c]

Proposal Key:
    Address     b0fccd76cf57f42c
    Index       0
    Sequence    3

No Payload Signatures

Envelope Signature 0:
    Address     b0fccd76cf57f42c
    Signature   571fcc3e71751ffaf44c93cf065c7ad792c93dd2457f7f10882c60151da7bd721d149d17d2bab65f56e9f60df6e27293813f42fd6dad6408d66b804e264bb3a1
    Key Index   0


Arguments (2):
    - Argument 0: {"value":"420.00000000","type":"UFix64"}
    - Argument 1: {"value":"0x31bbb88233df243d","type":"Address"}

Code

// This transaction is a template for a transaction that
// could be used by anyone to send tokens to another account
// that has been set up to receive tokens.
//
// The withdraw amount and the account from getAccount
// would be the parameters to the transaction

import FungibleToken from 0x9a0766d93b6608b7
import ExampleToken from 0xb0fccd76cf57f42c

transaction(amount: UFix64, to: Address) {

    // The Vault resource that holds the tokens that are being transferred
    let sentVault: @FungibleToken.Vault

    prepare(signer: AuthAccount) {

        // Get a reference to the signer's stored vault
        let vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath)
            ?? panic("Could not borrow reference to the owner's Vault!")

        // Withdraw tokens from the signer's stored vault
        self.sentVault <- vaultRef.withdraw(amount: amount)
    }

    execute {

        // Get the recipient's public account object
        let recipient = getAccount(to)

        // Get a reference to the recipient's Receiver
        let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath)
            .borrow<&{FungibleToken.Receiver}>()
            ?? panic("Could not borrow receiver reference to the recipient's Vault")


Block ID        a6bd4e1abd3e294bf79ed7c2792f9ccea825b958144e2f8f120cd8455695b70f
Block Height    110535670
❌ Transaction Error 
[Error Code: 1101] error caused by: 1 error occurred:
        * transaction execute failed: [Error Code: 1101] cadence runtime error: Execution failed:
error: pre-condition failed: Cannot deposit an incompatible token type
   --> 9a0766d93b6608b7.FungibleToken:218:16





Status          ✅ SEALED
ID              21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61
Payer           b0fccd76cf57f42c
Authorizers     [b0fccd76cf57f42c]

Proposal Key:
    Address     b0fccd76cf57f42c
    Index       0
    Sequence    3

No Payload Signatures

Envelope Signature 0: b0fccd76cf57f42c
Signatures (minimized, use --include signatures)

Events:          
    Index       0
    Type        A.7e60df042a9c0868.FlowToken.TokensWithdrawn
    Tx ID       21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61
    Values
                - amount (UFix64): 0.00000199 
                - from (Address?): 0xb0fccd76cf57f42c 

    Index       1
    Type        A.7e60df042a9c0868.FlowToken.TokensDeposited
    Tx ID       21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61
    Values
                - amount (UFix64): 0.00000199 
                - to (Address?): 0x912d5440f7e3769e 

    Index       2
    Type        A.912d5440f7e3769e.FlowFees.FeesDeducted
    Tx ID       21c0fdb602080f1dea209155cf0ce7d83bc82cc5f5cad8c13168b81a6f358b61
    Values
                - amount (UFix64): 0.00000199 
                - inclusionEffort (UFix64): 1.00000000 
                - executionEffort (UFix64): 0.00000020 



Code (hidden, use --include code)

Payload (hidden, use --include payload)

Context

I assume I could import outdated FungibleToken contract which has testnet address: 0x9a0766d93b6608b7, I deployed only ExampleToken contract to my account, and it imports existing one (on chain one)
ExampleToken contract was taken from master today (Jul 14, 2023)

Add metadata views for Fungible Token contracts

Issue To Be Solved

The metadata standard that we are currently using for NFTs can also be utilized for fungible tokens
We should create views and add them to the FT standard.

Suggest A Solution

  • Should the views go in the regular MetadataViews contract in the NFT repo or create a new contract?
  • Can we re-use existing views, or does it not make sense to get them mixed up?
  • Need to define a display view, a path information view, a license view, externalURL, any more?
  • Add the views to the ExampleToken contract and tests
  • Create docs/tutorials for how to use them and migrate

Rename FungibleToken to FungibleTokenInterface

Issue To Be Solved

FungibleToken seems to be a vague name for an Interface, especially given that the Flow Docs use "FungibleToken" as the name for their contract for a tutorial.

(Optional): Suggest A Solution

For clarity, can the interface be thus called FungibleTokenInterface

  1. Reduces confusion for people who just start with Flow

I realize it will ask for some refractoring of code, but it is well worth doing before the testnet launch!

Add new transaction and script templates

Issue To Be Solved

There are many ways a fungible token can be used and we want more transaction templates to be defined for them.

Suggest A Solution

Please leave comments in this issue if you think of a transaction that you would like to be added as a template.
This issue will also be updated with ideas as they are added.

When adding a new template:

  1. Create an issue for the template you are adding and assign it to yourself. Leave a comment on this post that you are working on it. Include any relevant questions or discussion about your template in the issue that you create.
  2. Write the template as a .cdc file and save it in the transactions directory.
  3. Save the fungible token address as 0xFUNGIBLETOKENADDRESS
  4. Save the example token address as 0xTOKENADDRESS
  5. Use ExampleToken as the token name and exampleTokenVault, etc. as the token and storage name
  6. Use transaction parameters instead of hard-coded values.
  7. Write a go template getter in lib/go/templates/ and run make generate in that directory.
  8. Write a test for your transaction in lib/go/test/ and make sure the test passes.
  9. Ensure that your template and test are well commented.

Template Suggestions

Feel free to choose a template from this list to work on, or make a comment to add your own idea.

  • Send tokens to multiple addresses. The addresses and amounts can be configurable parameters to the transaction.
  • Remove the public receiver capability from an account.
  • Remove the balance capability from an account.
  • Create a provider capability and store it in another account's /private/ domain.
  • Use an account's provider capability in your /private/ domain to withdraw tokens from their account and deposit it in your account.

Make TokenForwarder and Switchboard target capabilities retrievable

Issue To Be Solved

Add a few helper methods that will make it easier to validate underlying receiver capabilities in the TokenForwarder and FungibleTokenSwitchboard contracts. Without this, it is much harder to know what we are looking at under the hood, which means that malicious users could setup any number of roundabout ways to make receivers actually invalid (like an infinite loop of receivers that will drain all gas in a transaction)

Made a brief diagram to showcase this problem, it applies to both TokenForwarding and the Switchboard:
flow-ft

In any case, without some mechanism to investigate what is being referenced in the forwarder or switchboard resources, there isn't a way on-chain to ensure that what we are sending through can actually reach its destination. Because of that, I can "poison" the chain of resources anywhere down the line to prevent a token vault from being able to be sent.

With these editions, we can make some form of validator that only permits a maximum depth, and rejects the deposit if it goes beyond that. This will allow marketplaces to be defensive and reject vault distribution if it goes too far, or if it has the wrong type along its chain (a non forwarder or switchboard)

#112

Issue multiple FungibleTokens from a single contract

Issue To Be Solved

Currently if a project wishes to issue multiple tokens that are compatible they need to deploy a new contract for each token they wish to issue.

This is quite common requirement in DeFi applications issuing LPTokens, Fractional tokens, social tokens etc.

Suggest A Solution

Create a 2nd FungibleTokens (plural) interface that adds an ID field to the Vault resource allowing for an extra dimension of token type per contract implementation.

Are there any drawbacks to this approach?

I'm thinking FungibleTokenZ might be a better name to avoid the confusion of plural vs singular. (FungibleTokenSet or FungibleTokenCollection)

Should this live in this repo or have another separate repo?
Personally, I think it would make sense to put them here together and mention the appropriate use cases for each..

error: cannot deploy invalid contract

Instructions

I did a quick clone of the repository and ran flow accounts add-contract ./contracts/ExampleToken.cdc --network emulator after flow emulator

Problem

The example token contract is not deploying on emulator

Steps to Reproduce

  1. git clone the repo
  2. flow emulator
  3. flow accounts add-contract ./contracts/ExampleToken.cdc --network emulator

Please let me know if im missing something!

Context

Transaction ID: ff8dc2768e3bccf13c97fe4bf66830620a38d798201630ecce0738522309accd
❌ Failed to deploy contract
❌ Command Error: execution error code 1101: [Error Code: 1101] error caused by: 1 error occurred:
        * transaction execute failed: [Error Code: 1101] cadence runtime error: Execution failed:
error: cannot deploy invalid contract
 --> ff8dc2768e3bccf13c97fe4bf66830620a38d798201630ecce0738522309accd:4:3
  |
4 |                     signer.contracts.add(name: name, code: code.decodeHex() )
  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot find declaration `MetadataViews` in `f8d6e0586b0a20c7.MetadataViews`
 --> f8d6e0586b0a20c7.ExampleToken:2:7
  |
2 | import MetadataViews from 0xf8d6e0586b0a20c7
  |        ^^^^^^^^^^^^^ available exported declarations are:


error: cannot find declaration `FungibleTokenMetadataViews` in `f8d6e0586b0a20c7.FungibleTokenMetadataViews`
 --> f8d6e0586b0a20c7.ExampleToken:3:7
  |
3 | import FungibleTokenMetadataViews from 0xf8d6e0586b0a20c7
  |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ available exported declarations are:


error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:101:20
    |
101 |             return [Type<FungibleTokenMetadataViews.FTView>(),
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:102:20
    |
102 |                     Type<FungibleTokenMetadataViews.FTDisplay>(),
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:103:20
    |
103 |                     Type<FungibleTokenMetadataViews.FTVaultData>()]
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:113:21
    |
113 |                 case Type<FungibleTokenMetadataViews.FTView>():
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:118:21
    |
118 |                 case Type<FungibleTokenMetadataViews.FTDisplay>():
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:136:21
    |
136 |                 case Type<FungibleTokenMetadataViews.FTVaultData>():
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:115:52
    |
115 |                         ftDisplay: self.resolveView(Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type parameter: `T`
   --> f8d6e0586b0a20c7.ExampleToken:116:54
    |
116 |                         ftVaultData: self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: cannot infer type from array literal:  requires an explicit type annotation
   --> f8d6e0586b0a20c7.ExampleToken:125:54
    |
125 |                     let medias = MetadataViews.Medias([media])
    |                                                       ^

error: cannot infer type from dictionary literal:  requires an explicit type annotation
   --> f8d6e0586b0a20c7.ExampleToken:132:33
    |
132 |                         socials: {

FEATURE: Fungible Token Switchboard Contract

Issue To Be Solved

Currently, if a user wants to receive a particular fungible token, they must run a specific setup transaction to initialize the specific vault in their account. This is a bit awkward for users who want to pay someone in a different fungible token than the one they have specified. This problem is especially pronounced in the discussion of how royalties should be paid for NFT sales. A user provides a {FungibleToken.Receiver} capability to receive royalties from, but if the sale is performed with a different token type than the one specified, the marketplace has to either look for the other token type through the receiver's owner.address field, or just not pay royalties, which is not ideal.

Want a solution that will allow anyone to deposit a generic fungible token into a standard receiver path that will forward the tokens to the correct vault in the account. The solution should be as generic and scalable as possible while not exposing any users to any ddos-esque risk or security risk.

Suggest A Solution

  • Create a design document for the smart contract and write an initial proposal. Post the proposal here.
  • Discuss with the community and Flow team members to align on important features.
  • Within this epic, create and estimate issues for:
  • The development and testing of the contract
  • The documentation of the contract and transactions

Needs to integrate with the proposed solution for Royalties with the generic receiver

Design proposal Document in Notion

Unanswered Questions

Would the contract have a strict list of fungible tokens that it supports that an admin can update when needed, or would we leave that in the hands of the owner to specify which fungible tokens they want to support?

Would the resource create a new vault for a user when someone tries to send them tokens that their account doesn't already support?

Context

This is preventing royalties from being as useful and powerful as possible

Fix Go bindata tool so that `contracts` and `templates` generate can run at the same time

Issue To Be Solved

Every time a contract or transaction is changed and we want to run automated tests, we have to rerun make generate in lib/go/contracts/ and lib/go/templates to ensure that those changes are reflected in the templates we use for the tests in lib/go/test.

Suggest A Solution

  • Fix Makefiles so that a make generate from the root of the repo runs make generate in each of the packages.
  • Run a make generate automatically when the automated tests are run.

Add a javascript contracts package

Issue To Be Solved

We want to be able to release packages in other language so that apps written in those languages can import them to get the contract code defined in the repo, similar to how it is done in Go.

Suggest A Solution

  • Design and write a package in lib/javascript/ for getting contract code.
  • Use the code in contracts/ and replace the addresses and names with arguments that the caller provides.
  • Write tests to make sure the templates are created correctly.

Build FT Metadata Views features

Issue To Be Solved

In a similar way as the NFT Metadata standard, we need to implement the Metadata for Fungible Tokens in order to assure its interoperability across the flow ecosystem.

Suggest A Solution

Create a new contract that will define the needed Metadata views as structs, following the pattern used for the NFT metadata.

If there are multiple solutions, please present each one separately. Save comparisons for the very end.)

Context

This would allow the creation of a FT Catalog that will showcase the different fungible tokens available on the Flow blockchain.

safeDeposit and addNewVaultsByPath methods on switchboard are not entirely panic free

Problem

safeDeposit(from: @FungibleToken.Vault): @FungibleToken.Vault? and addNewVaultsByPath(paths: [PublicPath], address: Address) could panic if the stored object pointed by the capability is changed to a new one that does not implement {FungibleToken.Receiver} . See onflow/nft-storefront#79 (comment)

Steps to Reproduce

Link a ft receiver to the switchboard and then remove the vault pointed by the receiver, storing on the same path any other resource than a {FungibleToken.Receiver}, then attempt to safeDeposit. Passing a path to addNewVaultsByPath that points to a non {FungibleToken.Receiver} resource.

Acceptance Criteria

Is this edge case important enough to update the contract? The solution will be to check on the capabilities before attempting to borrow them, how can check panic?

Check PrivateForwarder transactions and tests

Problem

The PrivateReceiverForwarder contract transactions aren't importing correctly the contract and create_private_forwarder.cdc has syntax errors. Go through them and write js tests using them to ensure it is working properly.

Create Design Proposal for FT Switchboard

This issue is part of #62

Issue To Be Solved

For the fungible token switchboard contract, a design proposal document should be put together with the plan for the smart contract

Suggest A Solution

Create a design document similar to a FLIP document with motivation, design summary, tradeoffs, docs plans, etc

Draft proposal (will be posted in #62 when complete)

Add Practical Token Setup and Transfer transactions that utilize switchboard

Issue To Be Solved

Need more practical transactions for setting up the Fungible Token Switchboard in real user's accounts.
More context documented here: https://www.notion.so/dapperlabs/Royalties-Switchboard-393d11734bc64341a44131a7b3621053

Suggest A Solution

Add new transactions:

  • Update setup_account to set up a switchboard resource if it isn't already there and add the correct capability to it.
  • Transactions to add a generic receiver capability or list capabilities to the switchboard provided a path or list of paths to receiver capabilities.
  • Transaction to setup an account with USDC and add the USDC receiver to the switchboard
  • Add a generic transfer transaction to check if the receiver provided is a switchboard and accepts the given token type, and if not, utilize the lost and found smart contract to store the temporary vault to be transferred. (No need to copy the whole lost and found contract to this repo to test this one, just do a manual test on the emulator to make sure it works)

cc @albeethekid

DOCS: Document FungibleTokenSwitchboard

Issue To Be Solved

After coding the new switchboard contract we will need docs describing it features and how to use it.

Suggest A Solution

  • Add feature details on the repo REAME.md
  • Create a guide on how to use the Switchboard resource instead of a regular {FungibleToken.Receiver}
  • Make a tutorial for upgrading and account for using the Switchboard and how to add/remove capabilities

Build core FungibleTokenSwitchboard features

Issue To Be Solved

Build the core features specified in the epic. Also build the tests for the features

Suggest A Solution

  • We will write a new contract “FungibleTokenSwitchboard” that will feature a resource “Switchboard” conforming to {FungibleToken.Receiver} but instead of storing a vault balance, it will store a record of these capabilities: Capability<&AnyResource{FungibleToken.Receiver}>.

  • The deposit function enforced by the {FungibleToken.Receiver} will then check the type of the @FungibleToken.Vault received as an argument and route it to the proper Capability<&AnyResource{FungibleToken.Receiver}>. If there is no capability matching the deposited FT, the transaction will fail.

  • The owner of the Switchboard resource will be able to call the function addVaultCapability(switch: Capability<&{FungibleToken.Receiver}> in order to store a new one of these capabilities.

  • The Switchboard resource also should expose a getVaultCapabilities(): [Capability<&{FungibleToken.Receiver}>] in order to allow buyers to know if the token they are willing to use for the payment is supported by the seller / payment receiver.

Problems with Switchboard

Was there a FLIP for the swithcboard? I have not seen one, but then I have been very busy lately.

I have somes issues with the way this is made. This is based on what we have at .find in our Profile and some experience from developing on this chain for a while.

What if i want to have something like https://github.com/bjartek/flow-sharded-wallet that can accept FlowToken.Vault and proxy that to several other users. At the moment i cannot register this with switchboard since the type of it is not FlowToken.Vault but something else.

What about the ability for a entry to say what types it supports so that you can proxy it? Kind of like we do here https://github.com/findonflow/find/blob/main/contracts/Profile.cdc#L28

This switchboard is not compatible with TokenForwarding on dapper wallet either since the type of the receiver vault is not FlowToken.Vault but TokenForwarding.

I would really like to se better events here could we create a struct like FlowTokenAuthProvider that has a provider cap and an amount, and then the option to only withdraw that amount of funds from the provider? That way you will have both sender and receiver and we can emit a good event with both from and to, i would also love to see an aditional context parameter here to send in related tags.

Normalize flow.json

Issue To Be Solved

New contracts added to this repository need to be reflected on the flow.json file in order to allow smooth local deployments for developers

Suggest A Solution

Add the FungibleTokenSwitchboard, FungibleTokenMetadataViews and its dependencies to the contracts and deployments sections

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.