diff --git a/docs/unity/calling-contracts.md b/docs/unity/calling-contracts.md index fd6782dfe..f28e62dce 100644 --- a/docs/unity/calling-contracts.md +++ b/docs/unity/calling-contracts.md @@ -89,7 +89,7 @@ Micheline primitives include: - Strings, as in `new MichelineString("Hello")` - Bytes, as in `new MichelineBytes(bytes")` -As described in [Complex data types](/smart-contracts/data-types/conplex-data-types), Micheline values are organized as a series of nested pairs in tree and comb formats. +As described in [Complex data types](/smart-contracts/data-types/complex-data-types), Micheline values are organized as a series of nested pairs in tree and comb formats. For example, if an entrypoint accepts an integer, a string, and a series of bytes as a nested pair, you can format the parameter like this: ```csharp diff --git a/docs/unity/managing-contracts.md b/docs/unity/managing-contracts.md deleted file mode 100644 index 22897ff4d..000000000 --- a/docs/unity/managing-contracts.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -title: Managing contracts -authors: Tim McMackin -last_update: - date: 18 December 2023 ---- - -Smart contracts are backend programs that run on the Tezos blockchains. -Smart contracts can do many tasks, but for gaming they have two main purposes: - -- They handle tokens, which are digital assets stored on the blockchain -- They provide backend logic that users can trust because it cannot change - -For more information about contracts, see [Smart contracts](/smart-contracts). - -You can create your own smart contracts or use the built-in contract that the SDK provides for managing tokens in Unity projects. - -The Contract tutorial scene shows how to deploy a copy of the built-in contract from a Unity project. - -## The built-in contract - -The SDK includes a built-in contract that you can use to manage tokens for your Unity projects. - -The contract has entrypoints that allow you to create and transfer tokens. -See [Managing tokens](/unity/managing-tokens). - -The Michelson source code of the built-in contract is in the `Resources/Contracts` folder of the SDK, but it isn't very human-readable. -For an example of a deployed contract, see https://ghostnet.tzkt.io/KT1Nhr9Bmhy7kcUmezRxbbDybh5buNnrVLTY/entrypoints. - -## Deploying the built-in contract - -To deploy the built-in contract, call the `TokenContract.Deploy() method and pass a callback function: - -```csharp -public void DeployContract() -{ - TezosManager - .Instance - .Tezos - .TokenContract - .Deploy(OnContractDeployed); -} - -private void OnContractDeployed(string contractAddress) -{ - Debug.Log(contractAddress); -} -``` - -The project sends the deployment transaction to the connected wallet, which must approve the transaction and pay the related fees. -The callback function receives the address of the deployed contract, which the project uses to send requests to the contract. -It can take a few minutes for the contract to deploy and be confirmed in multiple blocks on the blockchain. - -The address that deployed the contract becomes the administrator of the contract and is the only account that can create tokens. - -The SDK provides information about the contract such as its address in the `TokenContract` object. -You can use block explorers such as [Better Call Dev](https://better-call.dev/) to see information about the deployed contract. - -For information about using the built-in contract, see [Managing tokens](/unity/managing-tokens). - -## Getting the contract address - -When you deploy a contract with the `TokenContract.Deploy()` method, the SDK saves the contract address by running this code: - -```csharp -PlayerPrefs.SetString("CurrentContract:" + Tezos.Wallet.GetActiveAddress(), contractAddress); -``` - -Then during SDK initialization, the SDK saves the address to the `TokenContract.Address` property. - -To retrieve the address of contracts that you haven't deployed through the project, you can use the [`API.GetOriginatedContractsForOwner()`](/unity/reference/API#getoriginatedcontractsforowner) method. - -## Calling the built-in contract - -The built-in contract has convenience methods for minting and transferring tokens; see [Managing tokens](/unity/managing-tokens). - -To call the contract's other entrypoints, use the `Wallet.CallContract()` method. -For example, to call the contract's `set_administrator` entrypoint to set a new administrator account, use this code: - -```csharp -TezosManager.Instance.Tezos.Wallet.CallContract( - contractAddress: TezosManager.Instance.Tezos.TokenContract.Address, - entryPoint: "set_administrator", - input: new MichelineString(newAdminAddress).ToJson() -); -``` - -For information about the entrypoints in the built-in contract, see Unity SDK TokenContract object. - -You can call any other contract by using its address, entrypoint name, and parameter value, as in this example: - -```csharp -TezosManager.Instance.Tezos.Wallet.CallContract( - contractAddress: address, - entryPoint: entryPointName, - input: new MichelineInt(12).ToJson() -); -``` - -This example passes the value 12 to the entrypoint in the variable `entryPointName`. - -Note that the parameter is encoded as a Michelson value. -For information about encoding more complex parameters, see [Encoding parameters](#encoding-parameters). - -To get the hash of the transaction, use the `ContractCallCompleted` event. - -## Calling other contracts - -To call any other contract, see [Calling contracts](/unity/calling-contracts). - -## Calling views - -To call a view, pass the address of the contract, the name of the view, and the Michelson-encoded parameter to the `TezosAPI.ReadView()` method. -You must set the return type on the `TezosAPI.ReadView()` method, as in this example for a view that returns a string: - -```csharp -var result = await TezosAPI.ReadView("KT1K46vZTMEe8bnacFvFQfgHtNDKniEauRMJ", "simple", "\"String value\""); -Debug.Log("View response: " + result); -``` - -## Encoding parameters - -When you call contract entrypoints or views with the `Wallet.CallContract()` or `Wallet.ReadView()` methods, you must encode the parameter as a Micheline value. -For example, if an entrypoint accepts two integers and one string as parameters, you must pass a list of two `Netezos.Encoding.MichelineInt` values and one `Netezos.Encoding.MichelineString` value: - -```csharp -var input = new MichelinePrim -{ - Prim = PrimType.Pair, - Args = new List - { - new MichelineInt(1), - new MichelineInt(2), - new MichelineString("My string value") - } -}.ToJson(); - -TezosManager.Instance.Tezos.Wallet.CallContract( - contractAddress: address, - entryPoint: entryPointName, - input: input -); -``` - -You can also use the value of the parameters in Michelson JSON. -The previous example looks like this with a JSON parameter value: - -```csharp -var input = @"{ - ""prim"": ""Pair"", - ""args"": [ - { - ""int"": ""1"" - }, - { - ""prim"": ""Pair"", - ""args"": [ - { - ""int"": ""2"" - }, - { - ""string"": ""My string value"" - } - ] - } - ] -}"; - -TezosManager.Instance.Tezos.Wallet.CallContract( - contractAddress: address, - entryPoint: entryPointName, - input: input -); -``` - -Some block explorers allow you to fill in parameter values for an entrypoint and then download the Michelson JSON to use in your code. - -You can build more complex parameters out of `Netezos.Encoding` objects. -For example, the built-in contract has an entrypoint named `update_operators`, which is an FA2 standard entrypoint that gives an account control over another account's tokens. -It accepts a list of changes to make to token operators, each including these fields in this order: - -- Either "add_operator" or "remove_operator" -- The address of the operator, which will be able to control the owner's tokens of the specified ID. -- The ID of the token -- The address of the token owner - -This is the Michelson parameter type for the endpoint: - -``` -(list %update_operators (or - (pair %add_operator (address %owner) - (pair (address %operator) - (nat %token_id))) - (pair %remove_operator (address %owner) - (pair - (address %operator) - (nat %token_id))))) -``` - -In this case, you set the "add_operator" value by passing a `PrimType.Left` Michelson primitive or the "remove_operator" value by passing a `PrimType.Right` primitive. -The values in the primitive are a series of nested pairs called a [right comb](/smart-contracts/data-types/complex-data-types#right-combs), as in this example: - -```json -{ - "prim": "LEFT", - "args": [ - { - "prim": "Pair", - "args": [ - { - "string": "tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx" - }, - { - "prim": "Pair", - "args": [ - { - "string": "tz1hQKqRPHmxET8du3fNACGyCG8kZRsXm2zD" - }, - { - "int": "1" - } - ] - } - ] - } - ] -} -``` - -The code to create a list of these elements to pass to the entrypoint looks like this: - -```csharp -var operatorAddress = "tz1hQKqRPHmxET8du3fNACGyCG8kZRsXm2zD"; -var ownerAddress = "tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx"; -int[] tokenIds = {1, 2, 3}; - -var operatorArray = new MichelineArray(); -foreach (var tokenId in tokenIds) -{ - operatorArray.Add(new MichelinePrim - { - Prim = PrimType.Left, - Args = new List - { - new MichelinePrim - { - Prim = PrimType.Pair, - Args = new List - { - new MichelineString(ownerAddress), - new MichelinePrim - { - Prim = PrimType.Pair, - Args = new List - { - new MichelineString(operatorAddress), - new MichelineInt(tokenId) - } - } - - } - } - } - }); -}; - -TezosManager.Instance.Tezos.Wallet.CallContract( - contractAddress: address, - entryPoint: "update_operators", - input: operatorArray.ToJson() -); -``` - -## Deploying other contracts - -To deploy a contract from Unity, you must compile the contract to Michelson in JSON format. -For example, to compile a contract in LIGO to Michelson JSON, run this code: - -```bash -ligo compile contract MyContract.jsligo \ - -m MyContract -o MyContract.json --michelson-format json -``` - -Then, ensure that the code of the code and initial storage value of the contract are wrapped in `code` and `storage` fields at the root of the file, as in this example: - -```json -{ - "code": [ - { - "prim": "parameter", - "args": [ - ... - }, - ], - "storage": { - "int": "0" - } -} -``` - -To deploy the contract from the Unity project, use the `Wallet.OriginateContract()` method, as in this example: - -```csharp -var contractJSON = Resources.Load("Contracts/MyContract").text; -TezosManager.Instance.Tezos.Wallet.OriginateContract(contractJSON); -``` - -To get the address of the deployed contract, use the `ContractCallCompleted` event. - - - diff --git a/docs/unity/managing-tokens.md b/docs/unity/managing-tokens.md index e18f06914..ab8871ba7 100644 --- a/docs/unity/managing-tokens.md +++ b/docs/unity/managing-tokens.md @@ -2,10 +2,10 @@ title: Managing tokens authors: Tim McMackin last_update: - date: 11 January 2024 + date: 12 November 2024 --- -The SDK's built-in contract is compatible with the [FA2 token standard](/architecture/tokens/FA2), which means that you can use a single smart contract to manage any number of types of tokens, including: +Tezos supports a variety of types of tokens, including: - Fungible tokens, which are collections of interchangeable tokens with a quantity that you define. Fungible tokens can be quantifiable commodities like in-game currency, fuel, ammunition, or energy, or they can be identical items with a limited quantity. @@ -14,141 +14,102 @@ Games use NFTs for items that are unique and must not be duplicated. You can create as many tokens and types of tokens as you need in one contract, but each transaction to create or transfer tokens incurs fees. -## Creating (minting) tokens +For more information about tokens, see [Tokens](/architecture/tokens). -To create a token type, call the contract's `mint` entrypoint and pass these parameters: +## FA2 tokens -- A callback function to run when the token is created -- The metadata for the token, which includes a name and description, URIs to preview media or thumbnails, and how many decimal places the token can be broken into -- The destination account that owns the new tokens, which can be a user account, this smart contract, or any other smart contract -- The number of tokens to create +While you can create tokens that behave in any way that you want them to behave, it's best to create tokens that follow a standard. +The Tezos [FA standards](/architecture/tokens#token-standards) enforce a standard format for tokens which allows applications like games, wallets, and block explorers to work with them in a consistent way. +For example, if you create an FA-compatible token and use it in a Unity application, players can look up information about their tokens in block explorers and transfer them with their wallets without interacting with the Unity application. -For example, this code creates a token type with a quantity of 100: +For this reason, Unity applications should use FA tokens whenever possible. -```csharp -var initialOwner = TezosManager - .Instance - .Wallet - .GetActiveAddress(); +The most popular and commonly-supported FA standard is [FA2](/architecture/tokens/FA2), so the examples on this page are for working with FA2 tokens. +FA2 tokens can be fungible tokens or non-fungible tokens, which makes the standard flexible enough for most uses of tokens. -const string imageAddress = "ipfs://QmX4t8ikQgjvLdqTtL51v6iVun9tNE7y7Txiw4piGQVNgK"; +## FA2 token contracts -var tokenMetadata = new TokenMetadata -{ - Name = "My token", - Description = "Description for my token", - Symbol = "MYTOKEN", - Decimals = "0", - DisplayUri = imageAddress, - ArtifactUri = imageAddress, - ThumbnailUri = imageAddress -}; +To create and work with FA2 tokens you must deploy an FA2-compatible smart contract. +For examples of FA2 contracts, see [Sample smart contracts](/smart-contracts/samples). +You can also use the tutorial [Create a fungible token with the SmartPy FA2 library](/smart-contracts/samples) to walk through the process of creating, customizing, and deploying an FA2 contract. -TezosManager - .Instance - .Tezos - .TokenContract - .Mint( - completedCallback: OnTokenMinted, - tokenMetadata: tokenMetadata, - destination: initialOwner, - amount: 100); - -private void OnTokenMinted(TokenBalance tokenBalance) -{ - Debug.Log($"Successfully minted token with Token ID {tokenBalance.TokenId}"); -} -``` +:::note + +The rest of this page assumes that you are using an FA2 contract. + +::: + +:::note + +You can use block explorers for help formatting the parameters for contract calls. +See [Encoding parameters as JSON strings](/unity/calling-contracts#encoding-parameters-as-json-strings). + +::: + +## Creating (minting) a new token type + +To create a new type of token and mint tokens, pass the metadata for the new token type, the number of tokens to mint, and the initial owner of the new tokens to the FA2 contract's `mint` entrypoint. +The FA2 standard does not require contracts to have a `mint` entrypoint, but most do. +If a contract does not have a `mint` entrypoint, it was created with all of the tokens that it will ever have and therefore no more tokens can be minted. + +TODO -For a complete example of creating tokens, see the file `TezosSDK/Examples/Contract/Scripts/MintToken.cs` and the Contract tutorial scene. -## Transferring tokens -To transfer tokens, call the contract's `Transfer` entrypoint and pass these parameters: -- A callback function to run when the transfer is complete -- The account to transfer the tokens to -- The ID of the token -- The amount of tokens to transfer -This example transfers 12 tokens with the ID 5 to the account in the variable `destinationAccountAddress`. +## Creating (minting) tokens of an existing type + +To mint tokens of an existing type, pass the token ID, the number of tokens to create, and the initial owner of the new tokens to the contract's `mint` entrypoint. +This example mints 5 tokens of the token type with the ID 7: ```csharp -public void HandleTransfer() -{ - TezosManager - .Instance - .Tezos - .TokenContract - .Transfer( - completedCallback: TransferCompleted, - destination: destinationAccountAddress, - tokenId: 5, - amount: 12); -} - -private void TransferCompleted(string txHash) +var mintJsonString = "{ \"prim\": \"Pair\", \"args\": [ { \"prim\": \"Pair\", \"args\": [ { \"string\": \"tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx\" }, { \"int\": \"5\" } ] }, { \"prim\": \"Pair\", \"args\": [ [], { \"int\": \"7\" } ] } ] }"; + +var mintTokensRequest = new OperationRequest { - Logger.LogDebug($"Transfer complete with transaction hash {txHash}"); -} + Destination = "KT1Nhr9Bmhy7kcUmezRxbbDybh5buNnrVLTY", + EntryPoint = "mint", + Arg = mintJsonString, + Amount = "0", +}; + +var response = await TezosAPI.RequestOperation(mintTokensRequest); ``` -For a complete example, see the Transfer tutorial scene. +## Transferring tokens -## Getting token balances +To transfer tokens, pass the source account, target account, token ID, and quantity to the contract's `transfer` entrypoint. +The account that sends the transfer call must be the owner or operator of the tokens. +For more information about token access control, see [FA2 tokens](/architecture/tokens/FA2). -To get the tokens that the connected account owns, call the [`API.GetTokensForOwner()`](/unity/reference/API#gettokensforowner) method in a coroutine. -This example prints information about the tokens that the account owns to the log: +This example transfers 2 tokens with the ID 7: ```csharp -private void Start() -{ - // Subscribe to account connection event - TezosManager.Instance.EventManager.WalletConnected += OnWalletConnected; -} +var transferTokensString = "[ { \"prim\": \"Pair\", \"args\": [ { \"string\": \"tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx\" }, [ { \"prim\": \"Pair\", \"args\": [ { \"string\": \"tz1hQKqRPHmxET8du3fNACGyCG8kZRsXm2zD\" }, { \"prim\": \"Pair\", \"args\": [ { \"int\": \"7\" }, { \"int\": \"2\" } ] } ] } ] ] } ]"; -private void OnWalletConnected(WalletInfo walletInfo) +var transferTokensRequest = new OperationRequest { - // Address of the connected wallet - var address = walletInfo.Address; + Destination = "KT1Nhr9Bmhy7kcUmezRxbbDybh5buNnrVLTY", + EntryPoint = "transfer", + Arg = transferTokensString, + Amount = "0", +}; - // Prepare the coroutine to fetch the tokens - var routine = TezosManager.Instance.Tezos.API.GetTokensForOwner( - OnTokensFetched, // Callback to be called when the tokens are fetched - address, true, 10_000, new TokensForOwnerOrder.ByLastTimeAsc(0)); +var response = await TezosAPI.RequestOperation(transferTokensRequest); +``` - StartCoroutine(routine); -} + ## Destroying (burning) tokens -The built-in contract does not have a burn entrypoint, so you can't destroy its tokens. -If you want to make tokens unusable, send them to an address that doesn't exist or to an account that you can't use. +The FA2 standard does not have a standard way of burning tokens. +Some FA2 implementations have a `burn` entrypoint. +In other cases, if you want to make tokens unusable, send them to an address that doesn't exist or to an account that you can't use. For example, you can create an account in a wallet app, send the tokens to it, and delete the private key for the account.