onflow / flow-ft Goto Github PK
View Code? Open in Web Editor NEWThe Fungible Token standard on the Flow Blockchain
Home Page: https://onflow.org
License: The Unlicense
The Fungible Token standard on the Flow Blockchain
Home Page: https://onflow.org
License: The Unlicense
We need to implement the FT Metadata Views on the ExampleToken.cdc
contract so it can be used as an example on how to use them.
Usually, tokens have a name
parameter along with decimals
and totalSupply
.
Should this be added to the interface?
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.
Failed to execute script to obtain NFT View.
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
<what are you currently working on that this is blocking?>
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.
lib/javascript/
for getting template transactions.transactions/
and replace them with arguments that the caller provides.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)
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.
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?
This issue is part of #62
For the fungible token switchboard contract, a design proposal document should be put together with the plan for the smart contract
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)
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
.
make generate
from the root of the repo runs make generate
in each of the packages.make generate
automatically when the automated tests are run.I did a quick clone of the repository and ran flow accounts add-contract ./contracts/ExampleToken.cdc --network emulator
after flow emulator
The example token contract is not deploying on emulator
flow emulator
flow accounts add-contract ./contracts/ExampleToken.cdc --network emulator
Please let me know if im missing something!
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: {
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
We have a new standard for contract imports: https://github.com/onflow/flow/blob/master/flips/2022-03-23-contract-imports-syntax.md
flow.json
templates
package to use these imports as replacements instead.New contracts added to this repository need to be reflected on the flow.json file in order to allow smooth local deployments for developers
Add the FungibleTokenSwitchboard, FungibleTokenMetadataViews and its dependencies to the contracts and deployments sections
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.
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..
This belongs to #73 epic
We need to develop a design document showing the plan for implementing Metadata Views for fungible tokens.
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)
There are many ways a fungible token can be used and we want more transaction templates to be defined for them.
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:
.cdc
file and save it in the transactions
directory.0xFUNGIBLETOKENADDRESS
0xTOKENADDRESS
ExampleToken
as the token name and exampleTokenVault
, etc. as the token and storage namelib/go/templates/
and run make generate
in that directory.lib/go/test/
and make sure the test passes.Feel free to choose a template from this list to work on, or make a comment to add your own idea.
/private/
domain./private/
domain to withdraw tokens from their account and deposit it in your account.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.
fungible_test.go
to use these new templates correctlyThe 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.
ExampleToken
contract and testsFungibleToken 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.
For clarity, can the interface be thus called FungibleTokenInterface
I realize it will ask for some refractoring of code, but it is well worth doing before the testnet launch!
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.
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.
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.
lib/javascript/
for getting contract code.contracts/
and replace the addresses and names with arguments that the caller provides.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.
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
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)
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 the core features specified in the epic. Also build the tests for the features
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.
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.
As it is on the NFT repo, rename the utilityContracts folder as utility and refactor any reference
Expand on existing FungibleTokenMetadataViews documentation with more context and simple examples.
Related: onflow/flow-core-contracts#369
We need to go through all the comments on the repo contracts, double check that they fit the docgen format, generate the doc and include in the repos
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.
There might be a solution to not being able to assert the type of a token vault with the interface.
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.
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.
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"
This repo will be used as an example implementation for an upcoming grant wishlist item.
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.
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.)
This would allow the creation of a FT Catalog that will showcase the different fungible tokens available on the Flow blockchain.
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.
Add these conditions:
Want to show how a Vault can be destroyed and subtracted from the supply.
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.
Needs to integrate with the proposed solution for Royalties with the generic receiver
Design proposal Document in Notion
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?
This is preventing royalties from being as useful and powerful as possible
After coding the new switchboard contract we will need docs describing it features and how to use it.
{FungibleToken.Receiver}
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
Add new transactions:
cc @albeethekid
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 .borrow
ing 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.
An (optional?) parameter for addNewVault
method, to override the deposit Type
that the capability handles.
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: ''
}
]
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:
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)
FungibleTokenSwitchboard.PublicPath
exposes {FungibleTokenSwitchboard.SwitchboardPublic}
which includes the deposit
method,
but is not usable as &{FungibleToken.Receiver}
Change the linked type to <&FungibleTokenSwitchboard.Switchboard{FungibleTokenSwitchboard.SwitchboardPublic, FungibleToken.Receiver}>
on
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.