GithubHelp home page GithubHelp logo

onflow / flow-ft Goto Github PK

View Code? Open in Web Editor NEW
144.0 37.0 53.0 2.97 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 Issues

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.

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?

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)

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.

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: {

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 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

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

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..

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)

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.

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

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!

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.

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.

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)

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.

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 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.

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

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.

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.

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.

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.

Transactions in Question

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

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

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

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

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: ''
      }
    ]

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

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}>(

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.