diff --git a/coverage.json b/coverage.json index 6a21548..85db917 100644 --- a/coverage.json +++ b/coverage.json @@ -23,7 +23,7 @@ "186": 1, "187": 1, "188": 1, - "200": 8, + "200": 9, "204": 4, "206": 4, "210": 4, @@ -34,20 +34,20 @@ "221": 4, "223": 4, "224": 4, - "27": 45, + "27": 47, "29": 2, "34": 4, "40": 4, "41": 4, - "52": 38, - "59": 2, + "52": 40, + "59": 3, "63": 0, "67": 1, - "93": 20, - "94": 20, - "95": 20, - "96": 20, - "97": 20 + "93": 21, + "94": 21, + "95": 21, + "96": 21, + "97": 21 }, "missed_lines": [ 63, @@ -77,8 +77,8 @@ "202": 6, "205": 18, "214": 0, - "223": 8, - "224": 8, + "223": 9, + "224": 9, "98": 7 }, "missed_lines": [ @@ -97,14 +97,14 @@ "103": 2, "104": 2, "107": 0, - "145": 38, - "146": 38, - "148": 38, - "149": 38, - "150": 38, - "151": 38, - "152": 38, - "153": 38, + "145": 40, + "146": 40, + "148": 40, + "149": 40, + "150": 40, + "151": 40, + "152": 40, + "153": 40, "163": 0, "164": 0, "165": 0, @@ -589,27 +589,27 @@ }, "excluded_locations": [ "A.0000000000000001.NonFungibleToken", - "A.0000000000000001.FlowServiceAccount", - "A.0000000000000001.FlowStorageFees", - "A.0000000000000001.FlowClusterQC", + "A.0000000000000002.FungibleToken", + "A.0000000000000001.ExampleNFT", + "I.Test", "I.BlockchainHelpers", - "A.0000000000000002.FungibleTokenMetadataViews", "A.0000000000000001.StakingProxy", "A.0000000000000001.FlowDKG", - "A.0000000000000004.FlowFees", - "A.0000000000000001.NodeVersionBeacon", - "A.0000000000000002.FungibleToken", + "A.0000000000000002.FungibleTokenMetadataViews", + "A.0000000000000001.FlowStakingCollection", "s.7465737400000000000000000000000000000000000000000000000000000000", + "A.0000000000000001.FlowServiceAccount", + "A.0000000000000004.FlowFees", "A.0000000000000001.FlowEpoch", - "A.0000000000000001.RandomBeaconHistory", - "A.0000000000000001.FlowIDTableStaking", - "A.0000000000000001.LockedTokens", "A.0000000000000003.FlowToken", "A.0000000000000001.MetadataViews", - "A.0000000000000001.FlowStakingCollection", "A.0000000000000001.ViewResolver", - "A.0000000000000001.ExampleNFT", + "A.0000000000000001.FlowIDTableStaking", + "A.0000000000000001.RandomBeaconHistory", "I.Crypto", - "I.Test" + "A.0000000000000001.FlowClusterQC", + "A.0000000000000001.NodeVersionBeacon", + "A.0000000000000001.FlowStorageFees", + "A.0000000000000001.LockedTokens" ] } \ No newline at end of file diff --git a/lib/go/templates/internal/assets/assets.go b/lib/go/templates/internal/assets/assets.go index 381da91..8a0b971 100644 --- a/lib/go/templates/internal/assets/assets.go +++ b/lib/go/templates/internal/assets/assets.go @@ -3,6 +3,7 @@ // burn_tokens.cdc (1.718kB) // create_forwarder.cdc (3.03kB) // generic_transfer.cdc (1.331kB) +// metadata/setup_account_from_address.cdc (1.906kB) // metadata/setup_account_from_vault_reference.cdc (1.909kB) // mint_tokens.cdc (1.827kB) // privateForwarder/create_account_private_forwarder.cdc (2.025kB) @@ -166,6 +167,26 @@ func generic_transferCdc() (*asset, error) { return a, nil } +var _metadataSetup_account_from_addressCdc = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x54\x4d\x6f\xe3\x36\x14\xbc\xeb\x57\x4c\x7d\x08\x64\xc0\x2b\xdf\x0d\x27\xc1\xd6\x6d\x6e\x2d\x8a\xac\x9b\xfb\x33\xf5\x64\x11\x2b\x93\x02\xf9\x64\x35\x08\xf2\xdf\x0b\x52\x1f\x91\x12\x27\x01\xb2\x3a\x18\x32\xdf\xe7\x0c\x67\xa4\x4f\xb5\x75\x82\xbb\xc6\x1c\xf5\xa1\xe2\xbd\xfd\xc9\x06\x85\xb3\x27\x2c\x66\x67\x8b\xe4\x52\xe6\x5f\x2c\x94\x93\xd0\x83\xe6\xd6\x5f\x2a\x9b\x25\x2c\x92\x64\xbd\x5e\x63\x5f\x6a\x0f\x71\x64\x3c\x29\xd1\xd6\x40\x7b\xb4\x25\x09\xc8\x80\x94\xb2\x8d\x11\xb4\xb6\xa9\x72\xb8\xc6\xc4\x0a\xb1\xf0\x2c\xd0\xe2\xb9\x2a\xd0\xd4\xe1\xe0\x44\x86\x8e\x8c\xa2\x9f\x06\x09\xe3\x7c\xd6\x75\x2f\x1a\x13\x5b\xc7\xea\xc6\xb3\xc7\x39\x6e\x28\x16\x3f\x8d\x6d\xd1\x96\xec\x78\x68\x1b\xfa\x95\x8c\x33\x35\x95\xc4\x02\x6d\xe0\xc5\xba\xd0\x9e\x4c\x1e\xd2\x94\x63\x12\x8e\x69\x7c\xaa\xe5\xb1\x4b\xce\x92\x64\x02\x23\x55\xd6\x88\x23\x25\xdf\xf3\xdc\xb1\xf7\x1b\xf4\x2f\x2b\x0c\x91\xbf\xe9\xc4\x1b\xfc\x10\xa7\xcd\x71\x89\xa7\x24\x01\x80\xda\x71\x4d\x8e\x53\xaf\x8f\x86\xdd\x06\xd4\x48\x99\xfe\xa0\x33\x3f\x50\xd5\xf0\x0a\x3b\xaa\xe9\xa0\x2b\x2d\x9a\xfd\x12\x57\xdf\x3b\x86\x42\x39\xfa\x67\xbd\xc6\xef\xd6\x39\xdb\x82\xe0\xb8\x60\xc7\x46\x45\x74\x23\xac\x88\x87\x73\x58\x13\xcf\x6a\xf2\x9e\xf3\x91\x6c\x92\xe9\x69\xdd\x1c\x2a\xad\xfe\x21\x29\xc7\x01\x15\x0b\x1c\x7b\x5b\x9d\xd9\xdd\x73\x81\x6b\x1c\x59\xfa\x45\x5e\xc3\x5e\x8e\x55\xe1\xc9\x86\xa8\xcf\x0e\x71\xc5\xed\xd5\x4c\x1f\x37\xa9\x89\x9c\x4c\x19\x9a\x77\xb8\xbd\x45\x4d\x46\xab\x74\xb1\x8b\x9a\x30\x56\x70\x78\x17\xed\x5c\x0e\x63\xdb\xc5\x32\x99\xb2\xf5\xaf\x0f\x77\x49\x32\xaf\x77\x2c\x4e\xf3\xb9\xbb\xe6\xbb\x7d\x90\x2c\x66\x14\x14\xf2\x10\xc8\xfc\x83\x84\x70\x3d\x25\x24\xeb\xdf\x77\xfd\xb8\x50\x9a\x86\xb3\xc6\x29\xde\x3f\xd6\xbc\x81\xd1\xd5\x2a\x8a\xb0\xfb\x1b\x7e\xb7\xef\x3b\x25\xbb\xdb\x8f\xa3\x6e\xd2\xe5\x12\xe4\x7f\xfb\xc0\x79\xd3\xf4\xdb\x4f\xd9\xeb\x97\x1d\x60\x8e\x90\xc2\x76\x28\xac\x8b\x81\xa3\x3e\xb3\x19\x47\x7e\x4c\xe7\xae\x73\x07\xc1\x70\x3b\xf5\x07\x1a\xaf\xcd\x31\xb6\xeb\x0c\xf4\x67\x88\xc5\x81\xa3\x43\xa1\x8d\xd7\xf9\x9b\x65\x66\xbc\xf3\x4b\xd9\xf6\xdb\xe4\x12\xb2\xd7\x5d\xd3\xf9\x5e\xc1\x44\xd0\x32\x68\xa3\x97\xfb\x98\xd1\x19\x2e\xeb\xad\x9e\x79\x3a\x73\xba\xfd\xf6\x32\x6c\x05\xb1\x9b\xe9\xa5\x0f\xa9\xc1\x1b\x2f\x22\xbd\xc8\x44\x67\x22\xa8\xc1\xbb\x8f\x23\xb1\x1d\x33\x6d\xa9\x55\x09\x6d\x54\xd5\xe4\xec\x63\x20\xbb\xef\x05\x05\x6d\x84\x5d\x41\x8a\x67\x2c\xc4\xc2\x1d\xd5\xb8\x1e\x36\x57\x93\x2f\xc3\x08\x43\x7b\xdf\xf0\xf6\xea\x69\xa6\x96\x2c\x62\x78\xbe\x49\x3f\x45\x73\xa9\x75\x04\xe3\xcb\x74\xd8\x60\x05\x92\x39\x31\xa7\x5e\x8d\x5d\xaf\x2f\x31\xc2\xff\xd5\x76\x94\x8b\x63\xc5\xfa\x7d\x2a\x86\xf0\x57\xd9\xb8\xef\xeb\x7f\x95\x90\xc9\x1e\x6f\x39\x19\x82\x13\x4e\x9e\x93\xe7\x04\xff\x07\x00\x00\xff\xff\xe5\x5e\x12\x03\x72\x07\x00\x00" + +func metadataSetup_account_from_addressCdcBytes() ([]byte, error) { + return bindataRead( + _metadataSetup_account_from_addressCdc, + "metadata/setup_account_from_address.cdc", + ) +} + +func metadataSetup_account_from_addressCdc() (*asset, error) { + bytes, err := metadataSetup_account_from_addressCdcBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "metadata/setup_account_from_address.cdc", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6, 0x9c, 0x89, 0x2b, 0x3d, 0xd4, 0xa8, 0xbb, 0x53, 0x93, 0x74, 0xac, 0x7a, 0x36, 0xb0, 0x66, 0xed, 0x3a, 0x77, 0xd4, 0xe0, 0x7c, 0x14, 0xff, 0x58, 0x20, 0xd9, 0x53, 0xf4, 0x42, 0xc5, 0xf}} + return a, nil +} + var _metadataSetup_account_from_vault_referenceCdc = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x53\xcd\x6e\xf3\x36\x10\xbc\xeb\x29\x06\x3e\xa4\x32\xe0\xc8\x77\xc3\x4e\x90\xba\x4d\x4f\x05\x82\xd4\xcd\x7d\x2d\xad\x2c\x22\x32\x29\x90\x2b\xab\x41\xe0\x77\x2f\x44\xfd\x98\x72\xdd\x2f\x40\x3e\x1d\x0c\x7a\xb9\xbb\x9c\x99\x9d\x55\xc7\xca\x58\xc1\x73\xad\x0f\x6a\x5f\xf2\xce\xbc\xb3\x46\x6e\xcd\x11\xb3\x49\x6c\x16\xdd\xca\xfc\x93\x85\x32\x12\x7a\x53\xdc\xb8\x5b\x65\x93\x84\xb1\x47\xfb\xef\x95\x9d\x29\x4f\x6c\xfb\xaa\x30\x34\x8b\xa2\xe5\x72\x89\x5d\xa1\x1c\xc4\x92\x76\x94\x8a\x32\x1a\xca\xa1\x29\x48\x40\x1a\x94\xa6\xa6\xd6\x82\xc6\xd4\x65\x06\x5b\x6b\x5f\x21\x06\x8e\x05\x4a\x1c\x97\x39\xea\xaa\x0d\x1c\x49\xd3\x81\x91\xf7\xa8\x20\x2d\x2c\x97\x74\xdd\xf3\x5a\xfb\xd6\xbe\xba\x76\xec\x70\xf2\x4c\xc4\xe0\x5d\x9b\x06\x4d\xc1\x96\x87\xb6\x6d\xbf\x82\x71\xa2\xba\x14\x5f\xa0\x34\x9c\x18\xdb\xb6\x27\x9d\xb5\x69\xa9\x65\x12\xf6\x69\x7c\xac\xe4\xa3\x4b\x4e\xa2\x28\xa0\x11\x53\x96\x59\x76\x6e\x85\xa7\xee\xb0\x40\x55\xef\x4b\x95\xbe\x90\x14\x2b\xbc\x8c\xe7\x39\x3e\xa3\x08\x00\x2a\xcb\x15\x59\x8e\x9d\x3a\x68\xb6\x2b\x50\x2d\x45\xfc\x17\x9d\xf8\x8d\xca\x9a\x17\xd8\x52\x45\x7b\x55\x2a\x51\xec\xe6\xb8\x7b\xea\xb4\x69\xcb\xd1\x7f\xcb\x25\x7e\x35\xd6\x9a\x06\x04\xcb\x39\x5b\xd6\xa9\xe7\x35\x12\xf2\x4c\x38\x83\xd1\x3e\x56\x91\x73\x9c\x8d\x32\x93\x84\xd1\x0b\xdc\xf1\x81\x92\x05\xb6\x1f\xdf\x2b\xe7\xd8\xe0\xc0\xd2\x03\x19\x08\xcf\xc7\xec\xf6\x4b\xd2\x00\x75\xb2\xf7\xe8\xd6\x77\x9f\xa1\x0f\x92\xe1\x70\x7e\x88\x2f\x6f\x4e\xdb\x3c\x3e\xa2\x22\xad\xd2\x78\xb6\xf5\x56\xd0\x46\xb0\xff\x82\x6a\x3b\xe3\x11\x2d\x66\xf3\x28\xd4\xe9\x6f\xd7\xce\x8f\x64\x5a\x6c\x59\xac\xe2\x53\x37\xda\xe7\x5d\x8b\x12\x13\xf2\xb9\xf8\xd8\xe6\x07\xfb\x91\x1c\x58\xba\xd2\xf8\x14\xb0\x5c\x85\xc2\x4d\xb1\xfc\xc1\x32\x3c\xd8\x02\xff\x8d\x84\x3a\xf0\x7e\x67\xfc\xcf\x05\xcf\x35\x9c\xb1\x62\xd3\x83\x4b\xc2\xe0\xa0\x1b\xe2\xd9\xae\xe0\x61\xfa\x9d\x3e\x99\xca\xf4\x2f\x02\x75\xac\x4a\x3e\xb2\x96\x40\xba\x6c\x80\x70\xa5\xda\xb6\x33\x3e\x41\x73\x13\x5a\x1f\xb5\x53\xfa\xe0\x1b\x74\xbb\xf1\x7b\x7b\xe7\x61\x8c\xcb\x07\xa5\x9d\xca\xf8\x9a\xe9\x84\x0f\x5f\xca\xd6\xf7\x01\x8f\xe4\xba\x6b\x3c\xc5\xd5\x6e\x09\x94\x0c\xf3\xef\xfd\x3c\x66\x74\x1b\x95\xf4\x5b\x9c\x38\x3a\x71\xbc\xbe\xbf\x3c\xb6\x80\x98\x55\x28\xe6\x90\x3a\x35\xe2\x4d\x25\x3a\xc7\x62\xb4\xf9\x07\x72\x63\x03\x29\x9b\x42\xa5\x05\x94\x4e\xcb\x3a\x63\xe7\x2f\x46\xc3\x43\x69\x61\x9b\x53\xca\x13\x15\x7c\xe1\x96\x2a\x6c\x06\xe4\x93\x25\x1a\x68\x28\xe7\x6a\x5e\xdf\x7d\x4e\xac\x98\x78\x0e\xe7\x87\xf8\x4b\x36\xb7\x5a\x7b\x32\xae\x88\x07\x04\x0b\x90\x4c\x85\x39\xf6\x56\xef\x7a\x7d\x4b\x11\xfe\xa7\x32\xa3\x5d\x2c\xa7\xac\xfe\x5f\x8a\xe1\xfa\xbb\x6a\xbc\xf6\xf5\x3f\x2b\x48\x80\xe3\xbf\x9a\x0c\x97\x81\x26\xe7\xe8\x1c\xe1\xdf\x00\x00\x00\xff\xff\x0c\xf6\xf8\x93\x75\x07\x00\x00" func metadataSetup_account_from_vault_referenceCdcBytes() ([]byte, error) { @@ -940,6 +961,7 @@ var _bindata = map[string]func() (*asset, error){ "burn_tokens.cdc": burn_tokensCdc, "create_forwarder.cdc": create_forwarderCdc, "generic_transfer.cdc": generic_transferCdc, + "metadata/setup_account_from_address.cdc": metadataSetup_account_from_addressCdc, "metadata/setup_account_from_vault_reference.cdc": metadataSetup_account_from_vault_referenceCdc, "mint_tokens.cdc": mint_tokensCdc, "privateForwarder/create_account_private_forwarder.cdc": privateforwarderCreate_account_private_forwarderCdc, @@ -1024,6 +1046,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "create_forwarder.cdc": {create_forwarderCdc, map[string]*bintree{}}, "generic_transfer.cdc": {generic_transferCdc, map[string]*bintree{}}, "metadata": {nil, map[string]*bintree{ + "setup_account_from_address.cdc": {metadataSetup_account_from_addressCdc, map[string]*bintree{}}, "setup_account_from_vault_reference.cdc": {metadataSetup_account_from_vault_referenceCdc, map[string]*bintree{}}, }}, "mint_tokens.cdc": {mint_tokensCdc, map[string]*bintree{}}, diff --git a/lib/go/templates/transaction_templates.go b/lib/go/templates/transaction_templates.go index b9d4b7e..aa607d7 100644 --- a/lib/go/templates/transaction_templates.go +++ b/lib/go/templates/transaction_templates.go @@ -5,6 +5,8 @@ package templates import ( _ "github.com/kevinburke/go-bindata" + "strings" + "github.com/onflow/flow-ft/lib/go/templates/internal/assets" ) @@ -13,6 +15,7 @@ const ( genericTransferFilename = "generic_transfer.cdc" transferManyAccountsFilename = "transfer_many_accounts.cdc" setupAccountFilename = "setup_account.cdc" + setupGenericVaultFilename = "metadata/setup_account_from_address.cdc" mintTokensFilename = "mint_tokens.cdc" createForwarderFilename = "create_forwarder.cdc" burnTokensFilename = "burn_tokens.cdc" @@ -29,6 +32,29 @@ func GenerateCreateTokenScript(env Environment) []byte { return []byte(ReplaceAddresses(code, env)) } +// GenerateSetupGenericVaultScript creates a script that instantiates +// a new Vault instance and stores it in storage. It can be used +// to create any token as long as you know the address of the contract +// and the name of the contract +func GenerateSetupAccountFromAddressScript(fungibleTokenAddr, fungibleTokenMVAddr string) []byte { + + code := assets.MustAssetString(setupGenericVaultFilename) + + code = strings.ReplaceAll( + code, + placeholderFungibleToken, + withHexPrefix(fungibleTokenAddr), + ) + + code = strings.ReplaceAll( + code, + placeholderFTMetadataViews, + withHexPrefix(fungibleTokenMVAddr), + ) + + return []byte(code) +} + // GenerateTransferVaultScript creates a script that withdraws an tokens from an account // and deposits it to another account's vault func GenerateTransferVaultScript(env Environment) []byte { @@ -40,11 +66,17 @@ func GenerateTransferVaultScript(env Environment) []byte { // GenerateTransferGenericVaultScript creates a script that withdraws an tokens from an account // and deposits it to another account's vault for any vault type -func GenerateTransferGenericVaultScript(env Environment) []byte { +func GenerateTransferGenericVaultScript(fungibleTokenAddr string) []byte { code := assets.MustAssetString(genericTransferFilename) - return []byte(ReplaceAddresses(code, env)) + code = strings.ReplaceAll( + code, + placeholderFungibleToken, + withHexPrefix(fungibleTokenAddr), + ) + + return []byte(code) } // GenerateTransferManyAccountsScript creates a script that transfers the same number of tokens diff --git a/lib/go/test/token_test.go b/lib/go/test/token_test.go index 92f7648..33808c3 100644 --- a/lib/go/test/token_test.go +++ b/lib/go/test/token_test.go @@ -262,7 +262,7 @@ func TestTokenExternalTransfers(t *testing.T) { t.Run("Should be able to transfer tokens with the generic transfer transaction", func(t *testing.T) { - script := templates.GenerateTransferGenericVaultScript(env) + script := templates.GenerateTransferGenericVaultScript(env.FungibleTokenAddress) tx := createTxWithTemplateAndAuthorizer(b, script, joshAddress) diff --git a/tests/metadata_views_tests.cdc b/tests/metadata_views_tests.cdc index d77535a..d611e6d 100644 --- a/tests/metadata_views_tests.cdc +++ b/tests/metadata_views_tests.cdc @@ -6,6 +6,8 @@ import "FungibleTokenMetadataViews" import "ExampleToken" import "FungibleToken" +access(all) let admin = Test.getAccount(0x0000000000000007) + /* Test Setup */ access(all) fun setup() { @@ -27,12 +29,21 @@ access(all) fun testSetupAccountUsingFTView() { setupExampleToken(alice) let aliceBalance = getExampleTokenBalance(alice) txExecutor("metadata/setup_account_from_vault_reference.cdc", [bob], [alice.address, /public/exampleTokenVault], nil, nil) - let bobBalance = getExampleTokenBalance(alice) + let bobBalance = getExampleTokenBalance(bob) Test.assertEqual(0.0, aliceBalance) Test.assertEqual(0.0, bobBalance) } +access(all) fun testSetupAccountUsingContractAddressAndName() { + let bob = Test.createAccount() + + txExecutor("metadata/setup_account_from_address.cdc", [bob], [admin.address, "ExampleToken"], nil, nil) + let bobBalance = getExampleTokenBalance(bob) + + Test.assertEqual(0.0, bobBalance) +} + access(all) fun testRetrieveVaultDisplayInfo() { let alice = Test.createAccount() diff --git a/transactions/metadata/setup_account_from_address.cdc b/transactions/metadata/setup_account_from_address.cdc new file mode 100644 index 0000000..fc8ec45 --- /dev/null +++ b/transactions/metadata/setup_account_from_address.cdc @@ -0,0 +1,37 @@ +import FungibleToken from "FungibleToken" +import FungibleTokenMetadataViews from "FungibleTokenMetadataViews" + +/// This transaction is what an account would run +/// to set itself up to manage fungible tokens. This function +/// uses views to know where to set up the vault +/// in storage and to create the empty vault. + +transaction(contractAddress: Address, contractName: String) { + + prepare(signer: auth(SaveValue, Capabilities) &Account) { + // Borrow a reference to the vault stored on the passed account at the passed publicPath + let resolverRef = getAccount(contractAddress) + .contracts.borrow<&FungibleToken>(name: contractName) + ?? panic("Could not borrow a reference to the fungible token contract") + + // Use that reference to retrieve the FTView + let ftVaultData = resolverRef.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("Could not resolve the FTVaultData view for the given Fungible token contract") + + // Create a new empty vault using the createEmptyVault function inside the FTVaultData + let emptyVault <-ftVaultData.createEmptyVault() + + // Save it to the account + signer.storage.save(<-emptyVault, to: ftVaultData.storagePath) + + // Create a public capability for the vault which includes the .Resolver interface + let vaultCap = signer.capabilities.storage.issue<&{FungibleToken.Vault}>(ftVaultData.storagePath) + signer.capabilities.publish(vaultCap, at: ftVaultData.metadataPath) + + // Create a public capability for the vault exposing the receiver interface + let receiverCap = signer.capabilities.storage.issue<&{FungibleToken.Receiver}>(ftVaultData.storagePath) + signer.capabilities.publish(receiverCap, at: ftVaultData.receiverPath) + + } +} + \ No newline at end of file