Using Wallets with Eidolon
The IWallet
interface is used universally between Desktop, Mobile and WebGL builds. The only thing that differs is their constructors and recommended usage flow. Below we dive into how the interface is used for each platform!
When building for WebGL
, the WebGLWallet
class is compatible with most popular browser wallets following the JSON-RPC
standard. It works seamlessly with wallets like MetaMask
, TrustWallet
, Enkrypt
, and many others, providing users with a wide range of wallet options for interacting with your Unity-based blockchain game.
Constructor
The WebGLWallet
class has a constructor that initializes a new instance. You can use this constructor to create instances of wallet providers for different blockchains. Both single-chain and multi-chain constructors are dynamic and work even if mixed together in the same script. Feel free to creatively use this to create awesome experiences.
Single-Chain Constructor
// You can provide an empty constructor to have the wallet inherit the RPC set with the Project Settings.WebGLWallet myWallet = new WebGLWallet()
Example:
// Reference to your contractWebGLWallet wallet;
public void Awake(){ // Initialize an instance of the WebGLWallet class WebGLWallet wallet = new WebGLWallet();}
Once your wallet instance has been instantiated, you can use it to invoke functions like Connect
, SwitchChains
, SendTransaction
and so much more!
Multi-Chain Constructor
// Alternatively you can override the default RPC to instantiate a Wallet instance on a different network
// Custom RPC ProviderJsonRpcProvider provider = new JsonRpcProvider(" customRpcHere")
WebGLWallet myWallet = WebGLWallet(provider);
provider
(JsonRpcProvider): The RPC (Remote Procedure Call) endpoint URL of the blockchain network.
Example:
// Reference to your walletsWebGLWallet ethereumWallet;WebGLWallet skaleWallet;
public void Awake(){ // Create new JsonRpcProvider instances to be used for the differen networks. JsonRpcProvider ethRpc = new JsonRpcProvider("EthRpcHere"); JsonRpcProvider skaleRpc = new JsonRpcProvider("SkaleRpcHere");
// All of the below instances will use the RPC passed in the constructor.
// Initialize an instance of your wallet on the Ethereum network ethereumWallet = new WebGLWallet(ethRpc);
// Initialize an instance of your wallet on the SKALE network skaleWallet = new WebGLWallet(skaleRpc);
// Let's use our instances
// Use your Ethereum wallet instance to connect to the users default browser wallet to Ethereum string account = ethereumWallet.Connect("1");
// Debug the account (optional) Debug.Log("Connected Account: " + account);}
With the multi-chain constructor, you can connect to different blockchains and interact with smart contracts on those networks. It allows you to set a default network using the Project Setup window, whilst still allowing you to interact with contracts on other networks.
Methods
After instantiating your WebGLWallet
instance, you can utilize any of the below wallet methods anywhere in your Unity project.
Connect
Connect(string chainId)
Method Description
Connects the wallet provider to a specific Ethereum network using the provided chain ID. THis function will return the connected public address.
Code Example
string chainId = "1"; // Main Ethereum Network (Change to the desired chain ID)string account = wallet.Connect(chainId);
Debug.Log("Connected Account: " + account);
Parameter Explanation
chainId
(string): The chain ID of the target network.
Connect (Overload)
Connect(string chainId, string rpcUrl, string chainName, string nativeCurrencyName, string nativeCurrencySymbol, string nativeCurrencyDecimals, string blockExplorer)
Method Description
Connects the wallet provider to a specific network, adding the network if it’s not available on the users browser wallet. This function will return the connected public address.
Code Example
string chainId = "1351057110"; // SKALE Calypso Test Network (Change to the desired chain ID)string rpcUrl = "https://testnet.skalenodes.com/v1/giant-half-dual-testnet"; // Replace with your RPC URLstring chainName = "SKALE Calypso Testnet";string nativeCurrencyName = "sFUEL";string nativeCurrencySymbol = "sFUEL";string nativeCurrencyDecimals = "18";string blockExplorer = "https://giant-half-dual-testnet.explorer.testnet.skalenodes.com/";
string account = wallet.Connect(chainId, rpcUrl, chainName, nativeCurrencyName, nativeCurrencySymbol, nativeCurrencyDecimals, blockExplorer);
Debug.Log("Connected Account: " + account);
Parameter Explanation
chainId
(string): The chain ID of the target network.rpcUrl
(string): The RPC URL of the network.chainName
(string): The name of the network.nativeCurrencyName
(string): The native currency name.nativeCurrencySymbol
(string): The native currency symbol.nativeCurrencyDecimals
(string): The number of decimals for the native currency.blockExplorer
(string): The URL of the block explorer for the network.
SwitchChains
SwitchChains(string chainId)
Method Description
Switches the connected Ethereum network to a different one using the specified chain ID.
Code Example
string chainId = "1351057110"; // SKALE Calypso Test Network (Change to the desired chain ID)wallet.SwitchChains(chainId);
Parameter Explanation
chainId
(string): The chain ID of the target network.
SwitchChains (Overload)
SwitchChains(string chainId, string rpcURL, string chainName, string nativeCurrencyName, string nativeCurrencySymbol, string nativeCurrencyDecimals, string blockExplorer)
Method Description
Switches the connected network, adding the network if it’s not available on the users browser wallet.
Code Example
string chainId = "1564830818"; // SKALE Calyspo Mainnet Network (Change to the desired chain ID)string rpcURL = "https://mainnet.skalenodes.com/v1/honorable-steel-rasalhague"; // Replace with your RPC URLstring chainName = "SKALE Calypso Mainnet";string nativeCurrencyName = "sFUEL";string nativeCurrencySymbol = "sFUEL";string nativeCurrencyDecimals = "18";string blockExplorer = "https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com/";
wallet.SwitchChainsAndAdd(chainId, rpcURL, chainName, nativeCurrencyName, nativeCurrencySymbol, nativeCurrencyDecimals, blockExplorer);
Parameter Explanation
chainId
(string): The chain ID of the target network.rpcURL
(string): The RPC URL of the network.chainName
(string): The name of the network.nativeCurrencyName
(string): The native currency name.nativeCurrencySymbol
(string): The native currency symbol.nativeCurrencyDecimals
(string): The number of decimals for the native currency.blockExplorer
(string): The URL of the block explorer for the network.
Disconnect
Disconnect()
Method Description
Disconnects from the currently connected wallet and clears the account information.
Code Example
wallet.Disconnect();
GetTransactionReceipt
GetTransactionReceipt()
Method Description
Retrieves the transaction receipt after sending a transaction.
Code Example
string transactionReceipt = wallet.GetTransactionReceipt();Debug.Log("Transaction Receipt: " + transactionReceipt);
Parameter Explanation
- None
GetChainId
GetChainId()
Method Description
Retrieves the users current connected chain ID.
Code Example
string chainId = wallet.GetChainId();Debug.Log("Current Chain: " + chainId);
Parameter Explanation
- None
SendTransaction
SendTransaction(string to, string gas = null, string gasPrice = null, string maxFeePerGas = null, string maxPriorityFee = null, string value = null, string nonce = null, string data = null, TransactionType type = TransactionType.EIP1559)
Method Description
Sends a transaction to a smart contract method. Parameters like gas, gasPrice and value are optional. This function will return a transaction hash if successful, or an exception if not.
Code Example
// Transfer 1 ETH to a user address by specifying the to address and the value in ETH.string transactionHash = wallet.SendTransaction("WalletAddressHere", value: "1");
Parameter Explanation
to
(string): The address your sending the transaction to.gas
(optional) (string): The gas limit for the transaction.gasPrice
(optional) (string): The Legacy gas price for the transaction.maxFeePerGas
(optional) (string): The EIP1559 gas fee for the transaction.maxPriorityFee
(optional) (string): The EIP1559 Priority fee for the transaction.value
(optional) (string): The value for chain native tokens.nonce
(optional) (string): The current transaction nonce count.data
(optional): The encoded data for a contract method and set of parameters.type
(optional): (TransactionType) The transction type used for this transaction. Defaults to EIP1559
Sign Message
SignMessage(string message)
Method Description
Prompts the connected account to sign a message.
Code Example
// Prompt the connected account to sign a message and return the signature.string signature = wallet.SignMessage("This is a test message!");
Parameter Explanation
message
(string): The message data you want the user to sign.
When building for either Desktop
or Mobile
, the EmbeddedWallet
class can be used and goes hand in hand with the Signer
class.
The Signer
class provides functionality to create and manage Ethereum accounts (wallets) for users within your Unity project. It includes methods for creating new accounts and loading existing ones. The class utilizes key storage mechanisms to store private keys and allows for interactive and invisible account creation and login.
By default, the generated keys are encrypted and stored within a Nethereum keystore. However, if you are developing on Android or Windows, you have the option to store the wallet file in the device-specific keystore, such as the Android Keystore or Windows Keystore.
Please note that in order to connect a signer to a session, you need to set it using the EmbeddedWallet
class:
Single-Chain Constructor
EmbeddedWallet mySigner = new EmbeddedWallet(string privateKey);
privateKey
(string): The private key that will handle signing transactions.- The
chainID
andRPC
is automatically assigned based on yourProject Setup
configuration.
Multi-Chain Constructor
// Alternatively you can override the default RPC to instantiate a wallet instance on a different network
// Custom RPC ProviderJsonRpcProvider provider = new JsonRpcProvider("customRpcHere");
// Private Keystring privateKey = "abc...xyz";
// Chain IDstring chainId = "1";
EmbeddedWallet mySigner = new EmbeddedWallet(privateKey, chainId, provider);
privateKey
(string): The private key that will handle signing transactions.chainId
(string): The chain ID that you want this signer to connect to.provider
(JsonRpcProvider): The RPC (Remote Procedure Call) endpoint URL of the blockchain network.
This is important as it allows you to sign transactions to the blockchain from within your unity game. Let’s explore how we can use the signer class below:
Methods
After instantiating your Embedded Wallet
instance, you can utilize any of the below wallet methods anywhere in your Unity project.
Create
The Create
method allows developers to create a new Ethereum account (wallet) for a user. It takes a username and password as input parameters and performs the following steps:
- Checks if a wallet with the provided username already exists.
- If not, generates a new Ethereum private key and creates a wallet file.
- Encrypts the private key using the provided password and stores it in your keystore of choice.
public static bool Create(string username, string password)
Load
The Load
method enables developers to load an existing Ethereum account (wallet) for a user. It takes a username and an optional password (used for decryption) as input parameters and performs the following steps:
- Checks if a wallet with the provided username exists.
- If found, reads the KeyStore file and deserializes it.
- Decrypts the private key using the provided password.
- Provides you with access to the private key and public key for use in-game.
Please note that the public account can be accessed through PlayerPrefs.GetString("Account")
.
public static string Load(string username, string password = null)
WalletExists
The WalletExists
method is a private helper method that checks if a wallet file exists for the given username.
private static bool WalletExists(string username)
Sign Message
SignMessage(string message)
Method Description
Prompts the connected account to sign a message.
Code Example
// Prompt the connected account to sign a message and return the signature.string signature = wallet.SignMessage("This is a test message!");
Parameter Explanation
message
(string): The message data you want the user to sign.
Example Usage
This foundation allows you as a developer to have various different approaches when it comes to onboarding new or logging in existing users. Let’s explore two common ways of utilizing the signers class:
Interactive Login
With this example, we allow the player to sign up and login using a very traditional system. By manually passing their preferred username and password, they can manage their own account and store information where they think is best.
using TMPro;using UnityEngine;
public class InteractiveLogin : MonoBehaviour{ // Input fields public TMP_InputField usernameInput; public TMP_InputField passwordInput;
// RPC Provider public JsonRpcProvider provider;
// Status Text public TextMeshProUGUI statusText;
// Account variables private string username; private string password;
public void Awake() { // Instantiate our RPC and use the default RPC from the project settings. provider = new JsonRpcProvider(); }
// Variables = Inputfield.value private void Update() { username = usernameInput.text; password = passwordInput.text; }
// Used to sign-up a new player to your game. public void SignUp() { // Clear the input fields (optional) usernameInput.text = ""; passwordInput.text = "";
// We can call the BackgroundSigner class to create a new account for us based on the users input. bool SignUpSuccess = Signer.Create(username, password);
// If the account is created successfully if (SignUpSuccess) { // Success logic goes here Debug.Log("Account created successfully!"); statusText.text = "Account created successfully!"; } // Otherwise else { // Failed logic goes here Debug.Log("Username already exists. Please choose a different username."); statusText.text = "Username already exists. Please choose a different username."; } }
// Used to login an existing player based on their account details set at Sign-up public async void Login() { // The signer key used sign transactions in your game string signer = Signer.Load(username, password);
// If the user logs in successfully if (signer != null) { // Success logic goes here Debug.Log("Logged in Successfully!");
// You can create a new wallet intance using the logged in signer. Please see the smart contract example to see where a wallet is used. Wallet wallet = new Wallet(signer);
// You can assign the public address for the wallet using the below string account = wallet.GetAddress();
// The connected wallet can be retrieved with PlayerPrefs.GetString("Account") statusText.text = "Logged in Successfully! Connected Address: " + account;
// Add any other logic required here. ie checking gas / sending gas. var currentGasBalance = await Provider.GetBalance(account);
// Example showcasing how we would fetch the gas for this signer. Debug.Log("Signer Gas Balance: " + currentGasBalance); } // Otherwise else { // Failed logic goes here // The BackgroundSigner class will log errors for failed login attempts. Feel free to add more here. statusText.text = "Failed to login - Wallet doesn't exist! Please Sign-up first!"; } }}
The Interactive login example provides a user-friendly interface for account creation and login while connecting to Ethereum blockchain features, allowing for seamless interaction with decentralized applications.
Invisible Login
With this example, we generate a wallet ID and secret which is then stored within the device keystore for secure long-term storage. This allows you as the developer to create seamless background login flows which don’t require any input from your players.
using TMPro;using UnityEngine;
public class InvisibleLogin : MonoBehaviour{ // Variables public TextMeshProUGUI statusText; private string ID; public JsonRpcProvider provider;
// We can create a new invisible signer by calling the Signer.Create() method. public void CreateAccount() { // Instantiate our RPC and use the default RPC from the project settings. provider = new JsonRpcProvider();
// We can use anything to identify the signer -IP/MAC Address, GUID - In this example we use a unique, persistent device ID. ID = SystemInfo.deviceUniqueIdentifier;
// Generate a new secret to secure the signer. We can pass a length to our PasswordGenerator class. string secret = PasswordGenerator.GenerateRandomPassword(32);
// Create the signer bool AccountCreated = Signer.Create(ID, secret);
// If the signer has been created... if (AccountCreated) { statusText.text = "Account created successfully with ID: " + ID;
// Add any other logic required here. ie checking gas / sending gas.
// In our example we'll just login Invoke(nameof(LoadAccount), 4f); } // Otherwise if the ID already exists, we can login. else { LoadAccount(); } }
public async void LoadAccount() { // Load our private key using the Signer.Load() method. string signer = Signer.Load(ID, WindowsKeystore.GetStringFromKeystore(ID));
// If the signer value isn't empty.. if (signer != null) { // You can create a new wallet intance using the logged in signer. Please see the smart contract example to see where a wallet is used. Wallet wallet = new Wallet(signer);
// You can assign the public address for the wallet using the below string account = wallet.GetAddress();
// Sanity check to ensure we're connected. statusText.text = "Account: " + account + " logged in successfully!";
// Add any other logic required here. ie checking gas / sending gas. var currentGasBalance = await Provider.GetBalance(account);
// Example showcasing how we would fetch the gas for this signer. Debug.Log("Signer Gas Balance: " + currentGasBalance);
// You can go even further and add a conditional statement so that if the signer has < X amount of gas // You can send it gas using an API or your preferred method for gas distribution. } // Otherwise if the key is empty.. else { // There is error handling in the core library, but the developer can add extra here for debugging. statusText.text = "Couldn't log in"; } }}
The InvisibleLogin class provides a method for developers to create and manage Ethereum accounts on behalf of players, allowing them to seamlessly interact with decentralized applications without the need for players to handle username and password creation.