What is Contained in this Document

In this document you will find Integration examples for popular Unity wallets such as Metamask and Metafab.

MetaMask Integration

If you want to use MetaMask with String, you will need to know:

  1. How to set up MetaMask in your project
  2. How to Initialize MetaMask and connect to the players wallet
  3. How to request a transaction signature from the player using MetaMask

Please refer to the official MetaMask Unity documentation for further detail on how to use it. We will succinctly go over the areas of MetaMask Unity implementation which overlap with using String in Unity. This document presumes that the reader has already familiarized themselves with the Integration Steps section of this documentation.

Prerequisites

You will need to install the MetaMask Unity SDK in your project. You will also need the MetaMaskUnity object and the MetaMaskTransportBroadcaster with the QR Code listener in your scene - as shown in their example.

using Cysharp.Threading.Tasks;
using StringSDK;
using MetaMask.Unity;
using MetaMask.Models;
using System.Text;

This example uses StringSDK, MetaMask, a C# Promises package and System Text for Base64 Decryption.

Initialization

Your initialization might look something like this:

public class GameLogicBehavior : MonoBehavior
{
    // Not required, but used for brevity
    private MetaMask.MetaMaskWallet wallet;

    // We will use this later to execute the blockchain actions
    private TransactionRequest lastQuote;

    // We will use this later to store the execution response
    private TransactionResponse txResponse;

    // We will use this to keep track of the String internal ID for the player
    private string stringPlayerID;

    async UniTaskVoid Start() 
    {
        // Initialize the String SDK with your API key
        StringXYZ.ApiKey = "YOUR_API_KEY_HERE";

        // Initialize MetaMask, display QR code and subscribe to events
        MetaMaskUnity.Instance.Initialize();
        wallet = MetaMaskUnity.Instance.Wallet;
        wallet.Connect();
        wallet.WalletConnected += OnWalletConnected;
        wallet.WalletAuthorized += OnWalletAuthorized;
    }
    // ...
}

Notice how the Start() method initializes MetaMaskUnity, gets a handle to its wallet, initializes a connection to the wallet, then subscribes to some events that the wallet emits.

Logging the Player In

Your login code might look something like this:

public async void LoginPlayerToString()
{
    // Request String login payload for the player to sign
    var payloadToSign = await StringXYZ.RequestLogin(wallet.SelectedAddress);

    // To display the payload in human readable form in MetaMask, we need to decode it
    var base64decode = Encoding.UTF8.GetString(Convert.FromBase64String(payloadToSign.nonce));

    // Construct a signature request for MetaMask using our login payload
    var parameters = new string[] { wallet.SelectedAddress, base64decode };
    var request = new MetaMaskEthereumRequest
    {
        Method = "personal_sign",
        Parameters = parameters
    };

    // Request the signature and store the string of the JSON response
    var jsonResponse = await wallet.Request(request);
    var signature = jsonResponse.GetString();

    // Construct the String login request using the signature
    var login = new LoginRequest(
        nonce: payloadToSign.nonce,
        signature: signature
    );

    // Log the player in - if they do not have an account one will be created automatically
    var auth = await StringXYZ.Login(login);
  
    // Store the String player ID for future use
    stringPlayerID = auth.user.id;

    // Set the users name and email with String.  See VerifyUser()
    if (auth.user.status == "unverified")
    {
        VerifyUser(); 
    }
}

Please note the use of wallet.SelectedAddress which denotes the players public key, as well as the construction of the "personal_sign" MetaMaskEthereumRequest along with its execution and response. It's a good idea to store the String Player ID (auth.user.id) for future reference.

That's it

That's it! Accessing the public key (address) of the player and signing a message are all that's required of any wallet service to interact with String.

Metafab Integration

If you want to use Metafab with String, you will need to know:

  1. How to set up Metafab in your project
  2. How to Initialize Metafab and connect to the players wallet
  3. How to sign a transaction on behalf of the player using Metafab

Please refer to the official Metafab Unity documentation for further detail on how to use it. We will succinctly go over the areas of Metafab Unity implementation which overlap with using String in Unity. This document presumes that the reader has already familiarized themselves with the Integration Steps section of this documentation.

Prerequisites

You will need to install the Metafab Unity SDK in your project. You will also need the Cysharp Threading package installed in your project, which String Unity also uses.

using Cysharp.Threading.Tasks;
using StringSDK;
using MetafabSDK;
using System.Text;

This example uses StringSDK, MetaFab, a C# Promises package and System Text for Base64 Decryption.

Initialization

Your initialization might look something like this:

public class GameLogicBehavior : MonoBehavior
{
    // These Metafab vars will be passed into our String package
    private string playerWallet;
    private string playerWalletID;

    // We will use this later to execute the blockchain actions
    private TransactionRequest lastQuote;

    // We will use this later to store the execution response
    private TransactionResponse txResponse;

    // We will use this internal ID to submit the players information to String
    private string stringPlayerID;

    async UniTaskVoid Start() 
    {
        // Authenticate the Game with Metafab
        var response = await Metafab.GamesApi.AuthGame(Config.Email, Config.Password, default);

        // Metafab needs us to copy some of the response data
        Config.PublishedKey = response.publishedKey;
        Metafab.PublishedKey = Config.PublishedKey;
        Metafab.SecretKey = response.secretKey;
        Metafab.Password = Config.Password;

        // Log player into game with their credentials
        var auth = await Metafab.PlayersApi.AuthPlayer("sample-player", "password", 0);

        // Store the information we need about the player to be passed into our String package
        playerWallet = auth.wallet.address;
        playerWalletID = auth.wallet.id;

        // Metafab uses this header in order to create cryptographic signatures on behalf of the player
        Metafab.WalletDecryptKey = auth.walletDecryptKey;
    }
// ...
}

Please note the use of auth.wallet.address which contains the public key of the players Metafab wallet, as well as its internal Metafab ID auth.wallet.id which will be used later in conjunction with auth.walletDecryptKey to sign a transaction on their behalf.

Logging the Player In

Your login code might look something like this:

public async void LoginPlayerToString()
{
    // Request String login payload for the player to sign
    var payloadToSign = await StringXYZ.RequestLogin(playerWallet);

    // To display the payload in human readable form in MetaMask, we need to decode it
    var base64decode = Encoding.UTF8.GetString(Convert.FromBase64String(payloadToSign.nonce));

    // Construct a signature request for MetaFab using our login payload
    var request = new CreateWalletSignatureRequest(base64decode);

    // Request the signature and store the string of the JSON response
    var res = await Metafab.WalletsApi.CreateWalletSignature(playerWalletID, request);
    var signature = res.signature;

    // Construct the String login request using the signature
    var login = new LoginRequest(
        nonce: payloadToSign.nonce,
        signature: signature
    );

    // Log the player in - if they do not have an account one will be created automatically
    var auth = await StringXYZ.Login(login);

    // Store the String player ID for future use
    stringPlayerID = auth.user.id;

    // Set the users name and email with String.  See VerifyUser()
    if (auth.user.status == "unverified")
    {
        VerifyUser(); 
    }
}

The Wallet Signature Request is created by passing in the decoded String login payload nonce. Then the signature is created on behalf of the user by calling Metafab.WalletsApi.CreateWalletSignature().

That's it!

That's it. Accessing the public key (address) of the players Metafab wallet and signing the login payload are all that are required to use Metafab with String.

Transaction Request Examples

The TransactionRequest is an object which you will construct according to your specific needs, and the desired operation on the blockchain you wish to execute. We will provide a couple examples to demonstrate what they might look like. Check out the Appendices section of this Implementation Guide for an in-depth explanation of each parameter in the TransactionRequest.

Mint an NFT on Fuji Testnet using mintTo(address)

        TransactionRequest transactionRequest = new TransactionRequest(
            userAddress: walletAddr,
            assetName: "String Avalanche NFT",
            chainId: 43113,
            contractAddress: "0xea1ffe2cf6630a20e1ba397e95358daf362c8781",
            contractFunction: "mintTo(address)",
            contractReturn: "uint256",
            contractParameters: new string[] { walletAddr },
            txValue: "0.08 eth",
            gasLimit: "800000");

Here, we are specifying the Chain ID of the Fuji Testnet (aka Avalanche Testnet). The contract is located at 0xea1ffe2cf6630a20e1ba397e95358daf362c8781, and its mint function mintTo(address) takes in a single address parameter and it returns a uint256. It costs 0.08 AVAX (the native network token) to mint. We set the gas limit high just to be safe. We will likely not be anywhere near this limit in terms of the actual gas cost charged.

Mint an NFT on Ethereum Mainnet using mintTo(address[])

TransactionRequest transactionRequest = new TransactionRequest(
	userAddress: walletAddr,
  assetName: "String Ethereum NFT",
	chainID: 1,
	contractAddress: "0xB2153Ef7d90bb03637855E2f96e83e30EA73f940",
	contractFunction: "mintTo(address[])",
	contractReturn: "uint256[]",
	contractParameters: new string[] {$"[{walletAddr},{walletAddr},{walletAddr}]"},
	txValue: "0.24 eth",
	gasLimit: "800000"
);

Notice how this smart contract takes in an array of addresses as its first and only parameter for the mintTo method. In order to specify this, we construct a string representing an array as the first (and only) index of the contractParameters string array. We also multiplied the txValue because each token costs 0.08 eth to mint.


What’s Next