From 581572e1206cfe205ec845b390de06e263e1b92c Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Tue, 30 Jan 2024 14:48:29 -0600 Subject: [PATCH 1/4] add unit tests and open up interactive template struct --- flixkit/flix_service.go | 2 + go.mod | 13 +- go.sum | 24 ++-- internal/flix_service.go | 1 + internal/v1_1/FungibleToken.cdc | 237 ++++++++++++++++++++++++++++++++ internal/v1_1/generator.go | 1 + internal/v1_1/generator_test.go | 237 ++++++++++++++++++++++++++++++++ 7 files changed, 497 insertions(+), 18 deletions(-) create mode 100644 internal/v1_1/FungibleToken.cdc diff --git a/flixkit/flix_service.go b/flixkit/flix_service.go index 4f0e3ac..b7cdd71 100644 --- a/flixkit/flix_service.go +++ b/flixkit/flix_service.go @@ -27,6 +27,8 @@ type NetworkAddressMap = internal.NetworkAddressMap // FlixServiceConfig is the configuration for the FlixService that provides a override for FlixServerURL and default values for FileReader and Logger. type FlixServiceConfig = internal.FlixServiceConfig +type InteractiveTemplate = internal.InteractionTemplate + // NewFlixService returns a new FlixService given a FlixServiceConfig func NewFlixService(config *FlixServiceConfig) FlixService { return internal.NewFlixService(config) diff --git a/go.mod b/go.mod index fc1cd0f..d2bee6e 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.20 require ( github.com/ethereum/go-ethereum v1.12.0 github.com/hexops/autogold/v2 v2.2.1 - github.com/onflow/cadence v0.42.5 - github.com/onflow/flow-cli/flowkit v1.6.1-0.20231110211255-b41f57a8b8c7 - github.com/onflow/flow-go-sdk v0.41.16 + github.com/onflow/cadence v0.42.6 + github.com/onflow/flow-cli/flowkit v1.11.0 + github.com/onflow/flow-go-sdk v0.41.17 github.com/spf13/afero v1.9.4 github.com/stoewer/go-strcase v1.3.0 github.com/stretchr/testify v1.8.4 @@ -128,12 +128,12 @@ require ( github.com/onflow/atree v0.6.0 // indirect github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20231016154253-a00dbf7c061f // indirect github.com/onflow/flow-core-contracts/lib/go/templates v1.2.4-0.20231016154253-a00dbf7c061f // indirect - github.com/onflow/flow-emulator v0.58.0 // indirect + github.com/onflow/flow-emulator v0.59.0 // indirect github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20230711213910-baad011d2b13 // indirect - github.com/onflow/flow-go v0.32.4-0.20231115172515-c1ec969fd6f2 // indirect + github.com/onflow/flow-go v0.32.4-0.20231130134727-3c01c7f8966c // indirect github.com/onflow/flow-go/crypto v0.24.10 // indirect github.com/onflow/flow-nft/lib/go/contracts v1.1.0 // indirect - github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231018182244-e72527c55c63 // indirect + github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231124194313-106cc495def6 // indirect github.com/onflow/nft-storefront/lib/go/contracts v0.0.0-20221222181731-14b90207cead // indirect github.com/onflow/sdks v0.5.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -161,6 +161,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.15.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/texttheater/golang-levenshtein/levenshtein v0.0.0-20200805054039-cae8b0eaed6c // indirect diff --git a/go.sum b/go.sum index 49e16f1..abc0992 100644 --- a/go.sum +++ b/go.sum @@ -666,31 +666,31 @@ github.com/onflow/atree v0.1.0-beta1.0.20211027184039-559ee654ece9/go.mod h1:+6x github.com/onflow/atree v0.6.0 h1:j7nQ2r8npznx4NX39zPpBYHmdy45f4xwoi+dm37Jk7c= github.com/onflow/atree v0.6.0/go.mod h1:gBHU0M05qCbv9NN0kijLWMgC47gHVNBIp4KmsVFi0tc= github.com/onflow/cadence v0.20.1/go.mod h1:7mzUvPZUIJztIbr9eTvs+fQjWWHTF8veC+yk4ihcNIA= -github.com/onflow/cadence v0.42.5 h1:QCilotmJzfRToLd+02o3N62JIioSr8FfN7cujmR/IXQ= -github.com/onflow/cadence v0.42.5/go.mod h1:raU8va8QRyTa/eUbhej4mbyW2ETePfSaywoo36MddgE= -github.com/onflow/flow-cli/flowkit v1.6.1-0.20231110211255-b41f57a8b8c7 h1:EI/XTe2E5U23oHzjaLPIm4TKMLD+v2vI5vHjO1BkRyM= -github.com/onflow/flow-cli/flowkit v1.6.1-0.20231110211255-b41f57a8b8c7/go.mod h1:tPBuuYss8S8carovC49N1YVPIBheF6KD9Vr6Ocg3VpQ= +github.com/onflow/cadence v0.42.6 h1:VtI0EpKrdbfqITRMsvyZC4dhgcW1x1LNUQuEpdMDzus= +github.com/onflow/cadence v0.42.6/go.mod h1:raU8va8QRyTa/eUbhej4mbyW2ETePfSaywoo36MddgE= +github.com/onflow/flow-cli/flowkit v1.11.0 h1:RSfKlla/l+ZJwqAmlvA5HPFbQ6ia2wzKSG0kJ8fqVa0= +github.com/onflow/flow-cli/flowkit v1.11.0/go.mod h1:aH4shan7Ggxd0GIXZD2S4kYMemNfzP1rLWvzKnb6K3g= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20231016154253-a00dbf7c061f h1:S8yIZw9LFXfYD1V5H9BiixihHw3GrXVPrmfplSzYaww= github.com/onflow/flow-core-contracts/lib/go/contracts v1.2.4-0.20231016154253-a00dbf7c061f/go.mod h1:jM6GMAL+m0hjusUgiYDNrixPQ6b9s8xjoJQoEu5bHQI= github.com/onflow/flow-core-contracts/lib/go/templates v1.2.4-0.20231016154253-a00dbf7c061f h1:Ep+Mpo2miWMe4pjPGIaEvEzshRep30dvNgxqk+//FrQ= github.com/onflow/flow-core-contracts/lib/go/templates v1.2.4-0.20231016154253-a00dbf7c061f/go.mod h1:ZeLxwaBkzuSInESGjL8/IPZWezF+YOYsYbMrZlhN+q4= -github.com/onflow/flow-emulator v0.58.0 h1:/Zo+qznjqN2jJNOGQu1mFK2lZT3L6c8V18AmAzXLKZ8= -github.com/onflow/flow-emulator v0.58.0/go.mod h1:pi23Sx7kuj65dJRT7OIlio53XRAhLTUUhf3zV9TN3PA= +github.com/onflow/flow-emulator v0.59.0 h1:KIfm9/+x62KqcZDjqE35fkuvVuY506OZ917xNtb3U6E= +github.com/onflow/flow-emulator v0.59.0/go.mod h1:Js1KKaXrui2yKKkXAlKTqmByRySis6/FH+vkGA6Kqu0= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20230711213910-baad011d2b13 h1:B4ll7e3j+MqTJv2122Enq3RtDNzmIGRu9xjV7fo7un0= github.com/onflow/flow-ft/lib/go/contracts v0.7.1-0.20230711213910-baad011d2b13/go.mod h1:kTMFIySzEJJeupk+7EmXs0EJ6CBWY/MV9fv9iYQk+RU= -github.com/onflow/flow-go v0.32.4-0.20231115172515-c1ec969fd6f2 h1:ujrwrJelTX0R3HtX9kSJRoU7bUtcdvf1Pmpvy31yvNQ= -github.com/onflow/flow-go v0.32.4-0.20231115172515-c1ec969fd6f2/go.mod h1:bKcX2932bIunkyXBKW9qmw8Z0zXpHol+5xCJCumV/H8= +github.com/onflow/flow-go v0.32.4-0.20231130134727-3c01c7f8966c h1:75LED6hmarR0uazKZG8nkqqDlUiqz6NdzkdQQiGjvlI= +github.com/onflow/flow-go v0.32.4-0.20231130134727-3c01c7f8966c/go.mod h1:YJDAoDjbY4OWBj44XV+Qe+dIwn+hlywUDL5xclOOLbw= github.com/onflow/flow-go-sdk v0.24.0/go.mod h1:IoptMLPyFXWvyd9yYA6/4EmSeeozl6nJoIv4FaEMg74= -github.com/onflow/flow-go-sdk v0.41.16 h1:HsmHwEVmj+iK+GszHbFseHh7Ii5W3PWOIRNAH/En08Q= -github.com/onflow/flow-go-sdk v0.41.16/go.mod h1:bVrVNoJKiwB6vW5Qbm5tFAfJBQ5we4uSQWnn9gNAFhQ= +github.com/onflow/flow-go-sdk v0.41.17 h1:HpNn3j2fqLGA6H3HGfAuh2A+TsPBv8gWO3kvK9Hvtic= +github.com/onflow/flow-go-sdk v0.41.17/go.mod h1:ZIj2XBI9R0QiKzbI6iPwOeqyIy/M4+atczoMOEWdKYw= github.com/onflow/flow-go/crypto v0.21.3/go.mod h1:vI6V4CY3R6c4JKBxdcRiR/AnjBfL8OSD97bJc60cLuQ= github.com/onflow/flow-go/crypto v0.24.10 h1:nH97tnnC0RaFXO5GuJic5lt/5IEiOuuWa3gaJI2Le3w= github.com/onflow/flow-go/crypto v0.24.10/go.mod h1:O7jjGhgJEp94t9qXfBWdwW5BArm5L5gYa6XoBJiTdHc= github.com/onflow/flow-nft/lib/go/contracts v1.1.0 h1:rhUDeD27jhLwOqQKI/23008CYfnqXErrJvc4EFRP2a0= github.com/onflow/flow-nft/lib/go/contracts v1.1.0/go.mod h1:YsvzYng4htDgRB9sa9jxdwoTuuhjK8WYWXTyLkIigZY= github.com/onflow/flow/protobuf/go/flow v0.2.2/go.mod h1:gQxYqCfkI8lpnKsmIjwtN2mV/N2PIwc1I+RUK4HPIc8= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231018182244-e72527c55c63 h1:SX8OhYbyKBExhy4qEDR/Hw6MVTBTzlDb8LfCHfFyte4= -github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231018182244-e72527c55c63/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= +github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231124194313-106cc495def6 h1:KMN+OEVaw7KAgxL3p8ux7CMuyTvacAlYTbasOqowh4M= +github.com/onflow/flow/protobuf/go/flow v0.3.2-0.20231124194313-106cc495def6/go.mod h1:NA2pX2nw8zuaxfKphhKsk00kWLwfd+tv8mS23YXO4Sk= github.com/onflow/nft-storefront/lib/go/contracts v0.0.0-20221222181731-14b90207cead h1:2j1Unqs76Z1b95Gu4C3Y28hzNUHBix7wL490e61SMSw= github.com/onflow/nft-storefront/lib/go/contracts v0.0.0-20221222181731-14b90207cead/go.mod h1:E3ScfQb5XcWJCIAdtIeEnr5i5l2y60GT0BTXeIHseWg= github.com/onflow/sdks v0.5.0 h1:2HCRibwqDaQ1c9oUApnkZtEAhWiNY2GTpRD5+ftdkN8= diff --git a/internal/flix_service.go b/internal/flix_service.go index ed55ceb..c6e3c8f 100644 --- a/internal/flix_service.go +++ b/internal/flix_service.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-cli/flowkit/output" ) +type InteractionTemplate = v1_1.InteractionTemplate type FileReader interface { ReadFile(path string) ([]byte, error) } diff --git a/internal/v1_1/FungibleToken.cdc b/internal/v1_1/FungibleToken.cdc new file mode 100644 index 0000000..48b092d --- /dev/null +++ b/internal/v1_1/FungibleToken.cdc @@ -0,0 +1,237 @@ +/** + +# The Flow Fungible Token standard + +## `FungibleToken` contract interface + +The interface that all Fungible Token contracts would have to conform to. +If a users wants to deploy a new token contract, their contract +would need to implement the FungibleToken interface. + +Their contract would have to follow all the rules and naming +that the interface specifies. + +## `Vault` resource + +Each account that owns tokens would need to have an instance +of the Vault resource stored in their account storage. + +The Vault resource has methods that the owner and other users can call. + +## `Provider`, `Receiver`, and `Balance` resource interfaces + +These interfaces declare pre-conditions and post-conditions that restrict +the execution of the functions in the Vault. + +They are separate because it gives the user the ability to share +a reference to their Vault that only exposes the fields functions +in one or more of the interfaces. + +It also gives users the ability to make custom resources that implement +these interfaces to do various things with the tokens. +For example, a faucet can be implemented by conforming +to the Provider interface. + +By using resources and interfaces, users of Fungible Token contracts +can send and receive tokens peer-to-peer, without having to interact +with a central ledger smart contract. To send tokens to another user, +a user would simply withdraw the tokens from their Vault, then call +the deposit function on another user's Vault to complete the transfer. + +*/ + +/// The interface that Fungible Token contracts implement. +/// +pub contract interface FungibleToken { + + /// The total number of tokens in existence. + /// It is up to the implementer to ensure that the total supply + /// stays accurate and up to date + pub var totalSupply: UFix64 + + /// The event that is emitted when the contract is created + pub event TokensInitialized(initialSupply: UFix64) + + /// The event that is emitted when tokens are withdrawn from a Vault + pub event TokensWithdrawn(amount: UFix64, from: Address?) + + /// The event that is emitted when tokens are deposited into a Vault + pub event TokensDeposited(amount: UFix64, to: Address?) + + /// The interface that enforces the requirements for withdrawing + /// tokens from the implementing type. + /// + /// It does not enforce requirements on `balance` here, + /// because it leaves open the possibility of creating custom providers + /// that do not necessarily need their own balance. + /// + pub resource interface Provider { + + /// Subtracts tokens from the owner's Vault + /// and returns a Vault with the removed tokens. + /// + /// The function's access level is public, but this is not a problem + /// because only the owner storing the resource in their account + /// can initially call this function. + /// + /// The owner may grant other accounts access by creating a private + /// capability that allows specific other users to access + /// the provider resource through a reference. + /// + /// The owner may also grant all accounts access by creating a public + /// capability that allows all users to access the provider + /// resource through a reference. + /// + /// @param amount: The amount of tokens to be withdrawn from the vault + /// @return The Vault resource containing the withdrawn funds + /// + pub fun withdraw(amount: UFix64): @Vault { + post { + // `result` refers to the return value + result.balance == amount: + "Withdrawal amount must be the same as the balance of the withdrawn Vault" + } + } + } + + /// The interface that enforces the requirements for depositing + /// tokens into the implementing type. + /// + /// We do not include a condition that checks the balance because + /// we want to give users the ability to make custom receivers that + /// can do custom things with the tokens, like split them up and + /// send them to different places. + /// + pub resource interface Receiver { + + /// Takes a Vault and deposits it into the implementing resource type + /// + /// @param from: The Vault resource containing the funds that will be deposited + /// + pub fun deposit(from: @Vault) + + /// Below is referenced from the FLIP #69 https://github.com/onflow/flips/blob/main/flips/20230206-fungible-token-vault-type-discovery.md + /// + /// Returns the dictionary of Vault types that the the receiver is able to accept in its `deposit` method + /// this then it would return `{Type<@FlowToken.Vault>(): true}` and if any custom receiver + /// uses the default implementation then it would return empty dictionary as its parent + /// resource doesn't conform with the `FungibleToken.Vault` resource. + /// + /// Custom receiver implementations are expected to upgrade their contracts to add an implementation + /// that supports this method because it is very valuable for various applications to have. + /// + /// @return dictionary of supported deposit vault types by the implementing resource. + /// + pub fun getSupportedVaultTypes(): {Type: Bool} { + // Below check is implemented to make sure that run-time type would + // only get returned when the parent resource conforms with `FungibleToken.Vault`. + if self.getType().isSubtype(of: Type<@FungibleToken.Vault>()) { + return {self.getType(): true} + } else { + // Return an empty dictionary as the default value for resource who don't + // implement `FungibleToken.Vault`, such as `FungibleTokenSwitchboard`, `TokenForwarder` etc. + return {} + } + } + } + + /// The interface that contains the `balance` field of the Vault + /// and enforces that when new Vaults are created, the balance + /// is initialized correctly. + /// + pub resource interface Balance { + + /// The total balance of a vault + /// + pub var balance: UFix64 + + init(balance: UFix64) { + post { + self.balance == balance: + "Balance must be initialized to the initial balance" + } + } + + /// Function that returns all the Metadata Views implemented by a Fungible Token + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + pub fun getViews(): [Type] { + return [] + } + + /// Function that resolves a metadata view for this fungible token by type. + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + pub fun resolveView(_ view: Type): AnyStruct? { + return nil + } + } + + /// The resource that contains the functions to send and receive tokens. + /// The declaration of a concrete type in a contract interface means that + /// every Fungible Token contract that implements the FungibleToken interface + /// must define a concrete `Vault` resource that conforms to the `Provider`, `Receiver`, + /// and `Balance` interfaces, and declares their required fields and functions + /// + pub resource Vault: Provider, Receiver, Balance { + + /// The total balance of the vault + pub var balance: UFix64 + + // The conforming type must declare an initializer + // that allows providing the initial balance of the Vault + // + init(balance: UFix64) + + /// Subtracts `amount` from the Vault's balance + /// and returns a new Vault with the subtracted balance + /// + /// @param amount: The amount of tokens to be withdrawn from the vault + /// @return The Vault resource containing the withdrawn funds + /// + pub fun withdraw(amount: UFix64): @Vault { + pre { + self.balance >= amount: + "Amount withdrawn must be less than or equal than the balance of the Vault" + } + post { + // use the special function `before` to get the value of the `balance` field + // at the beginning of the function execution + // + self.balance == before(self.balance) - amount: + "New Vault balance must be the difference of the previous balance and the withdrawn Vault" + } + } + + /// Takes a Vault and deposits it into the implementing resource type + /// + /// @param from: The Vault resource containing the funds that will be deposited + /// + pub fun deposit(from: @Vault) { + // Assert that the concrete type of the deposited vault is the same + // as the vault that is accepting the deposit + pre { + from.isInstance(self.getType()): + "Cannot deposit an incompatible token type" + } + post { + self.balance == before(self.balance) + before(from.balance): + "New Vault balance must be the sum of the previous balance and the deposited Vault" + } + } + } + + /// Allows any user to create a new Vault that has a zero balance + /// + /// @return The new Vault resource + /// + pub fun createEmptyVault(): @Vault { + post { + result.balance == 0.0: "The newly created Vault must have zero balance" + } + } +} diff --git a/internal/v1_1/generator.go b/internal/v1_1/generator.go index 10629ca..1e3ef63 100644 --- a/internal/v1_1/generator.go +++ b/internal/v1_1/generator.go @@ -259,6 +259,7 @@ func generateDependencyNetworks(ctx context.Context, flowkit *flowkit.Flowkit, a if err != nil { return nil, err } + code := account.Contracts[name] depend := PinDetail{ PinContractName: name, diff --git a/internal/v1_1/generator_test.go b/internal/v1_1/generator_test.go index 31ffdce..377f0ae 100644 --- a/internal/v1_1/generator_test.go +++ b/internal/v1_1/generator_test.go @@ -1,13 +1,26 @@ package v1_1 import ( + "bytes" "context" + _ "embed" + "encoding/hex" "testing" + "github.com/ethereum/go-ethereum/rlp" "github.com/hexops/autogold/v2" "github.com/onflow/flixkit-go/internal/contracts" + "github.com/onflow/flow-cli/flowkit" + "github.com/onflow/flow-cli/flowkit/accounts" "github.com/onflow/flow-cli/flowkit/config" + "github.com/onflow/flow-cli/flowkit/gateway/mocks" + "github.com/onflow/flow-cli/flowkit/output" + "github.com/onflow/flow-cli/flowkit/tests" + "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go-sdk/crypto" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestHelloScript(t *testing.T) { @@ -176,3 +189,227 @@ func TestTransferFlowTransaction(t *testing.T) { autogold.ExpectFile(t, template) } + +var templateHashedTester = ` + { + "f_type": "InteractionTemplate", + "f_version": "1.1.0", + "id": "3a99af243b85f3f6af28304af2ed53a37fb913782b3efc483e6f0162a47720a0", + "data": { + "type": "transaction", + "interface": "", + "messages": [ + { + "key": "title", + "i18n": [ + { + "tag": "en-US", + "translation": "Transfer Tokens" + } + ] + }, + { + "key": "description", + "i18n": [ + { + "tag": "en-US", + "translation": "Transfer Flow to account" + } + ] + } + ], + "cadence": { + "body": "import \"FungibleToken\"\n\n#interaction(\n version: \"1.1.0\",\n title: \"Transfer Flow\",\n description: \"Transfer Flow to account\",\n language: \"en-US\",\n parameters: [\n Parameter(\n name: \"amount\", \n title: \"Amount\", \n description: \"The amount of FLOW tokens to send\"\n ),\n Parameter(\n name: \"to\", \n title: \"To\",\n description: \"The Flow account the tokens will go to\"\n )\n ],\n)\n\ntransaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n \n prepare(signer: AuthAccount) {\n self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n }\n\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n}", + "network_pins": [ + { + "network": "mainnet", + "pin_self": "dd046de8ef442e4d708124d5710cb78962eb884a4387df1f0b1daf374bd28278" + }, + { + "network": "testnet", + "pin_self": "4089786f5e19fe66b39e347634ca28229851f4de1fd469bd8f327d79510e771f" + } + ] + }, + "dependencies": [ + { + "contracts": [ + { + "contract": "FungibleToken", + "networks": [ + { + "network": "mainnet", + "address": "0xf233dcee88fe0abe", + "dependency_pin_block_height": 70493190, + "dependency_pin": { + "pin": "ac0208f93d07829ec96584d618ddbec6af3cf4e2866bd5071249e8ec93c7e0dc", + "pin_self": "cdadd5b5897f2dfe35d8b25f4e41fea9f8fca8f40f8a8b506b33701ef5033076", + "pin_contract_name": "FungibleToken", + "pin_contract_address": "0xf233dcee88fe0abe", + "imports": [] + } + }, + { + "network": "testnet", + "address": "0x9a0766d93b6608b7", + "dependency_pin_block_height": 149595558, + "dependency_pin": { + "pin": "ac0208f93d07829ec96584d618ddbec6af3cf4e2866bd5071249e8ec93c7e0dc", + "pin_self": "cdadd5b5897f2dfe35d8b25f4e41fea9f8fca8f40f8a8b506b33701ef5033076", + "pin_contract_name": "FungibleToken", + "pin_contract_address": "0x9a0766d93b6608b7", + "imports": [] + } + }, + { + "network": "emulator", + "address": "0xee82856bf20e2aa6", + "dependency_pin_block_height": 0 + } + ] + } + ] + } + ], + "parameters": [ + { + "label": "amount", + "index": 0, + "type": "UFix64", + "messages": [ + { + "key": "title", + "i18n": [ + { + "tag": "en-US", + "translation": "Amount" + } + ] + }, + { + "key": "description", + "i18n": [ + { + "tag": "en-US", + "translation": "The amount of FLOW tokens to send" + } + ] + } + ] + }, + { + "label": "to", + "index": 1, + "type": "Address", + "messages": [ + { + "key": "title", + "i18n": [ + { + "tag": "en-US", + "translation": "To" + } + ] + }, + { + "key": "description", + "i18n": [ + { + "tag": "en-US", + "translation": "The Flow account the tokens will go to" + } + ] + } + ] + } + ] + } + } +` + +func TestGenerateTemplateIdTransaction(t *testing.T) { + + flix, err := ParseFlix(templateHashedTester) + assert.NoError(t, err) + assert.Equal(t, "1.1.0", flix.FVersion) + + // program, err := parser.ParseProgram(nil, []byte(flix.Data.Cadence.Body), parser.Config{}) + assert.NoError(t, err) + prlp := parametersToRlp(flix.Data.Parameters) + fullyHashed := getHashedValue(prlp) + assert.Equal(t, "b5838adae6019bef5455241281621d45543fc27f48815476cddc9b1939de0d76", fullyHashed) + + m := messagesToRlp(flix.Data.Messages) + hashed := getHashedValue(m) + assert.Equal(t, "064d5a1903b9ebb4224c3345432cae5aa01650b71408f2518a6458e744afaf6d", hashed) + +} + +//go:embed FungibleToken.cdc +var fungibleTokenContract string + +func TestNetworkHashingIds(t *testing.T) { + contractName := "FungibleToken" + flix, err := ParseFlix(templateHashedTester) + assert.NoError(t, err) + assert.NotNil(t, flix) + assert.Equal(t, "1.1.0", flix.FVersion) + var mockFS = afero.NewMemMapFs() + var rw = afero.Afero{Fs: mockFS} + + _, fKit, gw := setup(rw) + a := bobMainnet() + + gw.GetAccount.Run(func(args mock.Arguments) { + addr := args.Get(0).(flow.Address) + racc := tests.NewAccountWithAddress(addr.String()) + racc.Contracts = map[string][]byte{ + contractName: []byte(fungibleTokenContract), + } + + gw.GetAccount.Return(racc, nil) + }) + + memoize := make(map[string]PinDetail) + ctx := context.Background() + + details, err := generateDependencyNetworks(ctx, fKit, a.Address.String(), contractName, memoize, 0) + assert.NoError(t, err) + assert.Equal(t, "ac0208f93d07829ec96584d618ddbec6af3cf4e2866bd5071249e8ec93c7e0dc", details.Pin) + assert.NotNil(t, details) +} + +func bobMainnet() *accounts.Account { + return newAccount("BobMainnet", "0xf233dcee88fe0abe", "seedseedseedseedseedseedseedseedseedseedseedseedAlice") +} + +func newAccount(name string, address string, seed string) *accounts.Account { + privateKey, _ := crypto.GeneratePrivateKey(crypto.ECDSA_P256, []byte(seed)) + + return &accounts.Account{ + Name: name, + Address: flow.HexToAddress(address), + Key: accounts.NewHexKeyFromPrivateKey(0, crypto.SHA3_256, privateKey), + } +} + +func setup(rw flowkit.ReaderWriter) (*flowkit.State, *flowkit.Flowkit, *mocks.TestGateway) { + state, err := flowkit.Init(rw, crypto.ECDSA_P256, crypto.SHA3_256) + if err != nil { + panic(err) + } + gw := mocks.DefaultMockGateway() + logger := output.NewStdoutLogger(output.NoneLog) + flowkit := flowkit.NewFlowkit(state, config.TestnetNetwork, gw.Mock, logger) + + return state, flowkit, gw +} +func getHashedValue(values []interface{}) string { + input := []interface{}{values} + var buffer bytes.Buffer + rlp.Encode(&buffer, input) + hexString := hex.EncodeToString(buffer.Bytes()) + + return ShaHex(hexString, "parameters") + +} From aa6fb64e08535faaac2d80ec94c58633056a9a74 Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Tue, 30 Jan 2024 14:52:32 -0600 Subject: [PATCH 2/4] add description for interactive template --- flixkit/flix_service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/flixkit/flix_service.go b/flixkit/flix_service.go index b7cdd71..84802dc 100644 --- a/flixkit/flix_service.go +++ b/flixkit/flix_service.go @@ -27,6 +27,7 @@ type NetworkAddressMap = internal.NetworkAddressMap // FlixServiceConfig is the configuration for the FlixService that provides a override for FlixServerURL and default values for FileReader and Logger. type FlixServiceConfig = internal.FlixServiceConfig +// InteractionTemplate is the struct that FLIX json files can be parsed into. type InteractiveTemplate = internal.InteractionTemplate // NewFlixService returns a new FlixService given a FlixServiceConfig From f44ce6060dd4762979b18f5ab27bd7478453b6ac Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Wed, 31 Jan 2024 15:35:15 -0600 Subject: [PATCH 3/4] fix generate template id --- internal/v1_1/generator.go | 1 + internal/v1_1/generator_test.go | 26 ++++++++++++++++++- internal/v1_1/testdata/TestHelloScript.golden | 2 +- .../v1_1/testdata/TestTransactionValue.golden | 2 +- .../TestTransferFlowTransaction.golden | 2 +- internal/v1_1/types.go | 7 ++--- internal/v1_1/types_test.go | 2 +- 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/internal/v1_1/generator.go b/internal/v1_1/generator.go index 1e3ef63..9ead6da 100644 --- a/internal/v1_1/generator.go +++ b/internal/v1_1/generator.go @@ -283,6 +283,7 @@ func generateDependencyNetworks(ctx context.Context, flowkit *flowkit.Flowkit, a } pins = append(pins, dep.PinSelf) } + depend.Imports = detailImports depend.Pin = ShaHex(strings.Join(pins, ""), "") return &depend, nil diff --git a/internal/v1_1/generator_test.go b/internal/v1_1/generator_test.go index 377f0ae..8ff275f 100644 --- a/internal/v1_1/generator_test.go +++ b/internal/v1_1/generator_test.go @@ -194,7 +194,7 @@ var templateHashedTester = ` { "f_type": "InteractionTemplate", "f_version": "1.1.0", - "id": "3a99af243b85f3f6af28304af2ed53a37fb913782b3efc483e6f0162a47720a0", + "id": "3accd8c0bf4c7b543a80287d6c158043b4c2e737c2205dba6e009abbbf1328a4", "data": { "type": "transaction", "interface": "", @@ -336,6 +336,7 @@ func TestGenerateTemplateIdTransaction(t *testing.T) { // program, err := parser.ParseProgram(nil, []byte(flix.Data.Cadence.Body), parser.Config{}) assert.NoError(t, err) prlp := parametersToRlp(flix.Data.Parameters) + fullyHashed := getHashedValue(prlp) assert.Equal(t, "b5838adae6019bef5455241281621d45543fc27f48815476cddc9b1939de0d76", fullyHashed) @@ -345,6 +346,28 @@ func TestGenerateTemplateIdTransaction(t *testing.T) { } +func TestGenerateFlixNetworkRLP(t *testing.T) { + + flix, err := ParseFlix(templateHashedTester) + assert.NoError(t, err) + assert.Equal(t, "1.1.0", flix.FVersion) + + // program, err := parser.ParseProgram(nil, []byte(flix.Data.Cadence.Body), parser.Config{}) + assert.NoError(t, err) + networkHahes := networksToRlp(flix.Data.Dependencies[0].Contracts[0].Networks) + + fullyHashed := getHashedValue(networkHahes) + assert.Equal(t, "2d4a4c4f90aab978e297d14ebd9713e37173cefd84ba55fd4d4a6c5bb018ec63", fullyHashed) + + contractHahes := contractsToRlp(flix.Data.Dependencies[0].Contracts) + contractCollectionHashed := getHashedValue(contractHahes) + assert.Equal(t, "e12a2186edfc2e0ea06c29195576371591fc09d2c05bdea2048f5a3d674f17c2", contractCollectionHashed) + + idValue, err := flix.EncodeRLP() + assert.NoError(t, err) + assert.Equal(t, "3accd8c0bf4c7b543a80287d6c158043b4c2e737c2205dba6e009abbbf1328a4", idValue) +} + //go:embed FungibleToken.cdc var fungibleTokenContract string @@ -377,6 +400,7 @@ func TestNetworkHashingIds(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "ac0208f93d07829ec96584d618ddbec6af3cf4e2866bd5071249e8ec93c7e0dc", details.Pin) assert.NotNil(t, details) + } func bobMainnet() *accounts.Account { diff --git a/internal/v1_1/testdata/TestHelloScript.golden b/internal/v1_1/testdata/TestHelloScript.golden index 478a19f..a504064 100644 --- a/internal/v1_1/testdata/TestHelloScript.golden +++ b/internal/v1_1/testdata/TestHelloScript.golden @@ -1,7 +1,7 @@ `{ "f_type": "InteractionTemplate", "f_version": "1.1.0", - "id": "00ea34585b7351092fe871b23b8e894e339068886058b19d79dcdce168e54a93", + "id": "e094ed9d1187d935320b45ada8bb5913abcb9793e249bee4d855e8b8dbf69ae3", "data": { "type": "script", "interface": "", diff --git a/internal/v1_1/testdata/TestTransactionValue.golden b/internal/v1_1/testdata/TestTransactionValue.golden index 3dff4f4..3e2cd94 100644 --- a/internal/v1_1/testdata/TestTransactionValue.golden +++ b/internal/v1_1/testdata/TestTransactionValue.golden @@ -1,7 +1,7 @@ `{ "f_type": "InteractionTemplate", "f_version": "1.1.0", - "id": "88d07e6a46150c5e241f637d69b324fcbce1b7f06c130d442638422cff704b58", + "id": "288eda8cf11f526fd3e56835db1382458c2344bdb6b59e3f8caa0108e13068d1", "data": { "type": "transaction", "interface": "", diff --git a/internal/v1_1/testdata/TestTransferFlowTransaction.golden b/internal/v1_1/testdata/TestTransferFlowTransaction.golden index f153612..8627ea0 100644 --- a/internal/v1_1/testdata/TestTransferFlowTransaction.golden +++ b/internal/v1_1/testdata/TestTransferFlowTransaction.golden @@ -1,7 +1,7 @@ `{ "f_type": "InteractionTemplate", "f_version": "1.1.0", - "id": "2233dc61f9e187efe089977e503a68024a6fd1d35e0971d8f36b1016ba032974", + "id": "c4f80fcd02e39ff627b08ca448e375c6cb295859cc4c234619ffe67710a75fc4", "data": { "type": "transaction", "interface": "", diff --git a/internal/v1_1/types.go b/internal/v1_1/types.go index 9cea150..6ef2859 100644 --- a/internal/v1_1/types.go +++ b/internal/v1_1/types.go @@ -484,6 +484,7 @@ func networksToRlp(Networks []Network) []interface{} { } values = append(values, networks) } + return values } @@ -517,14 +518,14 @@ func (flix InteractionTemplate) EncodeRLP() (result string, err error) { ShaHex(flix.Data.Type, ""), ShaHex(flix.Data.Interface, ""), messagesToRlp(flix.Data.Messages), - ShaHex(flix.Data.Cadence, ""), + ShaHex(flix.Data.Cadence.Body, ""), dependenciesToRlp(flix.Data.Dependencies), parametersToRlp(flix.Data.Parameters), } // msg := dependenciesToRlp(flix.Data.Dependencies) - //prettyJSON, _ := json.MarshalIndent(input, "", " ") - //fmt.Println(string(prettyJSON)) + // prettyJSON, _ := json.MarshalIndent(input, "", " ") + // fmt.Println(string(prettyJSON)) err = rlp.Encode(&buffer, input) if err != nil { diff --git a/internal/v1_1/types_test.go b/internal/v1_1/types_test.go index fd8a322..652c215 100644 --- a/internal/v1_1/types_test.go +++ b/internal/v1_1/types_test.go @@ -251,7 +251,7 @@ func TestGenerateParametersScripts(t *testing.T) { } func TestGenerateTemplateIdWithDeps(t *testing.T) { - templateId := "fcada4d7a654a0386a4bb048ac4c851ad7de3945e6e835dc4593581b8c8113da" + templateId := "3959702214a6991edba75c4722a5573736b185a6a9c38ca2ae0c28e0dc6e8d9b" code := ` { "f_type": "InteractionTemplate", From 1e37c660b9d224f69b21e40bdb58f7a7ddee292c Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Wed, 31 Jan 2024 15:37:24 -0600 Subject: [PATCH 4/4] remove making InteractiveTemplate avaiable publicly, need more requirements gathering --- flixkit/flix_service.go | 3 --- internal/flix_service.go | 1 - 2 files changed, 4 deletions(-) diff --git a/flixkit/flix_service.go b/flixkit/flix_service.go index 84802dc..4f0e3ac 100644 --- a/flixkit/flix_service.go +++ b/flixkit/flix_service.go @@ -27,9 +27,6 @@ type NetworkAddressMap = internal.NetworkAddressMap // FlixServiceConfig is the configuration for the FlixService that provides a override for FlixServerURL and default values for FileReader and Logger. type FlixServiceConfig = internal.FlixServiceConfig -// InteractionTemplate is the struct that FLIX json files can be parsed into. -type InteractiveTemplate = internal.InteractionTemplate - // NewFlixService returns a new FlixService given a FlixServiceConfig func NewFlixService(config *FlixServiceConfig) FlixService { return internal.NewFlixService(config) diff --git a/internal/flix_service.go b/internal/flix_service.go index c6e3c8f..ed55ceb 100644 --- a/internal/flix_service.go +++ b/internal/flix_service.go @@ -13,7 +13,6 @@ import ( "github.com/onflow/flow-cli/flowkit/output" ) -type InteractionTemplate = v1_1.InteractionTemplate type FileReader interface { ReadFile(path string) ([]byte, error) }