diff --git a/docs/tools/clients/index.md b/docs/tools/clients/index.md index b0a28dc45b..29998aa1c8 100644 --- a/docs/tools/clients/index.md +++ b/docs/tools/clients/index.md @@ -16,10 +16,6 @@ [Flow JVM SDK](https://github.com/onflow/flow-jvm-sdk) is a library for JVM-compatible languages, implemented in Kotlin, that provides utilities to interact with the Flow blockchain. -## Unity - -[Flow SDK for Unity](./unity-sdk/index.md) allows Unity developers to integrate their games and applications with the Flow blockchain. - ## JavaScript (FCL) [Flow Client Library (FCL)](./fcl-js/index.md) is a package used to interact with user wallets and the Flow blockchain. diff --git a/docs/tools/clients/unity-sdk/api-reference.md b/docs/tools/clients/unity-sdk/api-reference.md deleted file mode 100644 index b234b8b08c..0000000000 --- a/docs/tools/clients/unity-sdk/api-reference.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: API Reference -sidebar_position: 1 -sidebar_label: API Reference ---- - -Click on the following link to view the API Reference for the Flow SDK for Unity. - -[](https://unity-flow-sdk-api-docs.vercel.app/) \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/changelog.md b/docs/tools/clients/unity-sdk/changelog.md deleted file mode 100644 index 80f81f3c0a..0000000000 --- a/docs/tools/clients/unity-sdk/changelog.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Changelog -sidebar_position: 4 -sidebar_label: Changelog ---- -## Version 2.0.0 - -- Added new wallet provider Wallet Connect -- Added iOS as a supported platform -- Added Cadence Convert feature for simple de/serialization -- Changed how wallet providers are instantiated (see Breaking Changes) - -### Breaking Changes in 2.0.0 - -Previously, wallet providers (ie DevWallet) were instantiated as follows: - -```csharp -FlowSDK.RegisterWalletProvider(ScriptableObject.CreateInstance()); -``` - -From 2.0.0, this should now be: - -```csharp -FlowSDK.RegisterWalletProvider(new DevWalletProvider()); -``` - -## Version 1.0.3 - -- Add Example NFT sample - -## Version 1.0.2 - -- Fixed an issue where the latest Flow emulator would cause the editor to freeze - -## Version 1.0.1 - -- Added wallet authentication to contract and account creation tools -- Fixed all Unity warnings in all samples -- Removed unused and commented code -- Added README files to all samples -- Emulator listens on all local IP addresses -- Improved error handling from all API requests -- Improved error handling and feedback from Flow Control tools -- Limited Events.GetForBlockIds request to 50 block ids -- Fixed login panel on Flow Words Tutorial sample - -## Version 1.0.0 - -- Initial release of the Flow SDK for Unity \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/guides/_category_.yml b/docs/tools/clients/unity-sdk/guides/_category_.yml deleted file mode 100644 index c65b5e747e..0000000000 --- a/docs/tools/clients/unity-sdk/guides/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Guides -position: 2 \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/guides/convert.md b/docs/tools/clients/unity-sdk/guides/convert.md deleted file mode 100644 index 517c319d4a..0000000000 --- a/docs/tools/clients/unity-sdk/guides/convert.md +++ /dev/null @@ -1,231 +0,0 @@ -# Flow SDK Convert - -The FlowSDK provides a Convert class to convert between Cadence and C# datatypes. It provides two functions, ToCadence and FromCadence. - -The conversion is strict in regards to the types of the data. C# types *must* match the Cadence type being converted from/to or an exception will be thrown. The following are the required types: - -Cadence|C# ----|--- -Int|BigInteger -UInt|BigInteger -Int8|SByte -Int16|Int16 -Int32|Int32 -Int64|Int64 -Int128|BigInteger -Int256|BigInteger -UInt8|Byte -UInt16|UInt16 -UInt32|UInt32 -UInt64|UInt64 -UInt128|BigInteger -UInt256|BigInteger -Word8|Byte -Word16|UInt16 -Word32|UInt32 -Word64|UInt64 -Fix64|Decimal -UFix64|Decimal -Address|String -String|String -Bool|Boolean -Path|CadencePathValue -Capability|CadenceCapabilityValue -`Array ([T])`|List<T> -`Dictionary ({T:R})`|Dictionary<T,R> -Struct|Class or Struct - - -## Usage - -### ToCadence - -The Convert.ToCadence function has the following signature: -```csharp -public static CadenceBase ToCadence(object source, string destinationType) -``` - -ToCadence will return a CadenceBase that can be passed into Script and Transaction functions. Example: - -```csharp -CadenceBase cb = Convert.ToCadence((Int64)44, "Int64"); -``` - -This will result in a CadenceNumber with a value of "44" and a type of "Int64". However: - -```csharp -CadenceBase cb = Convert.ToCadence(44, "Int64"); -``` - -will result in an exception "Exception: Can not convert System.Int32 into Int64. Requires source to be System.Int64." - -Similarly, the Cadence Int type is an arbitrary precision integer type and thus must be created using a System.BigInteger. - -```csharp -CadenceBase cb = Convert.ToCadence(new BigInteger(44), "Int"); -``` - -### FromCadence - -The Convert.FromCadence has the following signature: - -```csharp -public static T FromCadence(CadenceBase cadence) -``` -where T is the C# you would like the Cadence converted into. - -FromCadence will return a value of the requested type given a CadenceBase value. The Cadence and C# types must match. - -```csharp -CadenceBase cb = new CadenceNumber(CadenceNumberType.Int16, "44"); -Int16 i = Convert.FromCadence(cb); -``` - -If the requested and source types do not match, an exception will be thrown. - -```csharp -CadenceBase cb = new CadenceNumber(CadenceNumberType.Int16, "44"); -Int64 i = Convert.FromCadence(cb); -``` - -The above results in an exception: "Exception: Attempt to convert cadence to invalid type. Cadence type Int16 expects System.Int16, got System.Int64". - -### Composite Types - -Using the Convert class on primitive types isn't much easier than constructing the Cadence types yourself. Using it on Composite types is much more useful. - -Convert can convert between Cadence composite types (Struct, Enum, Resource, Event, Contract) and C# structs/classes. In order to annotate your C# classes/structs a CadenceAttribute is provided. -The CadenceAttribute has two properties. The Name and CadenceType properties allow you to provide hints to the converter as to what each C# types should be when converted to Cadence. - -Given the following C# class: - -```csharp -public class TestStruct -{ - public Int32 i32; - public String s; -} -``` - -and the following Cadence struct: - -```cadence -access(all) struct TestStruct { - access(all) let i32: Int32 - access(all) let s: String -} -``` - -you can convert from Cadence to C# using: - -```csharp -TestStruct ts = Convert.FromCadence(cadence); -``` - -It will match based on the field names, and the types are compatible. Converting from C# to Cadence, on the other hand, requires annotations. - -```csharp -[Cadence(CadenceType="A.XXX.CCC.TestStruct")] -public class TestStruct -{ - [Cadence(CadenceType="Int32", Name="i32")] - public Int32 i; - [Cadence(CadenceType="String")] - public String s; -} -``` - -This is because a C# string could be a Cadence Address or Cadence String. For consistency, Convert requires all C#->Cadence conversions to be annotated with a CadenceType. If a field is not -annotated, it will be skipped when converting from C# to Cadence. - -You can also use the Name parameter to account for differences in field naming. In the above example we mapped the Cadence "i32" field to the C# "i" field. The Name property is optional and -it will use the field name if no Name property is given. - -***Note: The CadenceType annotation on C# classes/structs (`[Cadence(CadenceType="A.XXX.CCC.TestStruct")]` in the above example) is ignored when converting from Cadence to C#. -Convert.FromCadence will populate all fields that have matching names/types regardless of the type of Cadence struct that is being converted. This allows you to convert a Cadence struct defined -int a Cadence script into C# even if you do not know what the Cadence type is.*** - -The class annotation (A.XXX.CCC.TestStruct) is required when converting from C# to Cadence. XXX should be the address of the account that contains the contract where the struct is -defined, without a leading "0x". The CCC is the name of the contract. - -Here's an example using the NBA TopShot contract (https://github.com/dapperlabs/nba-smart-contracts/blob/master/contracts/TopShot.cdc) on TestNet, defining a class to hold the Play struct: - -```csharp -[Cadence(CadenceType="A.877931736ee77cff.TopShot.Play")] -public class Play -{ - [Cadence(CadenceType="UInt32")] - public UInt32 playID; - [Cadence(CadenceType="{String:String}")] - public Dictionary metadata; -} -``` - -## Structs inside Structs - -If a Cadence struct contains another struct, the field should be annotated as a "Struct". Given the following Cadence: - -```cadence -access(all) struct Other { - access(all) let i: Int16 - - access(all) init(i:Int16) { - self.i=i - } -} - -access(all) struct Test { - access(all) let o : Other - - access(all) init(i: Int16) { - self.o = Other(i:i) - } -} -``` - -you could use this C#: - -```csharp -[Cadence(CadenceType="A.xxx.ccc.Nested")] -public class Other -{ - [Cadence(CadenceType="Int16")] - public Int16 i; -} - -[Cadence(CadenceType="A.xxx.ccc.Test")] -public class Test -{ - [Cadence(CadenceType="Struct")] - public Other o; -} -``` - -If you have a Cadence Test struct you can convert into C# using: -```csharp -TestStruct ts = Convert.FromCadence(cadence); -``` - -If you have a C# Test object, you can convert to Cadence using: -```csharp -CadenceBase cb = Convert.ToCadence(ts, "Struct"); -``` - -## Optionals - -Cadence optionals are indicated by appending a ?. For instance the Cadence type Int16? can contain either an Int16 value or nil. If the C# type is a reference type, no additional work is required. -For instance a Cadence String? will have the C# equivalent type of String. This is because the C# String is a reference type, which can natively be set to null. On the other hand, the Cadence Int16? requires the C# type Int16? which wraps type the value type Int16 in a `Nullable<>`. - -```csharp -//c is a Cadence Int16? type -Int16? i = Convert.FromCadence(c); -``` - -Trying to convert a Cadence optional into a non-nullable type results in an exception: - -```csharp -//c is a Cadence Int16? type -Int16 i = Convert.FromCadence(c); -``` - -"Exception: CadenceOptional requires a nullable type" \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/guides/dev-wallet.md b/docs/tools/clients/unity-sdk/guides/dev-wallet.md deleted file mode 100644 index 612026040b..0000000000 --- a/docs/tools/clients/unity-sdk/guides/dev-wallet.md +++ /dev/null @@ -1,114 +0,0 @@ -# Dev Wallet - -## Introduction - -Dev Wallet is an implementation of a wallet provider for development purposes. It provides the following: - -- An example of how to implement the `IWallet` interface, should you wish to implement your own wallet provider. -- An easy way to sign transactions during development, without the burden of connecting to real wallets. - -***IMPORTANT:*** Never use Dev Wallet in a production build, as it is not secure. - -The initial v1.0.0 release of the Flow SDK for Unity does not provide a production ready wallet provider. We are working hard to release one in the very near future. In the meantime, you can attempt to create your own wallet provider implementing the `IWallet` interface, or stick with Dev Wallet while you develop your game. - -## What are Wallets? - -A wallet is a piece of software or hardware that stores the private key associated with a Flow account. The term *custodian* is used to refer to the party that stores the private key. Hardware Wallets (eg Ledger), typically USB devices, allow users to be their own custodian, whereas hosted software wallets (eg Dapper Wallet) act as the custodian on behalf of the user. - -## What is a Wallet Provider? - -In terms of the Flow SDK for Unity, a Wallet Provider is a class which implements the `IWallet` interface and allows users to interact with specific hardware or software wallets. This includes authenticating with a wallet, retrieving the user's Flow account address from the wallet, and requesting the wallet to sign transactions on behalf of the user. - -As of v1.0.0, the Flow SDK for Unity only contains a single wallet provider - Dev Wallet. This is not a real wallet provider, but a mock wallet provider for development purposes. It simulates the same functionality as a real wallet provider, but doesn't require users to create and integrate with real wallets. This is a great feature for developers, because it allows them to focus on making the game and designing their on-chain architecture in a purely sandbox environment. A production ready wallet provider will be released in a future version of the SDK as a priority feature. - -## How to use Dev Wallet - -Dev Wallet uses the accounts listed in the Accounts tab of the Flow Control Window. You can "authenticate" as one of these accounts, then sign transactions with that account. For more information on setting up these accounts, see [flow-control#flowcontrol-accounts](../../unity-sdk/guides/flow-control.md#flowcontrol-accounts). - -### Registering - -To use Dev Wallet, you must register it as follows: - -```csharp -FlowSDK.RegisterWalletProvider(ScriptableObject.CreateInstance()); -``` - -This should be done at the same time as initializing the Flow SDK. Here is a complete example of initializing the Flow SDK to use the emulator and registering Dev Wallet: - -```csharp -using DapperLabs.Flow.Sdk; -using DapperLabs.Flow.Sdk.DevWallet; - -FlowConfig flowConfig = new FlowConfig(); -flowConfig.NetworkUrl = FlowControl.Data.EmulatorSettings.emulatorEndpoint // local emulator -flowConfig.Protocol = FlowConfig.NetworkProtocol.HTTP; - -FlowSDK.Init(flowConfig); -FlowSDK.RegisterWalletProvider(ScriptableObject.CreateInstance()); -``` - -### Authenticating - -The `IWallet.Authenticate` method is as follows: - -```csharp -public void Authenticate(string username, System.Action OnAuthSuccess, System.Action OnAuthFailed); -``` - -`username` is a string that can be used to identify a user's wallet. For Dev Wallet, this corresponds to the `Name` field of an account in the Accounts tab of the Flow Control Window. If you know which account you want to authenticate with, supply the account name as this argument. If you pass in a blank string, the user will get a dialog where they can choose which account to authenticate as. As this is a development tool, there are no passwords - it is simply simulating authentication. -`OnAuthSuccess` is a function that will be called when you have successfully authenticated. The callback function must take a `string` argument, which will contain the authenticated account's Flow address. -`OnAuthFailed` is a function that will be called if authentication failed. In Dev Wallet, this would only fail if you passed in a `username` that doesn't exist. - -Here is an example of authenticating as the `user1` account from game code: - -```csharp -FlowSDK.GetWalletProvider().Authenticate("user1", (string flowAddress) => -{ - Debug.Log($"Authenticated - Flow account address is {flowAddress}"); -}, () => -{ - Debug.Log("Authentication failed."); -}); -``` - -Here is an example of calling `Authenticate` to display a list of Dev Wallet accounts to choose from: - -```csharp -FlowSDK.GetWalletProvider().Authenticate("", (string flowAddress) => -{ - Debug.Log($"Authenticated - Flow account address is {flowAddress}"); -}, () => -{ - Debug.Log("Authentication failed."); -}); -``` - -### Signing Transactions - -If you are using the Flow SDK to sign transactions then you do not need to worry about this, as it is handled automatically. When you submit a transaction, the SDK will request Dev Wallet to sign the transaction as the authenticated user. A dialog will be displayed requesting the user to approve the transaction. - -For full disclosure, here are the methods on the `IWallet` interface to sign a transaction: - -```csharp -public Task SignTransactionPayload(FlowTransaction txn); - -public Task SignTransactionEnvelope(FlowTransaction txn); -``` - -In Flow, there are two parts of a transaction that can be signed - the Payload and the Authorization Envelope. The envelope must always be signed, and is the last thing to be signed by the Payer of the transaction fees. The Payload is only signed by the Proposer and\or the Authorizers IF they are not also the Payer (i.e. nobody signs the transaction twice). For more information see [transaction signing](../../../../build/basics/transactions.md#signing-a-transaction). - -The following is an example of how to call `SignTransactionPayload`, but as mentioned, this is automatically done by the SDK's `Transactions.Submit` function. It's an asynchronous so is therefore `await`ed, and returns the signature as a byte array. - -```csharp -byte[] signature = await FlowSDK.GetWalletProvider().SignTransactionPayload(txRequest); -``` - -### Unauthenticating - -You can unauthenticate from Dev Wallet by calling the following: - -```csharp -FlowSDK.GetWalletProvider().Unauthenticate(); -``` - -This will clear Dev Wallet's internal cache of who is authenticated. Note that trying to submit transactions without anyone authenticated will result in an error. diff --git a/docs/tools/clients/unity-sdk/guides/flow-control.md b/docs/tools/clients/unity-sdk/guides/flow-control.md deleted file mode 100644 index 2389a7abdc..0000000000 --- a/docs/tools/clients/unity-sdk/guides/flow-control.md +++ /dev/null @@ -1,115 +0,0 @@ -# FlowControl - -FlowControl is a set of Unity tools and functions designed to streamline development and interactions with the Flow blockchain. - -After installing the Unity Flow SDK, open the FlowControl Manager window by clicking on Windows->Flow->Flow Control. - -This will open the FlowControl Manager window. It will also add a Flow Control object to your scene and create a FlowControlData resource to store your data if they are not already present. You can have multiple FlowControlData resources to act as presets and switch between them as needed. - -## FlowControl Emulator Settings - -The first panel that displays will be your emulator settings. - -![FlowControl Emulator Settings](../media/FlowControlEmulator.png) - -**Installation Detection:** FlowControl will attempt to find the flow binary in your path. If it can't detect it, it will display a button with the label "Install" linking you to the install instructions. - -**Emulator Data Directory:** This field is required if you wish to control the emulator from FlowControl. Set this to the **directory** where you would like to store your persisted -emulator state (flowdb), and your initial emulator configuration (flow.json). This should typically be outside your project folder. The folder must exist. - -**Emulator endpoint:** If you want to connect to an emulator running on a different computer, such as when testing on mobile devices or several clients all connecting -to the same emulator instance, you can specify an emulator endpoint address here. - -**Run emulator in play mode?** This determines if the emulator should automatically start when you enter play mode. - -**Start Emulator** button: This will start the emulator in the background to allow you to interact with it in edit mode. - -**Stop Emulator** button: This will stop a running emulator process. - -**Clear Persistent Data** button: The Flow emulator maintains the state of the emulated blockchain in a database. This allows you to persist data between runs. If you would like to reset the emulated blockchain to its initial state, use this button. This will delete the flowdb directory located in the Emulator Data Directory. It will be recreated the next time you start the emulator. - -**Show Emulator Log** button: This opens the Emulator Output window. You can use this to verify that scripts you run against the emulator execute -properly and will display debug messages if something goes wrong. You can also open the log using Window->Flow->Emulator Output - -## Advanced Options - -Some options are not exposed in the Flow Control Manager, but can be changed directly in the FlowControlData asset. -![FlowControlData asset](../media/FlowControlDataAsset.png) - -**Flow Executable Path Override:** This allows you to select the flow binary you would like to use. This will override the auto detected flow binary. - -**Persist Data:** This allows you disable persisting the emulated blockchain to disk. If this is disabled, all changes you make to the emulated blockchain will be lost when the emulator stops or restarts. - -**Kill Other Flow:** When starting or restarting the emulator, any running flow processes will be terminated. If you wish to run your own emulator instance outside the control of Flow Control, uncheck this, as well as "Run Emulator in Play Mode" to ensure it doesn't get terminated. - -**Verbose:** This controls the verbosity of emulator output. Verbose output is usually desired, but if you would like non-verbose output for any reason, this allows you to control that setting. - -## FlowControl Accounts - -The Accounts panel allows you to store account information for easy access at runtime. -***Note: Adding accounts to this panel does not create accounts on the blockchain*** - -![FlowControl Accounts](../media/FlowControlAccounts.png) - -The "+" button at the top will add a blank account entry for you to fill in. The fields may differ depending on which Gateway is selected, but Gateway and Name are always required. - -The emulator_service_account account will be created/updated each time you start the emulator so that it will always be valid. - -If it is a Testnet account, a link to the Testnet Flowdiver page for that account will be provided. Clicking on it will open the page in your default browser. - -## FlowControl Text Replacement - -![FlowControl Text Replacement](../media/FlowControlTextReplacement.png) - -The Text Replacement panel allows you to manage string replacement inside your Cadence scripts. If you're switching between emulator and Testnet, this makes it easy to replace account addresses and other strings without having to edit the scripts each time. - -All instances of Original Text will be replaced with Replacement Text. There is no required format, but it is recommended that you enclose the placeholder text in a separator character to ensure that there are no conflicting replacements. - -The Description field is optional and is just used to make it easy to remember what each replacement is for. - -The Active checkbox indicates if this text replacement should be performed. - -"Apply to accounts" allows you to select specific accounts this replacement should apply to. Any transactions or script executions performed via these accounts will have this replacement applied. - -"Apply to gateways" allows you to select specific gateways this replacement should apply to. Any transactions or script executions performed via an account utilizing these gateways will have this replacement performed. - -Accounts must meet both the account and gateway requirements for a text replacement to take place. If an account is listed in the accounts list, but uses a gateway that is not in the gateways list, the replacement will not be processed and vice-versa. - -## FlowControl Tools - -The FlowControl Tools panel can be used to deploy contracts and create accounts. - -![FlowControl Tools](../media/FlowControlTools.png) - -### Manage Contracts - -The Contract Name is the name of the contract as it will appear on the blockchain. It must match the contract name in the contract file. Text replacements are done on the contract file before deploying. - -Contract is the cadence contract file that you want to deploy. - -Account is the account that should deploy the contract. The contract will be deployed to the same network that the account gateway uses. - -The Deploy Contract button will attempt to deploy the contract. Check the emulator logs or flowdiver.io to monitor the progress of deployment and check for errors. - -The Update Contract button will attempt to update an existing contract. There are rules as to what can change in a contract for an update to be accepted. See [cadence/language/contract-updatability](https://cadence-lang.org/docs/language/contract-updatability) for more information. - -The Remove Contract button will attempt to delete the contract with the given name from the specified account. Once a contract is deleted, a new contract with the same name can not be created on the same account. When using the emulator, clearing the database will allow you to recreate the contract. When using testnet or mainnet, that contract name will become unavailable for the account. This is done to prevent bypassing the contract update rules by deleting and recreating a contract with incompatible changes. - - -### Transactions - -This section will allow you to execute a transaction against the gateway associated with the Signer account. Currently it does not allow you to pass arguments to the transaction, but you can use a Text Replacement to work around this. - - -### Create New Account - -The Paying account is the account that will initially fund the new account. The new account name is not stored on the blockchain, and is only used to identify the new account in the Accounts panel. After the account is created, the account information will be added to the Accounts panel. Make sure to save a copy of this information somewhere. If the private key is lost, that account will not be accessible. You can change the account Name in the Accounts panel. Note that changing the Address or Private Key fields will not change these on the blockchain. - - -## FlowControl inspector -![FlowControl Inspector](../media/FlowControlInspector.png) - -Clicking the FlowControl object in your scene will give you access to the inspector. This will allow you to select which FlowControlData resource you would like to use -as well as export to/import from JSON. JSON is useful if you would like to modify FlowControl variables from a built project, since you will not be able to save the -FlowControlData resource from a built project. The Log JSON button will serialize the FlowControlData resource into a string and display it. The Load JSON button will -deserialize the displayed JSON into the selected FlowControlData resource. diff --git a/docs/tools/clients/unity-sdk/guides/ios-support.md b/docs/tools/clients/unity-sdk/guides/ios-support.md deleted file mode 100644 index c3972f741b..0000000000 --- a/docs/tools/clients/unity-sdk/guides/ios-support.md +++ /dev/null @@ -1,28 +0,0 @@ -# iOS Support - -To use the Flow SDK in iOS projects, there are a couple more settings you must configure in your Unity project. - -## Provisioning - -This is required for all iOS projects, so anyone with experience setting up iOS projects in Unity should be familiar with it. If you are new to developing for iOS on Unity, please follow [Unity's documentation on how to set up a project for iOS](https://docs.unity3d.com/Manual/iphone-GettingStarted.html). - -With iOS selected as the active platform, open the Player Settings (Edit - Project Settings - Player). Scroll down to **Identification** and enter your provisioning details. The fastest way to get up and running is to enter your **Signing Team ID** and check **Automatically Sign**. For a description of all these fields, please refer to [Unity's documentation](https://docs.unity3d.com/Manual/class-PlayerSettingsiOS.html#Identification). - -## IL2CPP Code Generation setting - -If your version of Unity is older than 2022.1 (keeping in mind the minimum supported version is 2021.3) you will need to change the following setting: - -1. Open Build Settings (under File). -2. If iOS is not already the active platform, select iOS and click **Switch Platform**. -3. Change **IL2CPP Code Generation** to **Faster (smaller) builds**. - -The reason this must be changed is because the Flow SDK utilises generic sharing of value types. For a detailed description on this problem and how it has been fixed in 2022.1, please read this [Unity blog post](https://blog.unity.com/engine-platform/il2cpp-full-generic-sharing-in-unity-2022-1-beta). - -## Managed Stripping Level - -Similar to the previous setting, sometimes automatic code stripping on iOS can strip out functions that the optimizer thinks aren't needed, but they actually are. We highly recommend you change this setting to avoid any of these issues. - -1. Open Project Settings (under Edit). -2. Go to the Player tab. -3. Expand Other Settings and scroll down to Optimization. -4. Change **Managed Stripping Level** to **Minimal**. \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/guides/wallet-connect.md b/docs/tools/clients/unity-sdk/guides/wallet-connect.md deleted file mode 100644 index c017608a21..0000000000 --- a/docs/tools/clients/unity-sdk/guides/wallet-connect.md +++ /dev/null @@ -1,128 +0,0 @@ -# Wallet Connect - -## Introduction - -Wallet Connect is a production ready wallet provider which implements the `IWallet` interface. It's not an actual wallet itself - it's a bridge that connects your Unity game with Flow Wallets that support Wallet Connect. - -## What are Wallets? - -A wallet is a piece of software or hardware that stores the private key associated with a Flow account. The term *custodian* is used to refer to the party that stores the private key. Hardware Wallets (eg Ledger), typically USB devices, allow users to be their own custodian, whereas hosted software wallets (eg Dapper Wallet) act as the custodian on behalf of the user. - -## What is a Wallet Provider? - -In terms of the Flow SDK for Unity, a Wallet Provider is a class which implements the `IWallet` interface and allows users to interact with specific hardware or software wallets. This includes authenticating with a wallet, retrieving the user's Flow account address from the wallet, and requesting the wallet to sign transactions on behalf of the user. - -As of v2.0.0, the Flow SDK for Unity contains two wallet providers - [Dev Wallet](./dev-wallet.md) and Wallet Connect. Dev Wallet is a mock wallet provider to make development easier, while Wallet Connect connects to real wallets and is therefore used for production. You could also implement your own wallet provider by implementing the `IWallet` interface. - -## How to implement Wallet Connect - -To implement Wallet Connect, you must first register your project in the Wallet Connect dashboard to obtain a Project ID, then register the provider with the Flow SDK. - -### Obtain Project ID - -1. Go to [https://cloud.walletconnect.com/sign-in](https://cloud.walletconnect.com/sign-in) and sign in, or create an account if you don't have one. -2. Click on New Project and provide a name. -3. Copy the Project ID. - -### Registering - -Create an instance of WalletConnectProvider and initialize it with the required config, then register it with the Flow SDK. Here is an example: - -```csharp -using DapperLabs.Flow.Sdk; -using DapperLabs.Flow.Sdk.WalletConnect; -using DapperLabs.Flow.Sdk.Crypto; - -IWallet walletProvider = ScriptableObject.CreateInstance(); -walletProvider.Init(new WalletConnectConfig -{ - ProjectId = "xxxxxxxxxxxxxxxxxxxxx", // the Project ID from the previous step - ProjectDescription = "An example project to showcase Wallet Connect", // a description for your project - ProjectIconUrl = "https://walletconnect.com/meta/favicon.ico", // URL for an icon for your project - ProjectName = "Dapper Unity Example", // the name of your project - ProjectUrl = "https://dapperlabs.com" // URL for your project -}); -FlowSDK.RegisterWalletProvider(walletProvider); -``` - -The description, icon, name and URL that you provide will appear in your user's wallet apps, when they connect the game to their wallet. - -### Authenticating - -The `IWallet.Authenticate` method is as follows: - -```csharp -public void Authenticate(string username, System.Action OnAuthSuccess, System.Action OnAuthFailed); -``` - -`username` is ignored in the Wallet Connect provider. -`OnAuthSuccess` is a function that will be called when you have successfully authenticated with a wallet. The callback function must take a `string` argument, which will contain the authenticated account's Flow address. -`OnAuthFailed` is a function that will be called if authentication failed, for example if the user rejected the request in their wallet app. - -Here is an example of authenticating from game code: - -```csharp -FlowSDK.GetWalletProvider().Authenticate("", (string flowAddress) => -{ - Debug.Log($"Authenticated - Flow account address is {flowAddress}"); -}, () => -{ - Debug.Log("Authentication failed."); -}); -``` - -### Signing Transactions - -If you are using the Flow SDK to sign transactions then you do not need to worry about this, as it is handled automatically. When you submit a transaction, the SDK will request Wallet Connect to sign the transaction as the authenticated user. The user will receive a notification in their wallet app to approve the transaction. - -For full disclosure, here are the methods on the `IWallet` interface to sign a transaction: - -```csharp -public Task SignTransactionPayload(FlowTransaction txn); - -public Task SignTransactionEnvelope(FlowTransaction txn); -``` - -In Flow, there are two parts of a transaction that can be signed - the Payload and the Authorization Envelope. The envelope must always be signed, and is the last thing to be signed by the Payer of the transaction fees. The Payload is only signed by the Proposer and\or the Authorizers IF they are not also the Payer (i.e. nobody signs the transaction twice). For more information on transaction signing, see [transaction signing](../../../../build/basics/transactions.md#signing-a-transaction). - -The following is an example of how to call `SignTransactionEnvelope`, but as mentioned, this is automatically done by the SDK's `Transactions.Submit` function. It is asynchronous so is therefore `await`ed, and returns the signature as a byte array. - -```csharp -byte[] signature = await FlowSDK.GetWalletProvider().SignTransactionEnvelope(txRequest); -``` - -## What your users will see - -When your game calls `Authenticate`, a dialog box will appear. The contents of the dialog box will depend on what platform the user is on. - -### Desktop builds (Windows, MacOS) - -The authenticate dialog box will contain a QR code. The user must scan the QR code with their wallet app, after which their app will ask them to approve the connection with your game. Once they have approved the connection, they will receive any transaction requests submitted by the game in their wallet app. They can approve or reject these transaction requests. - -### Mobile builds (iOS, Android) - -The authenticate dialog box will contain a list of wallet apps that support Flow and Wallet Connect. If an app is installed on the device, it will say `Installed` next to the name. Selecting an app will either open the app if it's installed, or direct the user to download the app from the App Store (iOS) or Play Store (Android). If the app was installed and is opened, the user can approve the connection with your game. Once they have approved the connection, they will receive any transaction requests submitted by the game in their wallet app. They can approve or reject these transaction requests. - -## Customising the Authentication Dialogs - -You can customise the UI of both the QR Code (desktop builds) and Wallet Select (mobile builds) dialogs, allowing you to keep the same UI theme as the rest of your game. To do this, supply your custom prefabs to the Wallet Connect Config object during initialization, such as in the following example: - -```csharp -// Register WalletConnect -IWallet walletProvider = new WalletConnectProvider(); -walletProvider.Init(new WalletConnectConfig -{ - ProjectId = "xxxxxxxxxxxxxxxxxxxxx", - ProjectDescription = "An example project to showcase Wallet Connect", - ProjectIconUrl = "https://walletconnect.com/meta/favicon.ico", - ProjectName = "Dapper Unity Example", - ProjectUrl = "https://dapperlabs.com" - QrCodeDialogPrefab = qrCodeCustomPrefab, // custom prefab for QR Code dialog (desktop builds) - WalletSelectDialogPrefab = walletSelectCustomPrefab // custom prefab for Wallet Select dialog (mobile builds) -}); -FlowSDK.RegisterWalletProvider(walletProvider); -``` - -> **Note**: \ -Your custom QR Code prefab must have the `QRCodeDialog` script added as a component. \ -Your custom Wallet Select prefab must have the `WalletSelectDialog` script added as a component. diff --git a/docs/tools/clients/unity-sdk/index.md b/docs/tools/clients/unity-sdk/index.md deleted file mode 100644 index 014447fbd5..0000000000 --- a/docs/tools/clients/unity-sdk/index.md +++ /dev/null @@ -1,84 +0,0 @@ -# Flow SDK for Unity - -The Flow SDK for Unity allows Unity developers to integrate their games and applications with the Flow blockchain. - -## Introduction - -As games developers ourselves, we understand that most games developers don’t have any background in blockchain technology or even understand why it is used. The Flow SDK for Unity has been developed by games developers, for games developers. Our aim is to make blockchain integration as easy as possible, so that you can focus on making a great game. - -First, it helps to understand why you would want to make a game on blockchain, and what you can do. - -## Why Blockchain? - -Here at Dapper Labs we’re big believers in Web3. The two main advantages of blockchain that we believe in are: - -**Trustlessness** – there is no trust required in any particular platform or service. Once something is on the blockchain, it’s there to stay. Users have full confidence that the data you put on-chain will always be available – there are no servers or databases that can be taken down. - -**Composability** – once your Smart Contracts have been deployed, other developers can utilize them. They can deploy additional Smart Contracts that leverage your own, or develop other client applications which use them. Creating composable pieces of software like this gives new experiences to existing audiences – it's a win-win for everyone. - -## So blockchains are awesome... why Flow? - -Flow is a fast, decentralized and developer-friendly layer-1 blockchain. It was created to solve problems that Dapper Labs encountered while scaling CryptoKitties on Ethereum. Flow differs from other blockchains because it was explicitly designed to support games and consumer applications on day one, with the throughput necessary to scale to millions of active users. Some points which are unique to Flow include: - -- Multi-node architecture which facilitates scalability -- Very low transaction fees -- Eco-friendly due to its proof-of-stake consensus and multi-node architecture -- Cadence – Flow's Smart Contract language, a resource-oriented programming language which has in-built security features -- Upgradable Smart Contracts - -## Overview of the Flow SDK - -The Flow SDK allows Unity games to read and write from\\to a Flow blockchain. There are three chains\\environments\\networks developers should be aware of: - -- Emulator – this is an executable you can run locally, or on your own network, which emulates a Flow blockchain. -- Testnet – the publicly available Flow blockchain used for testing. -- Mainnet – the publicly available Flow blockchain used for production. - -We highly recommend you do most of your development against a local emulator, and only deploy to Testnet when your Smart Contracts are finalised. While Flow does have “upgradable Smart Contracts”, there are many caveats to this, and you should get into the mindset that Smart Contracts are completely immutable once deployed. On an emulator, you can simply erase the entire chain and start again. - -The types of things you can do with the Flow blockchain include: - -- Mint, burn and trade NFTs and Fungible Tokens -- Store game data -- Read any publicly available information -- Run game logic - -It is completely up to you, the developer, to decide how much data\\logic you want to store\\run on-chain, and how much (if any) on off-chain architecture such as servers and databases. We encourage you to experiment on an emulator, and when you’re ready, test on Testnet to get an indication of performance. - -## Adding the Flow SDK - -To add the Flow SDK for Unity, go to it in the Asset Store and add it to My Assets: [https://assetstore.unity.com/packages/decentralization/flow-sdk-237172](https://assetstore.unity.com/packages/decentralization/flow-sdk-237172) - -Once you've added it to your account, follow these instructions to import it into your project: [https://docs.unity3d.com/Manual/upm-ui-import.html](https://docs.unity3d.com/Manual/upm-ui-import.html) - -Alternatively, the Flow SDK for Unity is hosted on github here: [https://github.com/onflow/UnityFlowSDK](https://github.com/onflow/UnityFlowSDK) - -To add it to your Unity project directly from the github repo: - -1. In the Unity Editor, go to Window, Package Manager. -2. Click the + dropdown in the top left corner. -4. Select "Add package from git URL" and enter `https://github.com/onflow/UnityFlowSDK.git`. - -## Updating the Flow SDK - -Check the Package Manager regularly for updates to the Flow SDK. - -1. In the Unity Editor, go to Window, Package Manager. -2. Under Packages - Dapper Labs, select Flow SDK. -3. Click Update. If there is no Update button then you are already up to date. - -## Samples - -To add any of the SDKs Samples, click the related import button, from the Flow SDK package panel, in the Unity Package Manager window. - -## Requirements - -The Flow SDK is compatible with Unity version 2021.3 or higher. - -Supported Platforms: Windows, OSX, Android - -## Support - -Email: unity-sdk-support@dapperlabs.com - -Discord: https://discord.gg/flow diff --git a/docs/tools/clients/unity-sdk/license.md b/docs/tools/clients/unity-sdk/license.md deleted file mode 100644 index 99ca02f7a7..0000000000 --- a/docs/tools/clients/unity-sdk/license.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: License -sidebar_position: 4 ---- - -MIT License - -Copyright 2023 Dapper Labs - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6039.png b/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6039.png deleted file mode 100644 index 91f241a615..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6039.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6040.png b/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6040.png deleted file mode 100644 index 429035c13d..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6040.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6041.png b/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6041.png deleted file mode 100644 index 318c7dc0f9..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/079fee6ed2cb0777c5ee99322d3d6041.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/0933fe23c63eedcd9ba2c4f3aac2878c.png b/docs/tools/clients/unity-sdk/media/0933fe23c63eedcd9ba2c4f3aac2878c.png deleted file mode 100644 index 0a46df5faa..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/0933fe23c63eedcd9ba2c4f3aac2878c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/0bc22650b47abb1cf606b2c7381a6377.png b/docs/tools/clients/unity-sdk/media/0bc22650b47abb1cf606b2c7381a6377.png deleted file mode 100644 index dfad1b07ec..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/0bc22650b47abb1cf606b2c7381a6377.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/15840cee138659b172d6023791b73155.png b/docs/tools/clients/unity-sdk/media/15840cee138659b172d6023791b73155.png deleted file mode 100644 index b78e2d718f..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/15840cee138659b172d6023791b73155.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/184e8a2ee8440b6b700072a9212e92bb.png b/docs/tools/clients/unity-sdk/media/184e8a2ee8440b6b700072a9212e92bb.png deleted file mode 100644 index e60c288f2c..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/184e8a2ee8440b6b700072a9212e92bb.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/2466ac711f979da15f6ad5a6cdb6421a.png b/docs/tools/clients/unity-sdk/media/2466ac711f979da15f6ad5a6cdb6421a.png deleted file mode 100644 index 99079bcc7e..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/2466ac711f979da15f6ad5a6cdb6421a.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/252129f67efc7d03357d9e5070d67ece.png b/docs/tools/clients/unity-sdk/media/252129f67efc7d03357d9e5070d67ece.png deleted file mode 100644 index d5fd50f104..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/252129f67efc7d03357d9e5070d67ece.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/270ed3d6ac5df815d35cc82f588a4a18.png b/docs/tools/clients/unity-sdk/media/270ed3d6ac5df815d35cc82f588a4a18.png deleted file mode 100644 index f516dfbfa0..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/270ed3d6ac5df815d35cc82f588a4a18.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/2ce641e868905901df937c4dd09de105.png b/docs/tools/clients/unity-sdk/media/2ce641e868905901df937c4dd09de105.png deleted file mode 100644 index 85ad600a72..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/2ce641e868905901df937c4dd09de105.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/2e08cbac6f2ff6a2cde89e7cd6c6be62.png b/docs/tools/clients/unity-sdk/media/2e08cbac6f2ff6a2cde89e7cd6c6be62.png deleted file mode 100644 index 6fb49cc012..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/2e08cbac6f2ff6a2cde89e7cd6c6be62.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/3421ca4434716e8f2efc89bbf5f3c7ad.png b/docs/tools/clients/unity-sdk/media/3421ca4434716e8f2efc89bbf5f3c7ad.png deleted file mode 100644 index 7c85a41037..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/3421ca4434716e8f2efc89bbf5f3c7ad.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/35f927dd8c61a096c387264c358eba6a.png b/docs/tools/clients/unity-sdk/media/35f927dd8c61a096c387264c358eba6a.png deleted file mode 100644 index 0d8c777b06..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/35f927dd8c61a096c387264c358eba6a.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/4444607a3effafd2c5bb8430b8fade3f.png b/docs/tools/clients/unity-sdk/media/4444607a3effafd2c5bb8430b8fade3f.png deleted file mode 100644 index e87e7ed378..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/4444607a3effafd2c5bb8430b8fade3f.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/4eaf391ee18bc8b70452e4495b5b317a.png b/docs/tools/clients/unity-sdk/media/4eaf391ee18bc8b70452e4495b5b317a.png deleted file mode 100644 index e10342b6b5..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/4eaf391ee18bc8b70452e4495b5b317a.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/51d4f1e5f01dc36b0fdd0853eb08b0bd.png b/docs/tools/clients/unity-sdk/media/51d4f1e5f01dc36b0fdd0853eb08b0bd.png deleted file mode 100644 index 978a72cc50..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/51d4f1e5f01dc36b0fdd0853eb08b0bd.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/54f8a5235b61d70f09904c38ed3e5e5c.png b/docs/tools/clients/unity-sdk/media/54f8a5235b61d70f09904c38ed3e5e5c.png deleted file mode 100644 index 8a2579c91e..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/54f8a5235b61d70f09904c38ed3e5e5c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/5569416d261ff8e9d05b53c082c8a1b1.png b/docs/tools/clients/unity-sdk/media/5569416d261ff8e9d05b53c082c8a1b1.png deleted file mode 100644 index da39d30c54..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/5569416d261ff8e9d05b53c082c8a1b1.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/55bad7978ba3915550c8367b319a9944.png b/docs/tools/clients/unity-sdk/media/55bad7978ba3915550c8367b319a9944.png deleted file mode 100644 index a64d81cede..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/55bad7978ba3915550c8367b319a9944.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/58d03fbcda193c672c0287da2f3f04ba.png b/docs/tools/clients/unity-sdk/media/58d03fbcda193c672c0287da2f3f04ba.png deleted file mode 100644 index c7970487b6..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/58d03fbcda193c672c0287da2f3f04ba.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/5ba73beddcefba8eade5c9ce4322a86d.png b/docs/tools/clients/unity-sdk/media/5ba73beddcefba8eade5c9ce4322a86d.png deleted file mode 100644 index a05b117a7d..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/5ba73beddcefba8eade5c9ce4322a86d.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/61267a392954311426b4144345f25dfd.png b/docs/tools/clients/unity-sdk/media/61267a392954311426b4144345f25dfd.png deleted file mode 100644 index b97cdf2a2f..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/61267a392954311426b4144345f25dfd.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/6279d33bd3d9eea312ef2f9b05000dbf.png b/docs/tools/clients/unity-sdk/media/6279d33bd3d9eea312ef2f9b05000dbf.png deleted file mode 100644 index 41a57f0920..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/6279d33bd3d9eea312ef2f9b05000dbf.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/62ec9ee52b31041e7420ac7ad7132684.png b/docs/tools/clients/unity-sdk/media/62ec9ee52b31041e7420ac7ad7132684.png deleted file mode 100644 index d8cbfacee6..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/62ec9ee52b31041e7420ac7ad7132684.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/6d1ca6e13da7d1c65496fd803a3c5f78.png b/docs/tools/clients/unity-sdk/media/6d1ca6e13da7d1c65496fd803a3c5f78.png deleted file mode 100644 index c17adc6cac..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/6d1ca6e13da7d1c65496fd803a3c5f78.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/6d33d20949b4923d1e805e8e21884935.png b/docs/tools/clients/unity-sdk/media/6d33d20949b4923d1e805e8e21884935.png deleted file mode 100644 index 8a43e38f78..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/6d33d20949b4923d1e805e8e21884935.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/741a3e12d817f800c2bced3c927912ba.png b/docs/tools/clients/unity-sdk/media/741a3e12d817f800c2bced3c927912ba.png deleted file mode 100644 index 594db4701c..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/741a3e12d817f800c2bced3c927912ba.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/7e85df9713fbb484d0955d698ccd1668.png b/docs/tools/clients/unity-sdk/media/7e85df9713fbb484d0955d698ccd1668.png deleted file mode 100644 index 7bfab1aec2..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/7e85df9713fbb484d0955d698ccd1668.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/814d81d58a609d94bc2d7bc9a6e90afe.png b/docs/tools/clients/unity-sdk/media/814d81d58a609d94bc2d7bc9a6e90afe.png deleted file mode 100644 index 076b06be17..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/814d81d58a609d94bc2d7bc9a6e90afe.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/82ea27a997c12eab231ad8bf2e49d335.png b/docs/tools/clients/unity-sdk/media/82ea27a997c12eab231ad8bf2e49d335.png deleted file mode 100644 index 35e517c6c4..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/82ea27a997c12eab231ad8bf2e49d335.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/84ffa7104bc4de009c328960c1c9c6e7.png b/docs/tools/clients/unity-sdk/media/84ffa7104bc4de009c328960c1c9c6e7.png deleted file mode 100644 index dba674dc41..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/84ffa7104bc4de009c328960c1c9c6e7.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/881d1efa0c8e4ff9bffe3382274a255c.png b/docs/tools/clients/unity-sdk/media/881d1efa0c8e4ff9bffe3382274a255c.png deleted file mode 100644 index 7b86d6d77a..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/881d1efa0c8e4ff9bffe3382274a255c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/894e40fa8ac7a3ebbc60e44c60be135a.png b/docs/tools/clients/unity-sdk/media/894e40fa8ac7a3ebbc60e44c60be135a.png deleted file mode 100644 index 7d6eb37db9..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/894e40fa8ac7a3ebbc60e44c60be135a.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/8cd5f4e2d4ac66693e0c2a6f796d1b38.png b/docs/tools/clients/unity-sdk/media/8cd5f4e2d4ac66693e0c2a6f796d1b38.png deleted file mode 100644 index bc3f78265f..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/8cd5f4e2d4ac66693e0c2a6f796d1b38.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766c.png b/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766c.png deleted file mode 100644 index 542df16076..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766d.png b/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766d.png deleted file mode 100644 index 26268c1944..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/999cca57ee98b89bfe5f012ee445766d.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/AppendixDeployContract.png b/docs/tools/clients/unity-sdk/media/AppendixDeployContract.png deleted file mode 100644 index 0095d61632..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/AppendixDeployContract.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/AppendixNewAccount.png b/docs/tools/clients/unity-sdk/media/AppendixNewAccount.png deleted file mode 100644 index 179e1fc0da..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/AppendixNewAccount.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/AppendixTextReplacement.png b/docs/tools/clients/unity-sdk/media/AppendixTextReplacement.png deleted file mode 100644 index 7929157caa..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/AppendixTextReplacement.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlAccounts.png b/docs/tools/clients/unity-sdk/media/FlowControlAccounts.png deleted file mode 100644 index eb393676ac..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlAccounts.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlDataAsset.png b/docs/tools/clients/unity-sdk/media/FlowControlDataAsset.png deleted file mode 100644 index 881a0dfcec..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlDataAsset.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlEmulator.png b/docs/tools/clients/unity-sdk/media/FlowControlEmulator.png deleted file mode 100644 index ea76b99074..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlEmulator.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlInspector.png b/docs/tools/clients/unity-sdk/media/FlowControlInspector.png deleted file mode 100644 index 5ce9a5d081..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlInspector.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlTextReplacement.png b/docs/tools/clients/unity-sdk/media/FlowControlTextReplacement.png deleted file mode 100644 index 218dc7d40c..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlTextReplacement.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/FlowControlTools.png b/docs/tools/clients/unity-sdk/media/FlowControlTools.png deleted file mode 100644 index 073e87cdfa..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/FlowControlTools.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/Lilico_Settings.jpg b/docs/tools/clients/unity-sdk/media/Lilico_Settings.jpg deleted file mode 100644 index 9cdad3e6d4..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/Lilico_Settings.jpg and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/Lilico_TestnetEnable.jpg b/docs/tools/clients/unity-sdk/media/Lilico_TestnetEnable.jpg deleted file mode 100644 index cc34602410..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/Lilico_TestnetEnable.jpg and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/aeda525491f7b1b329e57ac7d07fa997.png b/docs/tools/clients/unity-sdk/media/aeda525491f7b1b329e57ac7d07fa997.png deleted file mode 100644 index 79b00a7689..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/aeda525491f7b1b329e57ac7d07fa997.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/b33bd58a6b1085948b1fbc9b40b2f2b3.png b/docs/tools/clients/unity-sdk/media/b33bd58a6b1085948b1fbc9b40b2f2b3.png deleted file mode 100644 index 0727939486..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/b33bd58a6b1085948b1fbc9b40b2f2b3.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/bac9d81383b8aeacab6e2f757f6c5f1c.png b/docs/tools/clients/unity-sdk/media/bac9d81383b8aeacab6e2f757f6c5f1c.png deleted file mode 100644 index 001ff814c9..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/bac9d81383b8aeacab6e2f757f6c5f1c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/bd9262a67638aa9c519921629e8c9075.png b/docs/tools/clients/unity-sdk/media/bd9262a67638aa9c519921629e8c9075.png deleted file mode 100644 index afa7f72039..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/bd9262a67638aa9c519921629e8c9075.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/bd9b6a0a7261d3c605aad9760a304220.png b/docs/tools/clients/unity-sdk/media/bd9b6a0a7261d3c605aad9760a304220.png deleted file mode 100644 index 5fbf304deb..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/bd9b6a0a7261d3c605aad9760a304220.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/be2bc6569ca5a475b3c147cf34f07c1a.png b/docs/tools/clients/unity-sdk/media/be2bc6569ca5a475b3c147cf34f07c1a.png deleted file mode 100644 index 9c0b7e2702..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/be2bc6569ca5a475b3c147cf34f07c1a.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/c680c8f205102d2dd4254e0a5a4664b0.png b/docs/tools/clients/unity-sdk/media/c680c8f205102d2dd4254e0a5a4664b0.png deleted file mode 100644 index 77a365a70c..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/c680c8f205102d2dd4254e0a5a4664b0.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/cc91513990fc19f6348a32ba3770c543.png b/docs/tools/clients/unity-sdk/media/cc91513990fc19f6348a32ba3770c543.png deleted file mode 100644 index f96e553926..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/cc91513990fc19f6348a32ba3770c543.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/d4ef2f464230b54400ff46b92f2eb01e.png b/docs/tools/clients/unity-sdk/media/d4ef2f464230b54400ff46b92f2eb01e.png deleted file mode 100644 index bc390b442c..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/d4ef2f464230b54400ff46b92f2eb01e.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/db2741c083bc677b767e5f2973f63e1b.png b/docs/tools/clients/unity-sdk/media/db2741c083bc677b767e5f2973f63e1b.png deleted file mode 100644 index a2e9a41edf..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/db2741c083bc677b767e5f2973f63e1b.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/e6a3c62a95440af5c38b47c9efa27a72.png b/docs/tools/clients/unity-sdk/media/e6a3c62a95440af5c38b47c9efa27a72.png deleted file mode 100644 index 9bbfae2b07..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/e6a3c62a95440af5c38b47c9efa27a72.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/e81ec10f19a1f9d51f1ffaa102115244.png b/docs/tools/clients/unity-sdk/media/e81ec10f19a1f9d51f1ffaa102115244.png deleted file mode 100644 index 5ac70245e6..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/e81ec10f19a1f9d51f1ffaa102115244.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/e96f948858a786b5e26fa08cbcc2c260.png b/docs/tools/clients/unity-sdk/media/e96f948858a786b5e26fa08cbcc2c260.png deleted file mode 100644 index 3c51e693f0..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/e96f948858a786b5e26fa08cbcc2c260.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/eb0fba4dbd9a040424afb3182e5ce345.png b/docs/tools/clients/unity-sdk/media/eb0fba4dbd9a040424afb3182e5ce345.png deleted file mode 100644 index 20cfd357ef..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/eb0fba4dbd9a040424afb3182e5ce345.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/ecac86c8af7414ac22e95f64340acc44.png b/docs/tools/clients/unity-sdk/media/ecac86c8af7414ac22e95f64340acc44.png deleted file mode 100644 index 29d7d4e2bc..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/ecac86c8af7414ac22e95f64340acc44.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/ed8abd462a98bd71a9abe6a66c04d958.png b/docs/tools/clients/unity-sdk/media/ed8abd462a98bd71a9abe6a66c04d958.png deleted file mode 100644 index 0fe1eadf07..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/ed8abd462a98bd71a9abe6a66c04d958.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/f0dfb75d21cf7462d7f0aaf199d97b27.png b/docs/tools/clients/unity-sdk/media/f0dfb75d21cf7462d7f0aaf199d97b27.png deleted file mode 100644 index e8d0f4d010..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/f0dfb75d21cf7462d7f0aaf199d97b27.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/f30282a8889db37965bcd49b209ac614.png b/docs/tools/clients/unity-sdk/media/f30282a8889db37965bcd49b209ac614.png deleted file mode 100644 index 10bc679e4d..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/f30282a8889db37965bcd49b209ac614.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/f6a8324547c8ec499a2eb68248091ba3.png b/docs/tools/clients/unity-sdk/media/f6a8324547c8ec499a2eb68248091ba3.png deleted file mode 100644 index 4c09790bd9..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/f6a8324547c8ec499a2eb68248091ba3.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/f8bf765df950289fd3d1b7dc8e73d545.png b/docs/tools/clients/unity-sdk/media/f8bf765df950289fd3d1b7dc8e73d545.png deleted file mode 100644 index 4a73f6228e..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/f8bf765df950289fd3d1b7dc8e73d545.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/fd52a768f2367b763980b22d85479305.png b/docs/tools/clients/unity-sdk/media/fd52a768f2367b763980b22d85479305.png deleted file mode 100644 index 1cef1b26fb..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/fd52a768f2367b763980b22d85479305.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/media/fe0ccbbb8fb583dd7c461a1436d9206c.png b/docs/tools/clients/unity-sdk/media/fe0ccbbb8fb583dd7c461a1436d9206c.png deleted file mode 100644 index 0609be63a5..0000000000 Binary files a/docs/tools/clients/unity-sdk/media/fe0ccbbb8fb583dd7c461a1436d9206c.png and /dev/null differ diff --git a/docs/tools/clients/unity-sdk/samples/_category_.yml b/docs/tools/clients/unity-sdk/samples/_category_.yml deleted file mode 100644 index 1d2ab7fc93..0000000000 --- a/docs/tools/clients/unity-sdk/samples/_category_.yml +++ /dev/null @@ -1,2 +0,0 @@ -label: Samples -position: 3 \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/samples/flow-words-sample.md b/docs/tools/clients/unity-sdk/samples/flow-words-sample.md deleted file mode 100644 index e4dbfd01b4..0000000000 --- a/docs/tools/clients/unity-sdk/samples/flow-words-sample.md +++ /dev/null @@ -1,30 +0,0 @@ -# Flow Unity SDK - FlowWords Sample - -This sample is a working pre-deployed version of our FlowWords tutorial sample, running on FLOW Testnet.\ -To use this sample you will need a WalletConnect compatible Testnet wallet. e.g. Lilico. - -If you do not already have a compatible wallet, you can sign up for one here: [https://lilico.app](https://lilico.app)\ -You will need to install the mobile phone app. (iOS or Android) - -Once installed, you will need to enable dev mode in the Lilico app, do so as follows; -1. Go to the settings menu, by clicking the profile icon on the bottom right corner of the screen. -2. Select Developer Mode to enter the Developer mode submenu - - ![Lilico Settings](../media/Lilico_Settings.jpg) - -3. Enable Developer Mode with the toggle at the top of the screen, and then set the network to TestNet. - - ![Lilico Enable Devmode](../media/Lilico_TestnetEnable.jpg) - -4. Back out to the wallet dashboard by clicking the back button at the top left of the screen, then clicking the Dashboard button (small $) in the bottom left corner of the screen. -5. At this point your wallet should say TESTNET at the top of the screen. If your FLOW balance is zero, you can fund your Testnet account via the FLOW Testnet Faucet here: [https://testnet-faucet.onflow.org/fund-account](https://testnet-faucet.onflow.org/fund-account) - 1. Enter your Testnet account address, as shown in the Lilico app - 2. Complete the captcha, and click 'Fund Your Account' - 3. Refresh your Lilico app on your device to see your new FLOW balance! (swipe down on the dashboard screen) - -We assume you have already added the SDK package to your Unity project, and Imported the FlowWords Sample using the Unity Package Manager. -If you have not already done so, do this now. You can find instructions on how to do so [here.](../index.md#adding-the-flow-sdk) - -1. Open Game.scene from the FlowWords sample folder. (Samples\Flow SDK\\<version>\Flow Words\Scenes) -2. Press Play in Unity to run the game sample. -3. Whenever the game presents you with a QR code, scan the code with your mobile Wallet App, and approve the transactions on your mobile device! \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/samples/flow-words-tutorial.md b/docs/tools/clients/unity-sdk/samples/flow-words-tutorial.md deleted file mode 100644 index 4381369967..0000000000 --- a/docs/tools/clients/unity-sdk/samples/flow-words-tutorial.md +++ /dev/null @@ -1,1192 +0,0 @@ -# Tutorial - How to Build FlowWords - -## Outline - -The FlowWords tutorial example project is the complete FlowWords game, with the FlowSDK and any SDK related code removed. - -In this tutorial, you will learn how to; - -1. Configure the FlowSDK and local emulator for use with Unity -2. Deploy contracts, create Accounts and run Transactions on the Flow emulator from within Unity. -3. Incorporate Flow Accounts, Transactions and Scripts into Unity projects using Code - -## Requirements - -FlowWords Tutorial has been tested on Unity version 2021.3.6f1 - -This tutorial assumes you have created a blank project, and installed the FlowSDK package from the Unity Asset Store, and FlowWords Tutorial Sample. - -## Step 1 – Configure the FlowSDK and local emulator - -1. Open the scene at Assets\\Samples\\Flow SDK\\<version>\\Flow Words Tutorial Assets\\Scenes\\Game.unity - - ![Open Game scene example](../media/bac9d81383b8aeacab6e2f757f6c5f1c.png) - -2. Open the Flow Control Manager window, at Window-\>Flow-\>Flow Control. - This will create a Flow Control GameObject in the Game scene, and a FlowControlData asset in the Resources folder if they do not already exist. - - ![Open Flow Control Manager example](../media/f8bf765df950289fd3d1b7dc8e73d545.png) - -3. Go to the Emulator Settings tab. If the manager states the Flow Executable cannot be found, use the Install button provided and follow the instructions to install the Flow CLI. - Note: If Unity still does not detect the emulator after installing the Flow CLI, you may need to restart your computer for the install to take effect. -4. Set a directory for our Emulator data to reside in. This is where the emulator stores the state of the emulated Flow blockchain. - For the purposes of this tutorial, we will create a new folder called FlowEmulator inside our project folder. - - ![Set Emulator data directory example](../media/4eaf391ee18bc8b70452e4495b5b317a.png) - -5. Click Start Emulator to start the emulator and create the emulator_service_account, and ensure “Run emulator in play mode” is checked. - - ![Start Emulator example](../media/4444607a3effafd2c5bb8430b8fade3f.png) - - NOTE: If this is the first time you have run the emulator on Windows, you may be presented with a dialog to allow access through your firewall. This is safe to allow. - -6. Go to the Accounts tab and verify that the service account has been created. - This is the account we will use to create more accounts, and deploy our game contract on to the emulator. - - ![Flow Control Manager Accounts final state](../media/6279d33bd3d9eea312ef2f9b05000dbf.png) - -7. Back on the Emulator Settings tab, you can click Show Emulator Log, to view the output. - - ![Flow Emulator Output example](../media/d4ef2f464230b54400ff46b92f2eb01e.png) - -## Step 2 – Deploy Contracts, create Accounts and run Transactions - -We have provided you with the FlowWords game contract, but before we can interact with the contract, we have to deploy it to the blockchain. - -We also have to set up some text replacements as, once deployed, our scripts will require hardcoded references to the contract name and deployed address. - -### Set up Text Replacements - -Text replacements allow us to set and update references across all of our local Cadence scripts, without having to update each file individually. - -1. Open the Flow Control Manager window, and navigate to the Text Replacements tab. -2. Set up the text replacements as follows. You can add text replacements by clicking the ‘+’ button at the top of the panel. - - ![Flow Control Manager Text Replacement final state](../media/bd9262a67638aa9c519921629e8c9075.png) - - The anatomy of a text replacement is as follows; - **Description**: A friendly description, has no bearing on functionality. - **Original Text**: The original text, in your script files, which you want to replace. - **Replacement Text**: The text that you wish to replace the original text with. This is what gets submitted to the chain. - **Active**: This checkbox enables or disables the text replacement. - **Apply to Accounts**: Select which, if any, accounts this text replacement should apply to. - **Apply to Gateways**: Select which, if any, gateways (such as Emulator, or TestNet) this replacement should apply to. - -### Create User Accounts - -While it would be perfectly possible to play our game with the emulator service account, we will often want to test our contracts with multiple different user accounts. - -To create a new user account; - -1. Open the Flow Control Manager window, and navigate to the Tools tab. -2. In the Create New Account section; - - ![Flow Control Manager new account creation example](../media/aeda525491f7b1b329e57ac7d07fa997.png) - - 1. Select the paying account. This will usually be the emulator_service_account - 2. Enter a friendly name for the new account. This name is just for your own reference and does not get written to the chain. - 3. Click Create -3. If successful, the new account will appear under the Flow Control Manager Accounts tab. - - ![Flow Control Manager new account example final state](../media/84ffa7104bc4de009c328960c1c9c6e7.png) - -### Deploy the Contract - -Before anyone can interact with a contract, it must be deployed to the blockchain. - -We are going to deploy our game contract to the emulator, for local testing. But for deploying to Testnet, or Mainnet follow the same process. - -1. Go to the Emulator Settings tab, in the Flow Control Manager, and Start the Emulator if it is not already running. -2. Go to the Tools tab. In the Manage Contracts section, enter the contract name of ‘FlowWords’ – this should match our CONTRACT_NAME text replacement precisely. -3. Populate the Contract field with game-contract.cdc, which can be found in Resources/contracts - - ![Resources folder example](../media/741a3e12d817f800c2bced3c927912ba.png) - -4. Finally, ensure that the Account field contains emulator_service_account, and click the Deploy Contract button. - - ![Flow Control Manager contract deployment example](../media/ed8abd462a98bd71a9abe6a66c04d958.png) - -5. To check if the contract has deployed successfully, open the Flow Emulator Output window. Successful deployment will look as follows; - - ![Flow Emulator Output example](../media/0933fe23c63eedcd9ba2c4f3aac2878c.png) - -### Submit Transactions - -For administration purposes, it is sometimes useful to be able to directly submit transactions to the chain. - -We can use a transaction to check out text replacements are set up correctly, and our game contract has successfully deployed. - -1. In Flow Control Manager, navigate to tools. -2. In the Transactions section; - - ![Flow Control Manager contract deployment example](../media/2ce641e868905901df937c4dd09de105.png) - - 1. Populate the Transaction field with check-contract-deployed.cdc, located in Resources/transactions - - ![Resources folder example](../media/0bc22650b47abb1cf606b2c7381a6377.png) - - 2. Set the Signer field to the new Account we just created earlier. ‘Player1’ - 3. Click Execute Transaction -3. If you have successfully configured the SDK and deployed the game contract, you will see the following message; - - ![Flow Emulator Output example](../media/bd9b6a0a7261d3c605aad9760a304220.png) - -## Step 3 – Incorporate Flow Accounts, Transactions and Scripts into Code - -We have our SDK and emulator configured, and our game contract deployed. -Now we are going to create the code which will allow our game to send and receive data from the blockchain. - -Our FlowWords tutorial project contains a script called FlowInterface.cs, which can be found in the Scripts folder. This script contains all of our blockchain interfacing functions, which are called from GameManager.cs and UIManager.cs. - -Our game FlowInterface has 5 main functions: - -- Login -- Logout -- GetGameDataFromChain -- SubmitGuess -- LoadHighScoresFromChain - -Our functions are going to need to access a number of cadence scripts and transactions. -These have been provided for you, and can be found in the Resources\\scripts folder and Resources\\transactions folder. - -![Resources folder example](../media/e6a3c62a95440af5c38b47c9efa27a72.png) - -FlowInterface.cs has a number of Serialized TextAsset fields, which can be populated via the Unity inspector. Select the GameFlowInterface gameobject in the Game.unity scene, and populate the fields as follows, using the scripts and transactions in the aforementioned folders; (you may find these have already been populated for you) - -![GameFlowInterface script assignment final state example](../media/5569416d261ff8e9d05b53c082c8a1b1.png) - -### Login -Open FlowInterface.cs and find the Login function stub. - -The Login function’s role is to take the credentials entered by the user, create a FlowControl.Account object with which we can submit transactions to the chain, and run the login.cdc transaction. - -At the top of the file, add the following using statements to grant us easy access to the Flow SDK structures. - -```csharp -using DapperLabs.Flow.Sdk.Cadence; -using DapperLabs.Flow.Sdk.DataObjects; -using DapperLabs.Flow.Sdk.DevWallet; -using Convert = DapperLabs.Flow.Sdk.Cadence.Convert; -``` - -Your file should now look like this: - -```csharp -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; -using UnityEngine; -using DapperLabs.Flow.Sdk; -using DapperLabs.Flow.Sdk.Unity; -using DapperLabs.Flow.Sdk.Cadence; -using DapperLabs.Flow.Sdk.DataObjects; -using DapperLabs.Flow.Sdk.DevWallet; -using Convert = DapperLabs.Flow.Sdk.Cadence.Convert; -``` - -Uncomment the following at line 56: -```csharp -//private FlowControl.Account FLOW_ACCOUNT = null; -``` - -We now have to register a wallet provider with the Flow SDK. We are going to use DevWallet, which comes with the Flow SDK, and is only intended for development purposes on emulator and testnet. Add the following to the `Start` function: - -```cs -// Set up SDK to access Emulator -FlowConfig flowConfig = new FlowConfig() -{ - NetworkUrl = "http://127.0.0.1:8888/v1", // emulator - Protocol = FlowConfig.NetworkProtocol.HTTP -}; -FlowSDK.Init(flowConfig); - -// Register DevWallet wallet provider with SDK -FlowSDK.RegisterWalletProvider(new DevWalletProvider()); -``` -Your Start function should now look like this: - -```csharp -private void Start() -{ - if (Instance != this) - { - Destroy(this); - } - - // Set up SDK to access Emulator - FlowConfig flowConfig = new FlowConfig() - { - NetworkUrl = "http://127.0.0.1:8888/v1", // emulator - Protocol = FlowConfig.NetworkProtocol.HTTP - }; - FlowSDK.Init(flowConfig); - - // Register DevWallet wallet provider with SDK - FlowSDK.RegisterWalletProvider(new DevWalletProvider()); -} -``` - -> **WARNING:** Do not use DevWallet in production builds. It is only intended for development purposes and does NOT securely store keys.\ -Having the Address and Private Keys to a blockchain account gives your application full access to all of that account’s funds and storage. They should be treated with extreme care. - -Next, we will fill out the body of the Login function. - -```csharp -/// -/// Attempts to log in by executing a transaction using the provided credentials -/// -/// An arbitrary username the player would like to be known by on the leaderboards -/// Function that should be called when login is successful -/// Function that should be called when login fails -public void Login(string username, System.Action onSuccessCallback, System.Action onFailureCallback) -{ - // Authenticate an account with DevWallet - -} -``` - -First, we have to invoke the wallet provider to authenticate the user and get their flow address. Add the following code to the Login function; - -```cs -// Authenticate an account with DevWallet -FlowSDK.GetWalletProvider().Authenticate( - "", // blank string will show list of accounts from Accounts tab of Flow Control Window - (string address) => onSuccessCallback(address, username), - onFailureCallback); -``` - -The Authenticate function takes parameters as follows; - -- The first parameter is a username which corresponds to the name of an account in the Accounts tab. If you leave this string blank (as above), a dialog will be shown to the user to select an account from the Accounts tab. -- The second parameter is a success callback for `Authenticate()`. We pass in a lambda function which starts a coroutine to run our async function `OnAuthSuccess`. This takes the flow address that we got from `Authenticate()`, as well as a few other parameters. -- The third parameter is a callback for if `Authenticate()` fails. We pass through the fail callback that was passed to Login. - -Your completed Login function should look as follows; - -```csharp -/// -/// Attempts to log in by executing a transaction using the provided credentials -/// -/// An arbitrary username the player would like to be known by on the leaderboards -/// Function that should be called when login is successful -/// Function that should be called when login fails -public void Login(string username, System.Action onSuccessCallback, System.Action onFailureCallback) -{ - // Authenticate an account with DevWallet - if (FlowSDK.GetWalletProvider().IsAuthenticated() == false) - { - FlowSDK.GetWalletProvider().Authenticate( - "", // blank string will show list of accounts from Accounts tab of Flow Control Window - (string address) => onSuccessCallback(address, username), - onFailureCallback); - } -} - -``` -Now we need to implement the `GetGameDataFromChain` function for when we successfully authenticate our user and get their flow address.\ -At this point we have successfully authenticated our user, and all subsequent Scripts and Transactions will be submitted via the authenticated account. - - -### GetGameDataFromChain - -This function executes the get-current-gamestate.cdc transaction on the chain, and then processes the emitted events to get the CurrentGameState for the logged in account, and current GameStartTime for the game of the day, which we use to show time remaining. - -```csharp -/// -/// Attempts to get the current game state for the user from chain. -/// -/// An arbitrary username the player would like to be known by on the leaderboards -/// Callback on success -/// Callback on failure -public IEnumerator GetGameDataFromChain(string username, System.Action, Dictionary> onSuccessCallback, System.Action onFailureCallback) -{ - // get FLOW_ACCOUNT object for text replacements - - // execute getCurrentGameState transaction on chain - - // check for error. if so, break. - - // transaction success, get data from emitted events - - // process currentGameState event - - // process gameStartTime event - - // call GameManager to set game state - - yield return null; -} -``` - -As you will recall, we previously configured a number of text replacements.\ -These are useful, and allow us to easily change some hardcoded data from a contract or transaction, such as the deploy address of the contract, from one central location without having to edit every cadence script in our project.\ -Our transactions and scripts will need to access the text replacement function, which lives in the FlowControl.Account class, so we need to first create a FlowControl.Account object as follows; - -```cs -// get FLOW_ACCOUNT object for text replacements -FLOW_ACCOUNT = new FlowControl.Account -{ - GatewayName = "Emulator", // the network to match - AccountConfig = new Dictionary { { "Address", FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address } } // the account address to match -}; -``` - -We are about to pull down the user's saved game data. To make processing event data easier, we have declared three classes to hold the results of events emitted by transactions: - -```csharp -public class StatePayload -{ - public List currentState; -} - -public class TimePayload -{ - public Decimal startTime; -} - -public class GuessResultPayload -{ - public string result; -} -``` - -Let's compare these to the payloads for each event in the contract: - -```cadence -access(all) event CurrentState(currentState: [UserGuess]) -access(all) event LastGameStart(startTime: UFix64) -access(all) event GuessResult(result: String) - -access(all) struct UserGuess -{ - access(all) let Guess: String - access(all) let Result: String - init(guess: String, result: String) - { - self.Guess = guess - self.Result = result - } -} -``` - -When using Cadence.Convert, cadence arrays are converted into C# Lists, as shown in the StatePayload class and CurrentState event. - -In GameManager.cs, the GuessResult class is declared as: - -```csharp -public class GuessResult -{ - /// - /// the guess that was submitted - /// - [Cadence(CadenceType = "String", Name = "Guess")] - public string word; - /// - /// A 5 letter code indicating the result and resulting color a cell should be. - /// - /// - /// "p" = the letter at this position was in the word and in the correct (p)osition, color the cell green. - /// "w" = the letter at this position was in the (w)ord, but in the incorrect position, color the cell yellow. - /// "n" = the letter at this position was (n)ot in the word. - /// - [Cadence(CadenceType = "String", Name = "Result")] - public string colorMap; -} -``` - -We want the Cadence fields ```Guess``` and ```Result``` to map to the C# fields ```word``` and ```colorMap```. To do this, we add a Cadence attribute to each field with a ```Name``` -parameter that tells it which Cadence fields maps to that class field. - -We did not have to do that with the three payload classes we defined earlier because the C# field names exactly match the Cadence field names in the contract. - -Now that we have an Account object for text replacement, we can use it with the Transactions class to Submit our login.cdc transaction. - -Add the following code to the GetGameDataFromChain function; - -```cs -// execute getCurrentGameState transaction on chain -Task getStateTask = Transactions.SubmitAndWaitUntilExecuted(FLOW_ACCOUNT.DoTextReplacements(loginTxn.text), new CadenceString(username)); - -while (!getStateTask.IsCompleted) -{ - int dots = ((int)(Time.time * 2.0f) % 4); - - UIManager.Instance.SetStatus($"Retrieving data from chain" + new string('.', dots)); - - yield return null; -} -``` -Because transactions can take quite some time on chain, we create an asynchronous Task by calling SubmitAndWaitUntilExecuted from the Transactions class, to prevent blocking the main game thread. -Into SubmitAndWaitUntilExecuted, we pass the script that we want to execute, and any parameters. - -For our script, we refer to the serialized TextAsset field, loginTxn, to which we will assign login.cdc in the inspector. -We pass our script into SubmitAndWaitUntilExecuted via the DoTextReplacements function on our FLOW_ACCOUNT object, which will parse the cadence script and replace any of our defined text replacements. - -For parameters, the login.cdc script is expecting a single String parameter with the player’s display name in it. We pass in a new CadenceString object, which we create inline from the encapsulating function’s username string parameter. - -Next, we simply wait until our asynchronous task.IsCompleted. -While we wait, we update the UI with a simple animated ‘Connecting…’ text status, and yield to the Unity engine to prevent blocking the thread. - -Once our transaction has completed, we want to check if it was successful on chain. Add the following code beneath the transaction submission code; - -```cs -// check for error. if so, break. -if (getStateTask.Result.Error != null || getStateTask.Result.ErrorMessage != string.Empty || getStateTask.Result.Status == FlowTransactionStatus.EXPIRED) -{ - onFailureCallback(); - yield break; -} -``` - -Here we must check the transaction Result for three conditions: - -- Error: Was there an error submitting the transaction to the blockchain? -- ErrorMessage: Was there an error during processing on the blockchain? -- Status: A status of EXPIRED means that the transaction did not execute on time and was discarded. - -Any error here, and we are simply going to fail the login, and call our onFailureCallback. - -Next, we process the result of our transaction. -This transaction is designed to return the game state for the user, and the time remaining on the word of the day, via emitted events. -We can access these emitted events via the .Result.Events property on our task. -To do so, add the following code below our submission logic; - -```cs -// transaction success, get data from emitted events -List events = getStateTask.Result.Events; -FlowEvent currentStateEvent = events.Find(x => x.Type.EndsWith(".CurrentState")); -FlowEvent startTimeEvent = events.Find(x => x.Type.EndsWith(".LastGameStart")); - -if (currentStateEvent == null || startTimeEvent == null) -{ - onFailureCallback(); - yield break; -} -``` - -This chunk accesses the returned Events list, and attempts to find events ending with “.CurrentState” and “.LastGameStart”. We use .EndsWith, as the transaction returns fully qualified event names, and the name of the deployed contract may change during development. -Finally, we check that we do indeed have both of our required events, and if not, call the onFailure callback and break. - -Next, we will parse the contents of each event. Add the following code to the function; - -```cs -// process current game state event -Decimal gameStartTime = 0; -Dictionary letterStatuses = new Dictionary(); -List results = Convert.FromCadence(currentStateEvent.Payload).currentState; -foreach (GuessResult newResult in results) -{ - newResult.word = newResult.word.ToUpper(); - for (int i = 0; i < 5; i++) - { - bool letterAlreadyExists = letterStatuses.ContainsKey(newResult.word[i].ToString()); - string currentStatus = letterAlreadyExists ? letterStatuses[newResult.word[i].ToString()] : ""; - switch (currentStatus) - { - case "": - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - break; - case "p": - break; - case "w": - if (newResult.colorMap[i] == 'p') - { - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - } - break; - case "n": - if (newResult.colorMap[i] == 'p' || newResult.colorMap[i] == 'w') - { - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - } - break; - } - } -} - -// get game start time event -gameStartTime = Convert.FromCadence(startTimeEvent.Payload).startTime; -``` -From the contract, we know that the CurrentState event returns a list of ```UserGuess``` structs. We want to convert these to a C# ```List```. - -```csharp -List results = Convert.FromCadence(currentStateEvent.Payload).currentState; -``` - -This converts the Payload of the currentStateEvent event into a ```StatePayload``` object, then sets results to the ```currentState``` field of that object. - -Then, we iterate over the results list and update the letterStatuses that we display. - - - -The GameStartTime event is processed similarly: - -```csharp -gameStartTime = Convert.FromCadence(startTimeEvent.Payload).startTime; -``` - -The startTimeEvent payload is converted into a TimePayload object and the startTime field is extracted from that. Because the Cadence type is UFix64, we get back a C# Decimal struct. - -Finally, we call our onSuccess callback to return our results to our caller. -Add the following lines to the bottom of the function; - -```cs -// call GameManager to set game state -onSuccessCallback(gameStartTime, results, letterStatuses); -``` - -You can now remove the ```yield return null``` at the base of the function if you wish. - -Your completed function should now look like this; - -```csharp -/// -/// Attempts to get the current game state for the user from chain. -/// -/// An arbitrary username the player would like to be known by on the leaderboards -/// Callback on success -/// Callback on failure -public IEnumerator GetGameDataFromChain(string username, System.Action, Dictionary> onSuccessCallback, System.Action onFailureCallback) -{ - // get FLOW_ACCOUNT object for text replacements - FLOW_ACCOUNT = new FlowControl.Account - { - GatewayName = "Emulator", // the network to match - AccountConfig = new Dictionary { { "Address", FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address } } // the account address to match - }; - - // execute getCurrentGameState transaction on chain - Task getStateTask = Transactions.SubmitAndWaitUntilExecuted(FLOW_ACCOUNT.DoTextReplacements(loginTxn.text), new CadenceString(username)); - - while (!getStateTask.IsCompleted) - { - int dots = ((int)(Time.time * 2.0f) % 4); - - UIManager.Instance.SetStatus($"Retrieving data from chain" + new string('.', dots)); - - yield return null; - } - - // check for error. if so, break. - if (getStateTask.Result.Error != null || getStateTask.Result.ErrorMessage != string.Empty || getStateTask.Result.Status == FlowTransactionStatus.EXPIRED) - { - onFailureCallback(); - yield break; - } - - // transaction success, get data from emitted events - List events = getStateTask.Result.Events; - FlowEvent currentStateEvent = events.Find(x => x.Type.EndsWith(".CurrentState")); - FlowEvent startTimeEvent = events.Find(x => x.Type.EndsWith(".LastGameStart")); - - if (currentStateEvent == null || startTimeEvent == null) - { - onFailureCallback(); - yield break; - } - - // process current game state event - Decimal gameStartTime = 0; - Dictionary letterStatuses = new Dictionary(); - List results = Convert.FromCadence(currentStateEvent.Payload).currentState; - foreach (GuessResult newResult in results) - { - newResult.word = newResult.word.ToUpper(); - for (int i = 0; i < 5; i++) - { - bool letterAlreadyExists = letterStatuses.ContainsKey(newResult.word[i].ToString()); - string currentStatus = letterAlreadyExists ? letterStatuses[newResult.word[i].ToString()] : ""; - switch (currentStatus) - { - case "": - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - break; - case "p": - break; - case "w": - if (newResult.colorMap[i] == 'p') - { - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - } - break; - case "n": - if (newResult.colorMap[i] == 'p' || newResult.colorMap[i] == 'w') - { - letterStatuses[newResult.word[i].ToString()] = newResult.colorMap[i].ToString(); - } - break; - } - } - } - - // get game start time event - gameStartTime = Convert.FromCadence(startTimeEvent.Payload).startTime; - - // call GameManager to set game state - onSuccessCallback(gameStartTime, results, letterStatuses); -} -``` - - -### Logout - -The Logout function’s role is to disconnect the authenticated wallet from your app, and to clear the FlowControl.Account object, to prevent any more transactions from being executed with those account credentials. - -The Logout function is very simple. Simply add the following line; - -```cs -FLOW_ACCOUNT = null; -FlowSDK.GetWalletProvider().Unauthenticate(); -``` - -This clears the FlowAccount object, preventing any more transactions from being submitted with it, and unauthenticates the user from the wallet provider. - -Your completed Logout function should now look like this; - -```csharp -/// -/// Clear the FLOW account object -/// -public void Logout() -{ - FLOW_ACCOUNT = null; - FlowSDK.GetWalletProvider().Unauthenticate(); -} -``` - - -### SubmitGuess - -This function has two phases. First, it checks that the entered word is valid by submitting the check-word.cdc script to chain, and processing the returned value. - -If the word is deemed valid, it then submits the word guess to the game contract using the currently logged in user’s credentials, by executing the submit-guess.cdc transaction script on chain, and then processing the emitted events. - -For phase one, enter the following code at the top of the SubmitGuess function; - -```cs -// submit word via checkWord script to FLOW chain to check if word is valid -Task checkWordTask = Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(checkWordScript.text), new CadenceString(word.ToLower())); - -while (!checkWordTask.IsCompleted) -{ - int dots = ((int)(Time.time * 2.0f) % 4); - UIManager.Instance.SetStatus("Waiting for server" + new string('.', dots)); - yield return null; -} - -if (checkWordTask.Result.Error != null) -{ - onFailureCallback(); - UIManager.Instance.SetStatus("Error checking word validity."); - yield break; -} - -bool wordValid = ((checkWordTask.Result.Value as CadenceString).Value == "OK"); -if (wordValid == false) -{ - onFailureCallback(); - yield break; -} -``` - -This code starts by calling ExecuteAtLatestBlock, passing in the checkWordScript and our guess word as a CadenceString object, to create an async Task using our Flow Account object. - -Scripts on Cadence can be thought of as read-only transactions, which are performed very quickly. -Since scripts are read only, they do not require signing, and are best to use when you need to quickly get publicly available data from chain. - -As with our previous transactions, we then wait until our task.IsCompleted, and then check for any errors in the result. With scripts we only have to check the Result.Error, as this catches all possible failure modes. - -We then process the return value of the script, which can be found in the Result.Value property on our completed task object. Scripts do not emit events like transactions, but have return values like a regular function. -The return value is of the generic base type CadenceBase, which we cast to CadenceString, as we are expecting a string type return value. - -If the word guess is deemed to be invalid we call the onFailure callback and break, otherwise we proceed onto the guess submission phase. - -For the second phase of the function, add the following code below phase one; - -```cs -// if word is valid, submit guess via transaction to FLOW chain -Task submitGuessTask = Transactions.SubmitAndWaitUntilExecuted(FLOW_ACCOUNT.DoTextReplacements(submitGuessTxn.text), new CadenceString(word.ToLower())); -while (!submitGuessTask.IsCompleted) -{ - int dots = ((int)(Time.time * 2.0f) % 4); - UIManager.Instance.SetStatus("Waiting for server" + new string('.', dots)); - yield return null; -} - -if (submitGuessTask.Result.Error != null || submitGuessTask.Result.ErrorMessage != string.Empty || submitGuessTask.Result.Status == FlowTransactionStatus.EXPIRED) -{ - onFailureCallback(); - yield break; -} - -// get wordscore -string wordScore = ""; -FlowEvent ourEvent = submitGuessTask.Result.Events.Find(x => x.Type.EndsWith(".GuessResult")); -if (ourEvent != null) -{ - wordScore = Convert.FromCadence(ourEvent.Payload).result; - - // check if we are out of guesses - if (wordScore == "OutOfGuesses") - { - onFailureCallback(); - UIManager.Instance.SetStatus("Out Of Guesses. Try again tomorrow."); - yield break; - } - - // process result - onSuccessCallback(word, wordScore); -} -else -{ - onFailureCallback(); -} -``` - -This phase begins by submitting the submit-guess.cdc transaction, passing in our guess word as a new CadenceString parameter. We then wait for the task to complete as usual, and check for any errors. -As this is a transaction, we once again check the three possible failure modes, and call the onFailure callback if the transaction failed. - -Next we parse our transaction’s emitted events. - -```cadence -access(all) event GuessResult(result: String) -``` - -We are expecting an event called GuessResult, with a single string parameter called result. We created a C# version of that event: ```GuessResultPayload```. - -```csharp -// get wordscore -string wordScore = ""; -FlowEvent ourEvent = submitGuessTask.Result.Events.Find(x => x.Type.EndsWith(".GuessResult")); -if (ourEvent != null) -{ - wordScore = Convert.FromCadence(ourEvent.Payload).result; - - // check if we are out of guesses - if (wordScore == "OutOfGuesses") - { - onFailureCallback(); - UIManager.Instance.SetStatus("Out Of Guesses. Try again tomorrow."); - yield break; - } - - // process result - onSuccessCallback(word, wordScore); -} -else -{ - onFailureCallback(); -} -``` - -We first find our event in the Result.Events list on our task object. - -If our event is found, we then convert the payload to a ```GuessResultPayload``` and store the ```result``` field as ```wordScore```. We then pass the guess word, and the result back to our caller via the onSuccess callback. - -If the GuessResult event cannot be found in the Result.Events list, we call the onFailure callback. - -Once complete, your SubmitGuess function should look like this: - -```csharp -public IEnumerator SubmitGuess(string word, System.Action onSuccessCallback, System.Action onFailureCallback) -{ - // submit word via checkWord script to FLOW chain to check if word is valid - Task checkWordTask = Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(checkWordScript.text), new CadenceString(word.ToLower())); - while (!checkWordTask.IsCompleted) - { - int dots = ((int)(Time.time * 2.0f) % 4); - UIManager.Instance.SetStatus("Waiting for server" + new string('.', dots)); - yield return null; - } - - if (checkWordTask.Result.Error != null) - { - onFailureCallback(); - UIManager.Instance.SetStatus("Error checking word validity."); - yield break; - } - - bool wordValid = ((checkWordTask.Result.Value as CadenceString).Value == "OK"); - if (wordValid == false) - { - onFailureCallback(); - yield break; - } - - // if word is valid, submit guess via transaction to FLOW chain - Task submitGuessTask = Transactions.SubmitAndWaitUntilExecuted(FLOW_ACCOUNT.DoTextReplacements(submitGuessTxn.text), new CadenceString(word.ToLower())); - while (!submitGuessTask.IsCompleted) - { - int dots = ((int)(Time.time * 2.0f) % 4); - UIManager.Instance.SetStatus("Waiting for server" + new string('.', dots)); - yield return null; - } - - if (submitGuessTask.Result.Error != null || submitGuessTask.Result.ErrorMessage != string.Empty || submitGuessTask.Result.Status == FlowTransactionStatus.EXPIRED) - { - onFailureCallback(); - yield break; - } - - // get wordscore - string wordScore = ""; - FlowEvent ourEvent = submitGuessTask.Result.Events.Find(x => x.Type.EndsWith(".GuessResult")); - if (ourEvent != null) - { - wordScore = Convert.FromCadence(ourEvent.Payload).result; - - // check if we are out of guesses - if (wordScore == "OutOfGuesses") - { - onFailureCallback(); - UIManager.Instance.SetStatus("Out Of Guesses. Try again tomorrow."); - yield break; - } - - // process result - onSuccessCallback(word, wordScore); - } - else - { - onFailureCallback(); - } -} -``` - -### LoadHighScoresFromChain - -This function fires off a number of small scripts simultaneously, which pull publicly available high score data from the game contract on chain using; - -- get-highscores.cdc -- get-player-cumulativescore.cdc -- get-player-guess-distribution.cdc -- get-player-maxstreak.cdc -- get-player-scores.cdc -- get-player-streak.cdc. - -It then processes their returned values, and passes them out to the onSuccess call for the high scores UI to render. - -For this function, we are going to first fire off a number of simultaneous scripts on the blockchain. This is something you want to avoid with transactions, as transaction order of execution cannot be guaranteed due to the distributed nature of blockchain, however as scripts are read-only, and do not mutate the chain, order of execution is far less likely to matter. - -To execute the scripts, add the following code to the top of the function; - -```cs -// get player's wallet public address -string playerWalletAddress = FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address; - -// execute scripts to get highscore data -Dictionary> tasks = new Dictionary>(); -tasks.Add("GetHighScores", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetHighScores.text))); -tasks.Add("GetPlayerCumulativeScore", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerCumulativeScore.text), new CadenceAddress(playerWalletAddress))); -tasks.Add("GetPlayerWinningStreak", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerWinningStreak.text), new CadenceAddress(playerWalletAddress))); -tasks.Add("GetPlayerMaxWinningStreak", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerMaxWinningStreak.text), new CadenceAddress(playerWalletAddress))); -tasks.Add("GetGuessDistribution", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetGuessDistribution.text), new CadenceAddress(playerWalletAddress))); - -// wait for completion -bool complete = false; -while (!complete) -{ - complete = true; - foreach (KeyValuePair> task in tasks) - { - complete = complete && task.Value.IsCompleted; - } - yield return null; -} - -// check for errors -foreach (KeyValuePair> task in tasks) -{ - if (task.Value.Result.Error != null) - { - onFailureCallback(); - yield break; - } -} -``` - -This block of code first obtains the public address of the current authenticated player and creates a ```Dictionary``` to store our concurrent script tasks. -We then sequentially create async Tasks for each script that we want to execute, using ExecuteAtLatestBlock, and add them to the Task dictionary. - -In our use case, we want all of the tasks to complete before we display any results, so our wait for completion code block iterates over every Task in the dictionary, and only moves on once every task.IsComplete. - -Checking for errors is similarly done using a foreach loop, where every task is checked to ensure the Error field is null. If even one task has an Error, we call the onFailure callback and break. - -Next we need to process the returned values. Add the following code beneath the previous; - -```cs -// load global highscores -List GlobalScores = Convert.FromCadence>(tasks["GetHighScores"].Result.Value); -GlobalScores = GlobalScores.OrderByDescending(score => score.Score).Take(10).ToList(); - -// load player scores -BigInteger PlayerCumulativeScore = Convert.FromCadence(tasks["GetPlayerCumulativeScore"].Result.Value); -BigInteger PlayerWinningStreak = Convert.FromCadence(tasks["GetPlayerWinningStreak"].Result.Value); -BigInteger PlayerMaximumWinningStreak = Convert.FromCadence(tasks["GetPlayerMaxWinningStreak"].Result.Value); -List PlayerGuessDistribution = Convert.FromCadence>(tasks["GetGuessDistribution"].Result.Value); - -// callback -onSuccessCallback(GlobalScores, PlayerCumulativeScore, PlayerWinningStreak, PlayerMaximumWinningStreak, PlayerGuessDistribution); -``` - -Our global highscores are an array of Scores objects in the contract. - -```cadence -access(contract) let TopScores : [Scores] -access(all) struct Scores -{ - access(all) let AccId : Address - access(all) let Name : String - access(all) let Score : UInt -} -``` - -We have a ScoreStruct defined HighScoresPanel.cs as: - -```csharp -public struct ScoreStruct -{ - public string Name; - public BigInteger Score; -} -``` - -```csharp -List GlobalScores = Convert.FromCadence>(tasks["GetHighScores"].Result.Value); -GlobalScores = GlobalScores.OrderByDescending(score => score.Score).Take(10).ToList(); -``` - -Here we get the result of the GetHighScores task and convert it into a ```List```. Then we reorder the list and keep only the highest ten values. - -Next, we parse the detailed statistics for the current player, using ```Convert.FromCadence``` to convert from the Cadence values into the C# types we want. - -Finally, we call the onSuccess callback, passing in all of our parsed results. - -Once complete, your function should look as follows; - -```csharp -public IEnumerator LoadHighScoresFromChain(System.Action, BigInteger, BigInteger, BigInteger, List> onSuccessCallback, System.Action onFailureCallback) -{ - // get player's wallet public address - string playerWalletAddress = FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address; - - // execute scripts to get highscore data - Dictionary> tasks = new Dictionary>(); - tasks.Add("GetHighScores", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetHighScores.text))); - tasks.Add("GetPlayerCumulativeScore", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerCumulativeScore.text), new CadenceAddress(playerWalletAddress))); - tasks.Add("GetPlayerWinningStreak", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerWinningStreak.text), new CadenceAddress(playerWalletAddress))); - tasks.Add("GetPlayerMaxWinningStreak", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetPlayerMaxWinningStreak.text), new CadenceAddress(playerWalletAddress))); - tasks.Add("GetGuessDistribution", Scripts.ExecuteAtLatestBlock(FLOW_ACCOUNT.DoTextReplacements(GetGuessDistribution.text), new CadenceAddress(playerWalletAddress))); - - // wait for completion - bool complete = false; - while (!complete) - { - complete = true; - foreach (KeyValuePair> task in tasks) - { - complete = complete && task.Value.IsCompleted; - } - yield return null; - } - - // check for errors - foreach (KeyValuePair> task in tasks) - { - if (task.Value.Result.Error != null) - { - onFailureCallback(); - yield break; - } - } - - // load global highscores - List GlobalScores = Convert.FromCadence>(tasks["GetHighScores"].Result.Value); - GlobalScores = GlobalScores.OrderByDescending(score => score.Score).Take(10).ToList(); - - // load player scores - BigInteger PlayerCumulativeScore = Convert.FromCadence(tasks["GetPlayerCumulativeScore"].Result.Value); - BigInteger PlayerWinningStreak = Convert.FromCadence(tasks["GetPlayerWinningStreak"].Result.Value); - BigInteger PlayerMaximumWinningStreak = Convert.FromCadence(tasks["GetPlayerMaxWinningStreak"].Result.Value); - List PlayerGuessDistribution = Convert.FromCadence>(tasks["GetGuessDistribution"].Result.Value); - - // callback - onSuccessCallback(GlobalScores, PlayerCumulativeScore, PlayerWinningStreak, PlayerMaximumWinningStreak, PlayerGuessDistribution); - - yield return null; -} -``` - -## Step 5 – Play FlowWords! - -If you have correctly followed all of the steps above, you will now have a working project. - -1. Return to the Unity editor, and press the Play button. -2. Enter a friendly name - this will appear on the leaderboard. -3. Click Log In. -4. Select an account from the dialog that appears to authenticate with. - -You should see the login screen, say Connecting…, and then Loading… - -![Login Panel User Interface](../media/fe0ccbbb8fb583dd7c461a1436d9206c.png) - -Followed shortly thereafter by the game screen; - -![Game User Interface](../media/8cd5f4e2d4ac66693e0c2a6f796d1b38.png) - -And the High Scores screen (if you click the button); - -![HighScores User Interface](../media/eb0fba4dbd9a040424afb3182e5ce345.png) - -## Step 6 – Further Experimentation - -For an extra challenge, try some of the following; - -- Add more accounts and play with some friends, hot seat style -- Modify the game-contract.cdc to make a new game every 5 minutes instead of every 24 hours. -- Try to remove and redeploy the contract - (hint: on testnet and mainnet, once removed, a contract’s name can never be used again on the same account) - (extra hint: delete-game-resources.cdc) -- Poke about in the game contracts, scripts and transactions to see what they do! - -If you ever get the emulator into a messy state, you can always hit the Clear Persistent Data button, which will wipe the emulator back to its blank state. This will of course lose all deployed contracts and high score and game history. - -## Appendix – How to convert FlowWords to run on TestNet - -To modify the tutorial project to run on TestNet, only minor modifications are required. - -1. Change the network configuration to point to TestNet -2. Replace the DevWallet provider with a more secure solution. e.g. WalletConnect -3. Configure Account and update Text Replacements -4. Deploy the contract to TestNet - -### Change the network configuration to TestNet - -To change the network configuration, simply modify the start function as follows; - -```cs -private void Start() -{ - if (Instance != this) - { - Destroy(this); - } - - // Set up SDK to access TestNet - FlowConfig flowConfig = new FlowConfig() - { - NetworkUrl = "https://rest-testnet.onflow.org/v1", // testnet - Protocol = FlowConfig.NetworkProtocol.HTTP - }; - FlowSDK.Init(flowConfig); - - // Register DevWallet wallet provider with SDK - FlowSDK.RegisterWalletProvider(new DevWalletProvider()); -} -``` - -We have now replaced the emulator address with the address for the TestNet access point, and all subsequent transactions and scripts will be directed to TestNet. - -### Replace DevWallet with WalletConnect - -To now change the wallet provider, simply modify the start function as follows; - -```cs -private void Start() -{ - if (Instance != this) - { - Destroy(this); - } - - // Set up SDK to access TestNet - FlowConfig flowConfig = new FlowConfig() - { - NetworkUrl = "https://rest-testnet.onflow.org/v1", // testnet - Protocol = FlowConfig.NetworkProtocol.HTTP - }; - FlowSDK.Init(flowConfig); - - // Create WalletConnect wallet provider - IWallet walletProvider = new WalletConnectProvider(); - walletProvider.Init(new WalletConnectConfig - { - ProjectId = "", // insert Project ID from Wallet Connect dashboard - ProjectDescription = "A simple word guessing game built on FLOW!", - ProjectIconUrl = "https://walletconnect.com/meta/favicon.ico", - ProjectName = "FlowWords", - ProjectUrl = "https://dapperlabs.com" - }); - - // Register WalletConnect wallet provider with SDK - FlowSDK.RegisterWalletProvider(walletProvider); -} -``` - -You will also need to add the following using declarations to the top of the file; - -```cs -using DapperLabs.Flow.Sdk.WalletConnect; -using DapperLabs.Flow.Sdk.Crypto; -``` - -For this modification we have created a new WalletConnectProvider, and initialized it, and then registered our new WalletConnectProvider. - -The only thing missing is a Project Id.\ -Each WalletConnect application requires its own project id. You can get one by going to https://cloud.walletconnect.com and signing up for an account.\ -You can then create a new project on the website, give it a name (we suggest FlowWords), and enter the project id provided into your code. - -Finally, we need one more change to make sure our text replacement still functions correctly. - -At the beginning of the OnAuthSuccess function, change the FLOW_ACCOUNT GatewayName from "Emulator" to "Flow Testnet". - -```cs -// get FLOW account - we are only going to use this for text replacements -FLOW_ACCOUNT = new FlowControl.Account -{ - GatewayName = "Flow Testnet", - AccountConfig = new Dictionary { { "Address", FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address } } -}; -``` - -### Configure Account and update Text Replacements - -To deploy the contract to TestNet, the only current way to do so via the FlowSDK, is to use a TestNet account to which you know the private key. - -First, in the Accounts tab of the Flow Control window, create a new account by clicking the plus '+' icon at the top of the window.\ -Create the new account as follows, replacing the Address and Private Key with your own valid TestNet address / private key pair. - -![New Account Example](../media/AppendixNewAccount.png) - -Switch to the Text Replacements tab of the Flow Control window now, and create a new text replacement definition as follows, replacing the given replacement text with the address to your TestNet account. - -![New Text Replacement Example](../media/AppendixTextReplacement.png) - -Now modify the existing CONTRACT_NAME text replacement, by changing the Apply to Gateways field from Emulator, to All. This will make sure this replacement also applies to TestNet transactions. - -### Deploy the Contract to TestNet - -Now that all of the configuration is done, you can deploy the contract. - -On the Tools tab of the Flow Control window, set up the Manage Contracts section as follows; - -![New Account Example](../media/AppendixDeployContract.png) - -Where; -- Contract Name is the name of the contract on the blockchain. This should match the text replacement configured for CONTRACT_NAME -- Contract is the game-contract.cdc asset included with the project sample in the Assets\\Samples\\Flow SDK\\<version>\\Flow Words Tutorial Assets\\Resources directory. -- Account is the new Deploy Account that you created in the previous step. - -Once you have the Manage Contracts section filled in as per the example, click Deploy Contract. - -If all goes well, you will see the text 'Executing Transaction' appear below the Deploy Contract button, followed by a clickable link to the transaction result on [Flowdiver](https://testnet.flowdiver.io/). - -Congratulations! You can now run FlowWords, and play the game with a WalletConnect compatible wallet! - -> **NOTE:** TestNet requires that all contracts deployed to an account have a unique name, does not allow contract removal without authorisation, and only allows contract updates that do not break interfaces or data structures or introduce undefined data to existing resources.\ -\ -Due to these limitations, iterative development should always be done on Emulator before attempting to push anything to a live network. - diff --git a/docs/tools/clients/unity-sdk/samples/nft-browser.md b/docs/tools/clients/unity-sdk/samples/nft-browser.md deleted file mode 100644 index a4ecdf2dc0..0000000000 --- a/docs/tools/clients/unity-sdk/samples/nft-browser.md +++ /dev/null @@ -1,291 +0,0 @@ -# Simple NFT Viewer - -This example project will show you how to build a simple viewer that will allow you to view NFTs that conform to the [NFT](https://github.com/onflow/flow-nft) and [MetadataViews](https://github.com/onflow/flow-nft#nft-metadata) standards. - -This tutorial will mostly ignore the C# code that actually displays the NFTs and focus on a high level summary of the steps used. - -## Overview - -When querying the blockchain we utilize four scripts: -``` -* [GetCollections.cdc](Cadence/GetCollections.cdc) - Gets a list of Collections that conform to NFT.Collection for a given address -* [GetNftIdsForCollection.cdc](Cadence/GetNftIdsForCollection.cdc) - Gets a list of all NFT IDs that are contained in a given collection -* [GetDisplayDataForIDs.cdc](Cadence/GetDisplayDataForIDs.cdc) - Gets just the display data for a given NFT -* [GetFullDataForID.cdc](Cadence/GetFullDataForID.cdc) - Gets a more comprehensive set of data for a single NFT. -``` -While we could use a single script to query for all the data, larger collections will cause the script to time out. Instead we query for just the data we need to reduce the chances of a timeout occurring. - -## Finding Collections - -First we need to get a list of all collections on an account that are a subtype of NFT.Collection. - -```cadence -import NonFungibleToken from 0x1d7e57aa55817448 - -access(all) fun main(addr: Address) : [StoragePath] { - // Get the authorized Account object for the given address. - // The authorized account is needed because we're going to be looking into the Storage of the user - var acct = getAuthAccount(addr) - - //Array that we will fill with all valid storage paths - var paths : [StoragePath] = [] - - //Uses the storage iteration API to iterate through all storage paths on the account - acct.forEachStored(fun (path: StoragePath, type:Type): Bool { - //Check to see if the resource at this location is a subtype of NonFungibleToken.Collection. - if type.isSubtype(of: Type<@NonFungibleToken.Collection>()) { - //Add this path to the array - paths.append(path) - } - - //returning true tells the iterator to continue to the next entry - return true - }); - - //Return the array that we built - return paths -} -``` - -We use the [Storage Iteration API](https://cadence-lang.org/docs/language/accounts#storage-iteration) to look at everything the account has in it's storage and see if it is an NFT Collection. We return a list of all found NFT Collections. - -## Getting NFT IDs Contained in a Collection - -We use this to create a list of collection paths a user can pick from. When the user selects a path to view, we fetch a list of IDs contained in that collection: - -```cadence -import NonFungibleToken from 0x1d7e57aa55817448 - -access(all) fun main(addr: Address, path: StoragePath) : [UInt64] { - // Get the authorized Account object for the given address. - // The authorized account is needed because we're going to be looking into the Storage of the user - var acct = getAuthAccount(addr) - - //Get a reference to an interface of type NonFungibleToken.Collection public backed by the resource located at path - var ref = acct.borrow<&{NonFungibleToken.CollectionPublic}>(from: path)! - - //Return the list of NFT IDs contained in this collection - return ref!.getIDs() -} -``` - -## Getting Display Data for an NFT - -After we get a list of the available NFT IDs, we need to get some basic data about the NFT to display the thumbnail icon. - -```cadence -import NonFungibleToken from 0x1d7e57aa55817448 -import MetadataViews from 0x1d7e57aa55817448 - -access(all) fun main(addr: Address, path: StoragePath, ids: [UInt64]) : {UInt64:AnyStruct?} { - //Array to hold the NFT display data that we will return - //We use AnyStruct? because that is the type that is returned by resolveView. - var returnData: {UInt64:AnyStruct?} = {} - - // Get the authorized Account object for the given address. - // The authorized account is needed because we're going to be looking into the Storage of the user - var acct = getAuthAccount(addr) - - //Get a reference to a capability to the storage path as a NonFungibleToken.CollectionPublic - var ref = acct.borrow<&{NonFungibleToken.CollectionPublic}>(from: path)! - - //Loop through the requested IDs - for id in ids { - //Get a reference to the NFT we're interested in - var nftRef = ref.borrowNFT(id: id) - - //If for some reason we couldn't borrow a reference, continue onto the next NFT - if nftRef == nil { - continue - } - - //Fetch the information we're interested in and store it in our NFT structure - returnData[id] = nftRef.resolveView(Type()) - } - - return returnData -} -``` -This gives us a dictionary that maps NFT IDs to Display structs (```{UInt64:MetadataViews.Display}```). Because accessing this information can be tedious in C#, we can define some C# classes to make our lives easier: - -```csharp -public class File -{ - public string url; - public string cid; - public string path; -} - -public class Display -{ - public String name; - public String description; - public File thumbnail; -} -``` - -This will allow us to use Cadence.Convert to convert from the CadenceBase that the script returns into a Display class. - -This line in NFTViewer.cs is an example of converting using Cadence.Convert: - -```csharp -Dictionary displayData = Convert.FromCadence>(scriptResponseTask.Result.Value); -``` - -You might ask whey we don't combine GetNftIdsForCollection.cdc and GetDisplayDataForIDs.cdc to get the Display data at the same time we get the list of IDs. This approach would work in many cases, but when an account contains large numbers of NFTs, this could cause a script timeout. Getting the list of IDs is a cheap call because the NFT contains this list in an array already. -By getting just the NFT IDs, we could implement paging and use multiple script calls to each fetch a portion of the display data. -This example doesn't currently do this type of paging, but could do so without modifying the cadence scripts. - -## Getting Complete NFT Data - -When a user selects a particular NFT to view in more detail, we need to fetch that detail. - -```cadence -import NonFungibleToken from 0x1d7e57aa55817448 -import MetadataViews from 0x1d7e57aa55817448 - -//Structure that will hold all the data we want for an NFT -access(all) struct NFTData { - access(all) var NFTView: AnyStruct? - access(all) var Display : AnyStruct? - access(all) var HTTPFile: AnyStruct? - access(all) var IPFSFile: AnyStruct? - access(all) var Edition: AnyStruct? - access(all) var Editions: AnyStruct? - access(all) var Serial: AnyStruct? - access(all) var Royalty: AnyStruct? - access(all) var Royalties: AnyStruct? - access(all) var Media: AnyStruct? - access(all) var Medias: AnyStruct? - access(all) var License: AnyStruct? - access(all) var ExternalURL: AnyStruct? - access(all) var NFTCollectionDisplay: AnyStruct? - access(all) var Rarity: AnyStruct? - access(all) var Trait: AnyStruct? - access(all) var Traits: AnyStruct? - - init() { - self.NFTView = nil - self.Display = nil - self.HTTPFile = nil - self.IPFSFile = nil - self.Edition = nil - self.Editions = nil - self.Serial = nil - self.Royalty = nil - self.Royalties = nil - self.Media = nil - self.Medias = nil - self.License = nil - self.ExternalURL = nil - self.NFTCollectionDisplay = nil - self.Rarity = nil - self.Trait = nil - self.Traits = nil - } -} - -access(all) fun main(addr: Address, path: StoragePath, id: UInt64) : NFTData? { - // Get the authorized Account object for the given address. - // The authorized account is needed because we're going to be looking into the Storage of the user - var acct = getAuthAccount(addr) - - //Get a reference to a capability to the storage path as a NonFungibleToken.CollectionPublic - var ref = acct.borrow<&{NonFungibleToken.CollectionPublic}>(from: path)! - - //Get a reference to the NFT we're interested in - var nftRef = ref.borrowNFT(id: id) - - //If for some reason we couldn't borrow a reference, continue onto the next NFT - if nftRef == nil { - return nil - } - - var nftData : NFTData = NFTData() - - //Fetch the information we're interested in and store it in our NFT structure - nftData.Display = nftRef.resolveView(Type()) - nftData.NFTView = nftRef.resolveView(Type()) - nftData.HTTPFile = nftRef.resolveView(Type()) - nftData.IPFSFile = nftRef.resolveView(Type()) - nftData.Edition = nftRef.resolveView(Type()) - nftData.Editions = nftRef.resolveView(Type()) - nftData.Serial = nftRef.resolveView(Type()) - nftData.Media = nftRef.resolveView(Type()) - nftData.Rarity = nftRef.resolveView(Type()) - nftData.Trait = nftRef.resolveView(Type()) - nftData.Traits = nftRef.resolveView(Type()) - nftData.Medias = nftRef.resolveView(Type()) - nftData.ExternalURL = nftRef.resolveView(Type()) - nftData.Royalty = nftRef.resolveView(Type()) - nftData.Royalties = nftRef.resolveView(Type()) - nftData.License = nftRef.resolveView(Type()) - nftData.NFTCollectionDisplay = nftRef.resolveView(Type()) - - return nftData -} -``` -Here we define a struct NFTData that will contain all the different information we want and fill the struct via multiple resolveView calls. - -## C# Classes for Easy Converting - -The end of NFTViewer.cs contains classes that we use to more easily convert from Cadence into C#. One thing to note is that the Cadence structs contain Optionals, like: - -```var IPFSFile: AnyStruct?``` - -while the C# versions do not, such as - -```public IPFSFile IPFSFile;``` - -This is because we are declaring them as Classes, not Structs. Classes in C# are reference types, which can automatically be null. We could have used Structs, in which case we'd have to use: - -```public IPFSFile? IPFSFile``` - -This would wrap the IPFSFile struct in a Nullable, which would allow it to be null if the Cadence value was nil. - -Another thing to note is the declaration of the C# File class: - -```csharp -public class File -{ - public string url; - public string cid; - public string path; - - public string GetURL() - { - if (string.IsNullOrEmpty(url) && !string.IsNullOrEmpty(cid)) - { - return $"https://ipfs.io/ipfs/{cid}"; - } - - return url; - } -} -``` - -Compare this to the File interface in the MetadataViews contract: - -```cadence - access(all) struct interface File { - access(all) fun uri(): String - } -``` - -The MetadataViews.File interface doesn't actually contain any fields, only a single method. Because only two things in MetadataViews implement the -File interface (HTTPFile and IPFSFile), we chose to combine the possible fields into our File class. - -```cadence -access(all) struct HTTPFile: File { - access(all) let url: String -} - -access(all) struct IPFSFile: File { - access(all) let cid: String - access(all) let path: String? -} -``` - -This allows Cadence.Convert to convert either an HTTPFile or an IPFSFile into a File object. We can then check which fields are populated to determine which it was initially. - -This works fine for this simple viewer, but a more robust approach might be to create a ResolvedFile struct in the cadence script which has a single uri field and populates it by calling the uri() function on whatever File type was retrieved. diff --git a/docs/tools/clients/unity-sdk/samples/nft-example.md b/docs/tools/clients/unity-sdk/samples/nft-example.md deleted file mode 100644 index 6deb513a6b..0000000000 --- a/docs/tools/clients/unity-sdk/samples/nft-example.md +++ /dev/null @@ -1,419 +0,0 @@ -# Simple NFT demo - -This tutorial will show you how to create, mint and list a simple NFT. It follows the -Non Fungible Token standard (https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc), -but does not implement the MetadataViews interface. If you would like to make your NFT compatible with -marketplaces, look at implementing MetadataViews (https://github.com/onflow/flow-nft/blob/master/contracts/MetadataViews.cdc) - -The following are the main points of this tutorial: -1. Creating a contract that implements INFT -2. Deploying the contract -3. Listing, minting and storing NFTs defined by the contract via a transaction - - -### Getting started -Load the Samples/Flow SDK/x.x.x/Example NFT/Scenes/NFTExampleScene scene. -Press play and approve the transactions that come up (only on first time run) -Click Authenticate and choose the emulator_service_account. -Click Mint -Fill in the Text and URL fields and click Mint -Approve the transaction -Click List to refresh the NFT display panel and show your newly minted NFT -Repeat Mint and List as desired to make your list grow - -Now we'll show you how this works. - -### Creating an NFT contract - -When creating an NFT it is recommended (but not required) to implement the NonFungibleToken.INFT -interface. We will be doing so in this case. - -At its simplest, an NFT on Flow is a resource with a unique id. A Collection is a resource -that will allow you to store, list, deposit, and withdraw NFTs of a specific type. - -We recommend reading through the [NFT tutorial](https://cadence-lang.org/docs/tutorial/non-fungible-tokens-1) -to understand what is happening, as well as reviewing the contents of Cadence/Contracts/SDKExampleNFT.cdc - -The SDKExampleNFT minter allows for anyone to mint an SDKExampleNFT. Typically you would restrict -minting to an authorized account. - -This tutorial will not delve deeply into the NFT contract or Cadence, instead focusing on interacting -with them using the functionality the Unity SDK provides. - -### Deploying the contracts - -Open up Example.cs to follow along. - -Our Start function looks like this: - -```csharp -public void Start() -{ - //Initialize the FlowSDK, connecting to an emulator using HTTP - FlowSDK.Init(new FlowConfig - { - NetworkUrl = FlowControl.Data.EmulatorSettings.emulatorEndpoint, - Protocol = FlowConfig.NetworkProtocol.HTTP - }); - - //Register the DevWallet provider that we will be using - FlowSDK.RegisterWalletProvider(new DevWalletProvider()); - - //Deploy the NonFungibleToken and SDKExampleNFT contracts if they are not already deployed - StartCoroutine(DeployContracts()); -} -``` - -This initializes the FlowSDK to connect to the emulator, creates and registers a DevWalletProvioder, then -starts a coroutine to deploy our contract if needed. - -Contracts can be deployed via the FlowControl Tools window, but we will deploy them via code for ease -of use. - -The DeployContracts coroutine: - -```csharp -public IEnumerator DeployContracts() -{ - statusText.text = "Verifying contracts"; - //Wait 1 second to ensure emulator has started up and service account information has been captured. - yield return new WaitForSeconds(1.0f); - - //Get the address of the emulator_service_account, then get an account object for that account. - Task accountTask = Accounts.GetByAddress(FlowControl.Data.Accounts.Find(acct => acct.Name == "emulator_service_account").AccountConfig["Address"]); - //Wait until the account fetch is complete - yield return new WaitUntil(() => accountTask.IsCompleted); - - //Check for errors. - if (accountTask.Result.Error != null) - { - Debug.LogError(accountTask.Result.Error.Message); - Debug.LogError(accountTask.Result.Error.StackTrace); - } - - //We now have an Account object, which contains the contracts deployed to that account. Check if the NonFungileToken and SDKExampleNFT contracts are deployed - if (!accountTask.Result.Contracts.Exists(x => x.Name == "SDKExampleNFT") || !accountTask.Result.Contracts.Exists(x => x.Name == "NonFungibleToken")) - { - statusText.text = "Deploying contracts,\napprove transactions"; - - //First authenticate as the emulator_service_account using DevWallet - FlowSDK.GetWalletProvider().Authenticate("emulator_service_account", null, null); - - //Ensure that we authenticated properly - if (FlowSDK.GetWalletProvider().GetAuthenticatedAccount() == null) - { - Debug.LogError("No authenticated account."); - yield break; - } - - //Deploy the NonFungibleToken contract - Task txResponse = CommonTransactions.DeployContract("NonFungibleToken", NonFungibleTokenContract.text); - yield return new WaitUntil(() => txResponse.IsCompleted); - if (txResponse.Result.Error != null) - { - Debug.LogError(txResponse.Result.Error.Message); - Debug.LogError(txResponse.Result.Error.StackTrace); - yield break; - } - - //Wait until the transaction finishes executing - Task txResult = Transactions.GetResult(txResponse.Result.Id); - yield return new WaitUntil(() => txResult.IsCompleted); - - //Deploy the SDKExampleNFT contract - txResponse = CommonTransactions.DeployContract("SDKExampleNFT", SDKExampleNFTContract.text); - yield return new WaitUntil(() => txResponse.IsCompleted); - if (txResponse.Result.Error != null) - { - Debug.LogError(txResponse.Result.Error.Message); - Debug.LogError(txResponse.Result.Error.StackTrace); - yield break; - } - - //Wait until the transaction finishes executing - txResult = Transactions.GetResult(txResponse.Result.Id); - yield return new WaitUntil(() => txResult.IsCompleted); - - //Unauthenticate as the emulator_service_account - FlowSDK.GetWalletProvider().Unauthenticate(); - } - - //Enable the Authenticate button. - authenticateButton.interactable = true; - statusText.text = ""; -} -``` - -We start by waiting one second. This ensures that the emulator has finished initializing and -the required service account has been populated. - -Next we fetch the emulator_service_account Account. This Account object will contain the contracts -that are deployed to the account. We check if both the required contracts are deployed, -and if they are not, we deploy them. - -Upon first running the scene, you will be presented with two popups by DevWallet. This authorizes -the transactions that will deploy the contracts. You will not see these popups during subsequent -runs because the contracts will already be present on the account. If you purge the emulator -data, you will see the popups again the next time you play the scene. - -When using Testnet or Mainnet, the NonFungibleToken contract will already be deployed at a known location. -Launching the emulator with the --contracts flag will also deploy this contract. I this case we are running -without --contracts, so we will deploy the NonFungibleToken contract ourselves. - -### Listing, minting, and storing NFTs - -Now that the contracts are in place, the Authenticate button will be clickable. This uses the -registered wallet provider (DevWalletProvider) to authenticate. Unless you create another account -using the FlowControl Tools panel, only emulator_service_account will be available. - -After clicking Authenticate, it will prompt you to select an account to authenticate as. Choose -emulator_service_account. This is done with the following functions: - -```csharp - public void Authenticate() - { - FlowSDK.GetWalletProvider().Authenticate("", OnAuthSuccess, OnAuthFailed); - } - - private void OnAuthFailed() - { - Debug.LogError("Authentication failed!"); - accountText.text = $"Account: {FlowSDK.GetWalletProvider().GetAuthenticatedAccount()?.Address??"None"}"; - if (FlowSDK.GetWalletProvider().GetAuthenticatedAccount() == null) - { - mintPanelButton.interactable = false; - listButton.interactable = false; - } - } - - private void OnAuthSuccess(string obj) - { - accountText.text = $"Account: {FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address}"; - mintPanelButton.interactable = true; - listButton.interactable = true; - } -``` - -If authentication succeeds, a coroutine is started that will make the Mint button available. - -Clicking on the Mint button displays the Minting panel that will allow you to customize the NFT that will -be minted: - -```csharp -public void ShowMintPanel() - { - textInputField.text = ""; - URLInputField.text = ""; - mintPanel.SetActive(true); - } -``` - -### Minting -Clicking Mint in the Mint panel will trigger the creation of the NFT with the supplied text. - - -```csharp -public void MintNFT() - { - if(FlowSDK.GetWalletProvider() != null && FlowSDK.GetWalletProvider().IsAuthenticated()) - { - StartCoroutine(MintNFTCoroutine()); - } - - mintPanel.SetActive(false); - } -``` - -```csharp - public IEnumerator MintNFTCoroutine() - { - statusText.text = "Minting..."; - List args = new List - { - Convert.ToCadence(new Dictionary - { - ["Text"] = textInputField.text, - ["URL"] = URLInputField.text - }, "{String:String}") - }; - - Task txResponse = Transactions.Submit(mintTransaction.text, args); - - while(!txResponse.IsCompleted) - { - yield return null; - } - - if (txResponse.Result.Error != null) - { - statusText.text = "Error, see log"; - Debug.LogError(txResponse.Result.Error.Message); - yield break; - } - - Task txResult = Transactions.GetResult(txResponse.Result.Id); - - while (!txResult.IsCompleted) - { - yield return null; - } - - if (txResult.Result.Error != null) - { - statusText.text = "Error, see log"; - Debug.LogError(txResult.Result.Error.Message); - yield break; - } - - statusText.text = ""; - } -``` - -Because transactions can take a while, they are done in coroutines to prevent the interface from locking -up. - -First we construct a list of arguments we are going to pass to the transaction in MintAndSave.cdc. This -list consists of a single Dictionary containing the "Text" and "URL" keys and String values from the Mint -panel. We use Cadence.Convert to convert from a `Dictionary` into a Cadence `{String:String}` -for the argument. - -The MintAndSave.cdc file contains the transaction that will be executed. - -```cadence -import SDKExampleNFT from 0xf8d6e0586b0a20c7 -import NonFungibleToken from 0xf8d6e0586b0a20c7 - -transaction(md: {String:String}) { - let acct : auth(Storage, Capabilities) &Account - - prepare(signer: auth(Storage, Capabilities) &Account) { - self.acct = signer - } - - execute { - // Create collection if it doesn't exist - if self.acct.storage.borrow<&SDKExampleNFT.Collection>(from: SDKExampleNFT.CollectionStoragePath) == nil - { - // Create a new empty collection - let collection <- SDKExampleNFT.createEmptyCollection() - // save it to the account - self.acct.save(<-collection, to: SDKExampleNFT.CollectionStoragePath) - // link a public capability for the collection - let newCap = self.acct.capabilities.storage.issue<&{SDKExampleNFT.CollectionPublic, NonFungibleToken.CollectionPublic}>( - SDKExampleNFT.CollectionStoragePath - ) - self.acct.capabilities.publish(newCap, to: SDKExampleNFT.CollectionPublicPath) - } - - //Get a reference to the minter - let minter = getAccount(0xf8d6e0586b0a20c7) - .capabilities.get(SDKExampleNFT.MinterPublicPath) - .borrow<&{SDKExampleNFT.PublicMinter}>() - - - //Get a CollectionPublic reference to the collection - let collection = self.acct.capabilities.get(SDKExampleNFT.CollectionPublicPath) - .borrow<&{NonFungibleToken.CollectionPublic}>() - - //Mint a new NFT and deposit into the authorizers account - minter?.mintNFT(recipient: collection!, metadata: md) - } -} -``` - -This transaction checks to see if an SDKExampleNFT collection exists on the account, creating/saving/linking it if it does -not. Then it calls the contract to mint a new NFT with the desired metadata and saves it to the collection. - -### Listing NFTs - -The List button calls the UpdateNFTPanelCoroutine function that is responsible for populating the panel with information -about the SDKExampleNFT resources in the account you are authenticated as. - -```csharp -public IEnumerator UpdateNFTPanelCoroutine() -{ - //Create the script request. We use the text in the GetNFTsOnAccount.cdc file and pass the address of the - //authenticated account as the address of the account we want to query. - FlowScriptRequest scriptRequest = new FlowScriptRequest - { - Script = listScript.text, - Arguments = new List - { - new CadenceAddress(FlowSDK.GetWalletProvider().GetAuthenticatedAccount().Address) - } - }; - - //Execute the script and wait until it is completed. - Task scriptResponse = Scripts.ExecuteAtLatestBlock(scriptRequest); - yield return new WaitUntil(() => scriptResponse.IsCompleted); - - //Destroy existing NFT display prefabs - foreach (TMP_Text child in NFTContentPanel.GetComponentsInChildren()) - { - Destroy(child.transform.parent.gameObject); - } - - //Iterate over the returned dictionary - Dictionary> results = Convert.FromCadence>>(scriptResponse.Result.Value); - //Iterate over the returned dictionary - foreach (KeyValuePair> nft in results) - { - //Create a prefab for the NFT - GameObject prefab = Instantiate(NFTPrefab, NFTContentPanel.transform); - - //Set the text - string text = $"ID: {nft.Key}\n"; - foreach (KeyValuePair pair in nft.Value) - { - text += $" {pair.Key}: {pair.Value}\n"; - } - - prefab.GetComponentInChildren().text = text; - } -} -``` - -When running a script, you can query any account. In this case we will only query the account -that is authenticated with the wallet provider. - -It executes the script defined in GetNFTsOnAccount.cdc: - -```cadence -import SDKExampleNFT from 0xf8d6e0586b0a20c7 - -access(all) fun main(addr:Address): {UInt64:{String:String}} { - - //Get a capability to the SDKExampleNFT collection if it exists. Return an empty dictionary if it does not - let collectionCap = getAccount(addr).capabilities.get<&{SDKExampleNFT.CollectionPublic}>(SDKExampleNFT.CollectionPublicPath) - if(!collectionCap.check()) - { - return {} - } - - //Borrow a reference to the capability, returning an empty dictionary if it can not borrow - let collection = collectionCap.borrow() - if(collection == nil) - { - return {} - } - - //Create a variable to store the information we extract from the NFTs - var output : {UInt64:{String:String}} = {} - - //Iterate through the NFTs, extracting id and metadata from each. - for id in collection?.getIDs()! { - log(collection!.borrowSDKExampleNFT(id:id)) - log(collection!.borrowSDKExampleNFT(id:id)!.metadata) - output[id] = collection!.borrowSDKExampleNFT(id:id)!.metadata; - } - - //Return the constructed data - return output -} -``` - -This ensures that an SDKExampleNFT.Collection resource exists at the proper path, then creates and returns -a ```{UInt64:{String:String}}``` containing the information of all SDKExampleNFTs in the collection. We use -Cadence.Convert to convert this into a C# ```Dictionary>``` - -After that we Instantiate prefabs to display the data of each of the returned NFTs. \ No newline at end of file diff --git a/docs/tools/clients/unity-sdk/samples/quickstart.md b/docs/tools/clients/unity-sdk/samples/quickstart.md deleted file mode 100644 index 08bba4f5ff..0000000000 --- a/docs/tools/clients/unity-sdk/samples/quickstart.md +++ /dev/null @@ -1,378 +0,0 @@ -# FlowControl scripting example -This example shows how to use FlowControl to perform common tasks purely from C#, without using -the FlowControl editor interface. This will ignore any of the GUI related code and focus on -using the FlowControl and Flow SDK. - -## Prerequisites -Ensure you have flow-cli installed. This will allow us to use an emulated flow environment. -You can install it by following the instructions at [Flow CLI](../../../../tools/flow-cli/install.md) - -## Sample walk through -You can follow along in FlowControlExample.cs - -The first thing to notice is that we declare Start() to be an IEnumerator. This makes Start a coroutine. -You will always want to run FlowSDK functions inside a coroutine because they can take a while to complete -and you don't want to lock up your game while they are processed. - -```csharp -private IEnumerator Start() -{ - // Your start code -} -``` - -## Checking emulator state -The next thing we do is ensure the emulator is running. We give it a few seconds to start: - -```csharp -//Wait up to 2.5 seconds for the emulator to start. -int waited = 0; - -while (!FlowControl.IsEmulatorRunning && waited < 5) -{ - waited++; - yield return new WaitForSeconds(.5f); -} - -if (!FlowControl.IsEmulatorRunning) -{ - //Stop execution if the emulator is not running by now. - yield break; -} -``` - -## Creating a FlowControl Account -Next we'll create a FlowControl account to use ***ONLY*** for running scripts. The Flow network -doesn't require an account to run scripts, but FlowControl uses Accounts to determine which network -to connect to. - -```csharp -FlowControl.Account scriptOnlyAccount = new FlowControl.Account -{ - GatewayName = "Emulator" -}; -``` - -Because this account doesn't have any keys associated with it, it can't be used to run transactions. -It does define which Gateway to use, in this case the "Emulator" gateway, so it can be used -to run scripts. - -## Running scripts - -Next, we'll use this account to run a script on the emulator. Scripts on Flow are written in Cadence. -More information is available at [Developer Portal](https://cadence-lang.org/docs/language/) - -First we'll define the script that we want to run: - -```csharp -const string code = @"access(all) fun main(message: String): Int{ - log(message) - return 42 -}"; -``` - -This script requires a Cadence String as input, returns a Cadence Int, and will log the input -string to the emulator log. - -Now we execute this script: - -```csharp -Task task = scriptOnlyAccount.ExecuteScript(code, Convert.ToCadence("Test", "String")); -``` - -FlowControl uses an Account oriented approach. Everything is done using an Account object. In this -case we'll use the scriptOnlyAccount account that we created earlier to call ExecuteScript. - -A script is code that can not permanently mutate the state of the blockchain. It is read-only. -It ***CAN*** call functions that would change the state of the blockchain, but any changes that are -made will be discarded once the script finishes running. - -We pass in the Cadence code we want to run and any arguments that are required by the script. -We need to use Cadence specific data types, so we construct a new CadenceString using the string -"Test". - -This returns a `Task`. This is an asynchronous Task that will result -in a FlowScriptResponse when it is complete. - -Next, we need to wait for the Task to complete. Inside a Unity coroutine we can use the `WaitUntil` -function as follows: - -```csharp -yield return new WaitUntil(() => task.IsCompleted); -``` - -`WaitUntil` takes a function that returns a bool (`Func`), so we construct an anonymous one that -returns the IsCompleted field of the task. This cause Unity to pause execution of the current coroutine -until the task is completed. - -We then check to see if an error occured, and if so, log it to the console. - -```csharp -if (task.Result.Error != null) -{ - Debug.LogError($"Error: {task.Result.Error.Message}"); - yield break; -} -``` - -If there is no error, the script should have returned a Cadence Int value. We can access it as follows: - -```csharp -Debug.Log($"Script result: {Convert.FromCadence(task.Result.Value)}"); -``` - -This might be a bit confusing. The Task will have a Result. The result could contain an error, -but we checked for that earlier. If it doesn't contain an error, then it will contain a Value. - -That Value will be of type CadenceBase, which is the base type for all Cadence data types. We -know that the script returns a number, so we can convert it to an appropriate data type using Convert.FromCadence. - -## Creating an SdkAccount - -Next, let's create an account that can be used to execute transactions that mutate the state of -the blockchain. This will also demonstrate how you can use both FlowControl and the base SDK -together. - -```csharp -SdkAccount emulatorSdkAccount = FlowControl.GetSdkAccountByName("emulator_service_account"); -if (emulatorSdkAccount == null) -{ - Debug.LogError("Error getting SdkAccount for emulator_service_account"); - yield break; -} -``` - -When the emulator is started, FlowControl automatically creates an emulator_service_account FlowControl.Account -for you to use to access the built in emulator service account. We'll use that account to create a new account. - -Because the `CreateAccount` function is an SDK function, and not a FlowControl function, we'll need to create a -temporary `SdkAccount` from the FlowControl Account. The `GetSdkAccountByName` function will construct an -SdkAccount object from a FlowControl.Account object. - -If the name you pass to `FlowControl.GetSdkAccountByName` does not exist, it will return null, so we check -for that and stop execution if it fails. - -## Creating an account on the blockchain - -Now we'll use this new SdkAccount object to create a new Flow account on the emulated blockchain. - -```csharp -FlowSDK.RegisterWalletProvider(ScriptableObject.CreateInstance()); - -string authAddress = ""; -FlowSDK.GetWalletProvider().Authenticate("", (string address) => -{ - authAddress = address; -}, null); - -yield return new WaitUntil(() => { return authAddress != ""; }); - -//Convert FlowAccount to SdkAccount -SdkAccount emulatorSdkAccount = FlowControl.GetSdkAccountByAddress(authAddress); -if (emulatorSdkAccount == null) -{ - Debug.LogError("Error getting SdkAccount for emulator_service_account"); - yield break; -} - -//Create a new account with the name "User" -Task newAccountTask = CommonTransactions.CreateAccount("User"); -yield return new WaitUntil(() => newAccountTask.IsCompleted); - -if (newAccountTask.Result.Error != null) -{ - Debug.LogError($"Error creating new account: {newAccountTask.Result.Error.Message}"); - yield break; -} - -outputText.text += "DONE\n\n"; - -//Here we have an SdkAccount -SdkAccount userSdkAccount = newAccountTask.Result; - -``` -First we create and register a new `DevWalletProvider`. Any time a transaction is run, it calls the provided wallet provider. The `DevWalletProvider` -is an implementation of IWallet that shows a simulated wallet interface. It will allow you to view and authorize the submitted transaction. - -After creating and registering the wallet provider, we call `Authenticate` to display a popup that will allow you to select any of the accounts in the FlowControl -Accounts tab. You should choose emulator_service_account when prompted when running the demo. - -We then wait until the user has selected an account. - -`CommonTransactions` contains some utility functions to make performing frequent operations a little easier. -One of these is `CreateAccount`. It expects a `Name`, which is not placed on the blockchain, and the SdkAccount -that should pay for the creation of the new account. That returns a Task that is handled similarly to -before. - -If there is no error, the Result field of the task will contain the newly create account info. - -Now, in order to use this new account with FlowControl, we'll need to create a FlowControl.Account from -the SdkAccount we have. - -```csharp -FlowControl.Account userAccount = new FlowControl.Account -{ - Name = userSdkAccount.Name, - GatewayName = "Emulator", - AccountConfig = new Dictionary - { - ["Address"] = userSdkAccount.Address, - ["Private Key"] = userSdkAccount.PrivateKey - } -}; -``` - -Then we store this account in the FlowControlData object so that we can look it up by name later. - -```csharp -FlowControl.Data.Accounts.Add(userAccount); -``` - -## Deploying a contract - -The next section shows how to deploy a contract to the Flow network. Because this is another utility -function from `CommonTransactions`, it needs an SdkAccount. We'll use userSdkAccount we created earlier. - -First we need to define the contract we wish to deploy. - -```csharp -const string contractCode = @" - access(all) contract HelloWorld { - access(all) let greeting: String - - access(all) event TestEvent(field: String) - - init() { - self.greeting = ""Hello, World!"" - } - - access(all) fun hello(data: String): String { - emit TestEvent(field:data) - return self.greeting - } - }"; -``` - -We won't discuss how to write Flow contracts in depth here, but simply put this contract defines a single -function that will emit an event and return the string "Hello World!" when run. - -Then we use the same pattern we've used before to deploy this contract using the `CommonTransaction.DeployContract` -function. Note that we should register a new wallet provider since we are changing the account we want to run the transaction -as. - -```csharp -FlowSDK.GetWalletProvider().Authenticate(userAccount.Name, null, null); -Task deployContractTask = - CommonTransactions.DeployContract("HelloWorld", contractCode); - -yield return new WaitUntil(() => deployContractTask.IsCompleted); - -if (deployContractTask.Result.Error != null) -{ - Debug.LogError($"Error deploying contract: {deployContractTask.Result.Error.Message}"); - yield break; -} -``` -We'll reauthenticate with the wallet provider to tell it to use the new newly created account. Because we pass in a name this time, it -won't display the select account pop-up. - -The first argument to `DeployContract` is the contract name. This must match the name in the contract -data itself. The second argument is the Cadence code that defines the contract, and the third argument -is the SdkAccount that the contract should be deployed to. - -## Replacement text - -Next we'll see how to add a `ReplacementText` entry to FlowControl. This is typically done via the -FlowControl editor interface, but can be done programatically as shown. - -```csharp -FlowControl.TextReplacement newTextReplacement = new FlowControl.TextReplacement -{ - description = "User Address", - originalText = "%USERADDRESS%", - replacementText = userSdkAccount.Address, - active = true, - ApplyToAccounts = new List { "User" }, - ApplyToGateways = new List { "Emulator" } -}; - -FlowControl.Data.TextReplacements.Add(newTextReplacement); -``` - -Note that we are setting `ApplyToAccounts` and `ApplyToGateways` so that this `TextReplacement` will be -performed any time the FlowControl.Account account with the name "User" executes a function against the emulator. - -This new `TextReplacement` will be used when we execute a transaction using the contract we just deployed. - -## Transactions - -First we'll write the transaction we want to execute. - -```csharp -string transaction = @" - import HelloWorld from %USERADDRESS% - transaction { - prepare(acct: &Account) { - log(""Transaction Test"") - HelloWorld.hello(data:""Test Event"") - } - }"; -``` - -Based on the `TextReplacement` we created earlier, `%USERADDRESS%` will be replaced with the Flow address -of the user account we created. This will then call the `hello` function on the `HelloWorld` contract -we deployed to the user account. - -Next we follow a similar pattern to before: - -```csharp -Task transactionTask = userAccount.SubmitAndWaitUntilSealed(transaction); -yield return new WaitUntil(() => transactionTask.IsCompleted); - -if (transactionTask.Result.Error != null || !string.IsNullOrEmpty(transactionTask.Result.ErrorMessage)) -{ - Debug.LogError($"Error executing transaction: {transactionTask.Result.Error?.Message??transactionTask.Result.ErrorMessage}"); - yield break; -} -``` - -Here, we're using the `SubmitAndWaitUntilSealed` FlowControl function. This combines two SDK functions -together. It first submits the transaction to the network. Then it polls the network until the network -indicates that the transaction has been sealed and then returns the results. - -Because this is combining two operations together, there are two potential failure points. The first -is a network error or syntax error that causes the submission to be rejected. This will be indicated -in the `Result.Error` field. The second is something that goes wrong during the processing of the -transaction after submission was successful. This will be indicated in the Result.ErrorMessage field. -When using SubmitAndWaitUntilSealed or SubmitAndWaitUntilExecuted, you will want to check both of the -error fields to ensure it has completed successfully. - -Finally, we check the events emitted by the transaction. Because submitting transactions returns before -the transaction is actually processed, you can't return data directly from a transaction like you can -with a script. Instead, you emit events that can be retrieved. We'll check the events of the completed -transaction as follows: - -## Transaction Events - -```csharp -FlowEvent txEvent = transactionTask.Result.Events.Find(x => x.Type.Contains("TestEvent")); - -//Show that the transaction finished and display the value of the event that was emitted during execution. -//The Payload of the returned FlowEvent will be a CadenceComposite. We want the value associated with the -//"field" field as a string -Debug.Log($"Executed transaction. Event type: {txEvent.Type}. Event payload: {Convert.FromCadence(txEvent.Payload).field}"); -``` - -We end up a with a list of Events that were emitted by a transaction in the `Result.Events` object. We -use LINQ to find the event we're interested in. It will contain "TestEvent" in it. - -We need something to convert the Cadence TestEvent into, so we declared a C# class earlier: - -```csharp -public class TestEvent -{ - public String field; -} -``` - -Then we have to get the payload from the event to display. We'll convert it into our TestEvent class and access the ```field``` field. diff --git a/docs/tools/clients/unity-sdk/samples/ui-usage.md b/docs/tools/clients/unity-sdk/samples/ui-usage.md deleted file mode 100644 index 0b9eb8f8d7..0000000000 --- a/docs/tools/clients/unity-sdk/samples/ui-usage.md +++ /dev/null @@ -1,364 +0,0 @@ - -# Sample - FlowSDK Usage Demo - -This sample demonstrates how to call each of the APIs within the SDK. It's a simple UI containing input fields and buttons for testing out each API call, and displays information that is returned by the APIs. You will need to set up an emulator with applicable test data to use this sample. - -## Importing the Sample - -To import the sample, first add the Flow SDK to your project, then follow these steps. - -1. Open Package Manager. -2. Select the Flow SDK package. -3. On the right, expand Samples and click Import next to FlowSDK Usage Demo. - -![](../media/fd52a768f2367b763980b22d85479305.png) - -This will import the sample into a Samples folder under your Assets: - -![](../media/58d03fbcda193c672c0287da2f3f04ba.png) - -## Installing the Flow Emulator - -To install the Flow emulator, please follow the instructions for your platform here: - -[flow-cli/install](../../../../tools/flow-cli/index.md) - -Note that the Flow CLI and Flow emulator are bundled into the same command-line executable. - -To test that the Flow emulator is installed correctly, open a command line interface, type `flow` and press enter. You should see something like this: - -![](../media/be2bc6569ca5a475b3c147cf34f07c1a.png) - -## Configuring the Emulator - -The directory that you run the emulator from must contain a flow.json file. A default one can be created by running the command `flow init`, but this sample contains a flow.json file which is specific to the sample: - -![](../media/54f8a5235b61d70f09904c38ed3e5e5c.png) - -Firstly, open the scene that comes with the sample: - -![](../media/881d1efa0c8e4ff9bffe3382274a255c.png) - -Select the UI Canvas in the hierarchy: - -![](../media/82ea27a997c12eab231ad8bf2e49d335.png) - -In the inspector, scroll down to the bottom and click "Open Flow Control Window": - -![](../media/61267a392954311426b4144345f25dfd.png) - -The Flow Control Window contains all the editor tools that come with the Flow SDK. It should look like this: - -![](../media/6d33d20949b4923d1e805e8e21884935.png) - -If you see a message asking you to install the Flow emulator, then Unity has not detected your Flow installation. Click the Install button and follow the directions to install the Flow CLI \\ emulator. - -To run the emulator, we need to specify which directory to run it from, remembering that it requires a flow.json file. Click the browse button and select the folder in the sample that contains the flow.json file (it should be in **Assets/Samples/Flow SDK/\/FlowSDK Usage Demo/Scripts/Cadence**): - -![](../media/62ec9ee52b31041e7420ac7ad7132684.png) - -Click on Start Emulator. The message at the bottom of the window should say **Emulator Status: Running**. Click on Emulator Log. This will open another window showing the output of the emulator: - -![](../media/f6a8324547c8ec499a2eb68248091ba3.png) - -To test the emulator, open a command line interface and enter the command `flow blocks get latest`. You should see something like this: - -![](../media/270ed3d6ac5df815d35cc82f588a4a18.png) - -The command retrieves information about the latest block on the blockchain. The latest block is currently block height 0, meaning that there is only one block on the blockchain, the root block (hence why Parent ID is null). Now we're going to add some more blocks. - -In your file explorer, navigate to the folder that the emulator is running from. Execute the **emulator_test_data** script for your platform (**.bat** for windows, **.sh** for mac/linux). When the script has finished, go back to your command line interface and enter the command `flow blocks get latest` again. Now you should see something like this: - -![](../media/2466ac711f979da15f6ad5a6cdb6421a.png) - -There are now 11 blocks on your blockchain emulator - the latest block is at block height 10 (remember the first one was block height 0). Every block has its own ID and the ID of its parent, the previous block in the chain. - -You are now ready to run the sample app. - -## Running the FlowSDK Usage Demo - -Click the Play button in the editor. You will see the following: - -![](../media/3421ca4434716e8f2efc89bbf5f3c7ad.png) - -The buttons along the bottom display different tabs which correspond to different API calls within the SDK. The tabs demonstrate the following API calls: - -**Blocks** - -*GetById* -*GetByHeight* -*GetLatest* - -**Collections** - -*GetById* - -**Events** - -*GetForBlockHeightRange* -*GetForBlockIds* - -**Scripts** - -*ExecuteAtLatestBlock* - -**Transactions** - -*Submit* -*GetById* -*GetResult* - -**Accounts** - -*GetByAddress* - -We will now discuss each of the tests throughout the sample. As you read through each of these, it's recommended to look at the code calling these APIs. All of the code is contained here: - -![](../media/15840cee138659b172d6023791b73155.png) - -### Blocks - -Blocks are the foundation of blockchains, and Flow is no different. A Block primarily consists of two things - Collections and Block Seals. Collections contain Transactions that are yet to be executed, while Block Seals contain the results of Transactions that have been executed and verified, and are ready to be sealed. - -**Get Block by ID** - -*MainUIController.BlocksGetById()* - -Enter a valid Block ID and click the button to retrieve information about the block. - -**Get Block by Height** - -*MainUIController.BlocksGetByHeight()* - -Enter a valid Block Height to retrieve information about the block at that height. - -**Get Latest Block** - -*MainUIController.BlocksGetLatest()* - -Simply click the button to retrieve information about the latest block. - -### Collections - -A Collection contains a list of Transactions to be executed. - -**Get Collection by ID** - -*MainUIController.CollectionsGetById()* - -Enter a valid Collection ID to retrieve information about the collection. - -### Events - -Events are defined and emitted in your Smart Contracts. You would emit an event when you want to later query that something has happened. - -**Get Events for Height Range** - -*MainUIController.EventsGetForHeightRange()* - -This will retrieve any events of the given type emitted in a given block height range. Enter the Type, Start Block Height and End Block Height and click the button. - -**Get Events for Block Ids** - -*MainUIController.EventsGetForBlockIds()* - -Similar to the previous API, but instead of providing a block height range, you provide a comma separated list of Block Ids. Any events matching the Type that were emitted in those Block Ids will be returned. - -For both of the API calls, the dropdown in the bottom left will populate with a list of blocks. Selecting a block in the list will populate the right-hand side with the event results. - -#### Event Types - -For both of these API calls, the Event Type is a fully qualified type in the following format: - -``` -A.{contract address}.{contract name}.{event name} -``` - -Contract address - the account that the Smart Contract which defines the event is deployed to. -Contract name - the name of the Smart Contract which defines the event. -Event name - the name of the event as defined in the Smart Contract. - -Here is an example of an Event Type: - -``` -A.7e60df042a9c0868.FlowToken.TokensInitialized -``` - -#### Core Events - -There are a few core events that are emitted directly by Flow and don't follow the standard naming convention. These event types are: - -``` -flow.AccountCreated -flow.AccountKeyAdded -flow.AccountKeyRemoved -flow.AccountContractAdded -flow.AccountContractUpdated -flow.AccountContractRemoved -``` - -### Scripts - -Scripts are cadence code that you write and are executed on the blockchain. They can contain arguments and return values, and can interact with Smart Contracts. Scripts are read-only - they cannot mutate anything on the blockchain. Anything held in local memory is discarded when the script finishes execution. For more information on programming see [Cadence](https://cadence-lang.org/docs). - -**Execute Simple Script At Latest Block** - -*MainUIController.ExecuteSimpleScript()* - -This executes a very simple script which returns a string message. The message is printed to the UI. - -**Execute Script With Args At Latest Block** - -*MainUIController.ExecuteScriptWithArgs()* - -This demonstrates sending arguments with a simple script. Enter a number in each of the input fields and click the button. The script will add the numbers together and return the result, printing it to the UI. - -**Execute Get Tokens At Latest Block** - -*MainUIController.ExecuteGetTokens()* - -The **emulator_test_data** script that you ran earlier deployed a Smart Contract for fungible tokens called FlowSDKSampleToken. It also created a few accounts and minted some of these tokens into a couple of accounts. - -This test demonstrates passing an account address as an argument to a script, which returns a single value - the number of FlowSDKSampleTokens that account has. - -In the Flow Control Window, click the Accounts tab. This shows a few accounts which you can get the address of to test the script. - -![](../media/51d4f1e5f01dc36b0fdd0853eb08b0bd.png) - -If you would like to see the cadence script itself, it is located here: - -![](../media/f30282a8889db37965bcd49b209ac614.png) - -**Execute Print NFTs At Latest Block** - -*MainUIController.ExecutePrintNfts()* - -The **emulator_test_data** script also deployed a contract called FlowSDKSampleNFT, an example implementation of an NFT. It also minted a few of these NFTs and deposited them into a couple of accounts. - -This test is similar to the previous one - enter an account address into the input field and click the button to retrieve a list of NFTs that account owns. Instead of returning a single value, the script returns an array of values (NFT Ids). - -The cadence script for this test is in the same location as the previous test, called **print-nfts.cdc**. - -**Execute Get NFTs For Sale At Latest Block** - -*MainUIController.ExecuteGetNftsForSale()* - -This script does not have any arguments, but demonstrates returning a complex data structure - an array of structs. The struct, which has multiple properties, is defined in the script itself. - -The data being returned is a list of NFTs that are for sale, and contains the Id, price and owner's address for each NFT. Right now it will not return anything, because there are no NFTs for sale. You will need to run the **Submit Transaction List NFT** test in the Transactions tab for this script to display anything. - -The cadence script for this test is in the same location as the others, called **get-nfts-for-sale.cdc**. - -### Transactions - -Like Scripts, Transactions are cadence code that you write and are executed on the blockchain, but that is where the similarities end. The purpose of Transactions is to mutate data on the blockchain. To do this, the Transaction must be signed by the account/s that are going to be affected. The Transaction code can contain arguments, but can't return a value. This is because there is a delay for the Transaction to execute, because it has to go through the collection/consensus/execution/verification cycle. - -For more information about Transactions, see [cadence/language/transactions](https://cadence-lang.org/docs/language/transactions). - -For more information about Transaction signing, see [concepts/transaction-signing](../../../../build/basics/transactions.md#signing-a-transaction). - -For more information about Cadence programming, see [Cadence](https://cadence-lang.org/docs). - -**Signing In** - -To sign transactions, you must sign-in to a wallet provider. This sample uses DevWallet, an example wallet provider specifically made for development. For more information see [DevWallet](../guides/dev-wallet.md). - -Click the Sign In button. This will open a dialog asking to select an account to sign in with. The list of accounts is populated from the Accounts tab of the Flow Control Window. Select an account and click Ok. The address of the account you select will show underneath "Signed in as". You can now run the three transaction tests. - -**Submit Transaction** - -*MainUIController.SubmitTxSinglePayerProposerAuthorizer()* - -This is a very simple Transaction that logs out a message on the blockchain. Simply click the first Submit Transaction button on the left. Successfully submitting a Transaction will return a Transaction Id. - -Now go to the Flow Emulator Output and scroll to the bottom. You should see several things - the transaction was submitted, executed, the log message in the cadence code was printed, and a new block was committed. - -![](../media/db2741c083bc677b767e5f2973f63e1b.png) - -**Submit Transaction With Args** - -*MainUIController.SubmitTxWithArgs()* - -This is a very simple Transaction like the previous one which doesn't actually mutate anything, but simply demonstrates passing arguments to the Transaction. Enter a string argument and a number argument, then click the next Submit Transaction button. Like the previous test, the arguments are logged out on the blockchain. - -![](../media/f0dfb75d21cf7462d7f0aaf199d97b27.png) - -**Submit Transaction List NFT** - -*MainUIController.SubmitTxListNft()* - -This Transaction demonstrates an actual use case of listing an account's NFT for sale. Enter the NFT Id and price, then click the last Submit Transaction button. To get a list of NFT Ids for a given account, go back to the Scripts tab and run the "Execute Print NFTs At Latest Block" script. - -Note: The NFT Price must be a decimal number, eg 23.5 or 12.0 - -If you'd like to see the cadence code for this Transaction, it's located here: - -![](../media/5ba73beddcefba8eade5c9ce4322a86d.png) - -**Get Transaction By Id** - -*MainUIController.GetTransactionById()* - -This API call will return all of the information that was submitted with the Transaction. For ease of use, the input field for this is automatically populated with the Transaction Id of the previous Transaction that was submitted. You can of course enter a different valid Transaction Id. Clicking the button will populate all the fields under the Transaction heading. - -**Get Transaction Result** - -*MainUIController.GetTransactionResult()* - -This demonstrates how to retrieve the result of a Transaction. You must supply the Transaction Id using the same input as the previous test. As previously mentioned, the input is automatically populated with the Transaction Id of the last submitted Transaction. - -The fields under the Result heading are populated when running this API call. The following information is returned. - -Status: Current status of the Transaction. Can be Unknown, Pending, Finalized, Executed, Sealed or Expired. -Error Message: If there was an error executing the Transaction (eg a cadence runtime error), this will be populated with the error message from the blockchain. When running against an emulator, you will also see the error in the Flow Emulator Output. -Events: A list of events that were emitted during execution of the Transaction. - -### Accounts - -**Signing In** - -Like the Transactions tab, you must be signed in to a wallet provider to run most of these tests. That's because they are actually transactions, and therefore require signing. If you already signed in on the Transactions tab, you will still be signed in as the same user here. Otherwise, Click Sign In and select an account, then click Ok. - -**Get Account** - -*MainUIController.GetAccount()* - -Retrieves details about the given account address, including Flow token balance, public keys and any deployed Smart Contracts. This is not a transaction, therefore you do not need to be signed in to run this test. - -**Create Account** - -*MainUIController.CreateAccount()* - -To create an account on Flow, it needs to be sponsored by another account. Every account on Flow must have a minimum of 0.001 Flow tokens in their balance, so the sponsor must pay this to the new account. To test this, enter a name for the new account, then click Create Account. The account you are signed in as will sign the transaction and pay the 0.001 Flow tokens to the new account. If it fails, ensure the account has enough Flow tokens to execute this transaction and still retain the minimum 0.001 Flow tokens. - -A new public\\private keypair will be generated and assigned to the new account. - -Note: the Account Name is not actually stored on the blockchain. It is only used by the SDK. - -**Deploy Contract** - -*MainUIController.DeployContract()* - -This demonstrates deploying a Smart Contract to an account. Enter the name of the contract and the path of its source file, then click Deploy Contract. - -Note that the Contract Name must match the name of the contract in the source file. - -The Contract Source File location is relative to the project's Assets directory. - -**Remove Contract** - -*MainUIController.RemoveContract()* - -Removes a Smart Contract from an account. Enter the name of the contract then click Remove Contract. - -**Update Contract** - -*MainUIController.UpdateContract()* - -Updates a Smart Contract which is already deployed to the signed-in account. Unlike Deploy Contract, the `init()` function of the contract is not executed. Enter the Contract Name and the path of the updated source file, then click Update Contract. - -Note that the Contract Name must match the name of the contract in the source file, and must also match the name of the existing contract deployed to the account. - -The Contract Source File location is relative to the project's Assets directory. - -Only certain things in a Smart Contract can be modified. See this link about [Contract Updatability](https://cadence-lang.org/docs/language/contract-updatability). diff --git a/src/data/tools/index.ts b/src/data/tools/index.ts index 0e1acd2581..f840eba38d 100644 --- a/src/data/tools/index.ts +++ b/src/data/tools/index.ts @@ -629,5 +629,4 @@ export { niftoryTool, flowviewTool, dapperSelfCustodyWallet, - unitySDK, }; diff --git a/vercel.json b/vercel.json index dfa1440c75..28e4a74ab3 100644 --- a/vercel.json +++ b/vercel.json @@ -252,11 +252,6 @@ "destination": "/tools/clients/flow-go-sdk", "permanent": true }, - { - "source": "/tools/unity-sdk/:path*", - "destination": "/tools/clients/unity-sdk/:path*", - "permanent": true - }, { "source": "/account-linking/:path*", "destination": "/build/advanced-concepts/account-linking/:path*", @@ -692,11 +687,6 @@ "destination": "/tools/clients/flow-go-sdk/:path*", "permanent": true }, - { - "source": "/tooling/unity-sdk/:path*", - "destination": "/tools/clients/unity-sdk/:path*", - "permanent": true - }, { "source": "/tooling/:path*", "destination": "/tools/:path*",