Submit Your Wallet
The Thirdweb Unity SDK comes with many built-in wallets, but you can add your own wallet to the SDK by following these steps.
Step 1 - WalletProvider
Add your wallet identifier to the WalletProvider enum in the Wallet.cs file. This enum represents the different wallet providers.
public enum WalletProvider
{
...,
MyEpicWallet
}
Step 2 - Implement IThirdwebWallet
Implement the IThirdwebWallet interface for your wallet. This interface defines the methods and properties required for wallet integration.
using System.Numerics;
using System.Threading.Tasks;
using Nethereum.Web3;
using Nethereum.Web3.Accounts;
namespace Thirdweb.Wallets
{
/// <summary>
/// Interface for interacting with a Thirdweb wallet.
/// </summary>
public interface IThirdwebWallet
{
/// <summary>
/// Main Connect call - should fully connect to the wallet and return the address.
/// </summary>
/// <param name="walletConnection">The wallet connection details.</param>
/// <param name="rpc">The RPC endpoint.</param>
/// <returns>The address of the connected wallet.</returns>
Task<string> Connect(WalletConnection walletConnection, string rpc);
/// <summary>
/// Main Disconnect call - should fully disconnect from the wallet and reset any variables.
/// </summary>
Task Disconnect();
/// <summary>
/// Get the local account if any, return null otherwise.
/// </summary>
/// <returns>The local account, or null if not available.</returns>
Account GetLocalAccount();
/// <summary>
/// Return the address of the main account.
/// </summary>
/// <returns>The address of the main account.</returns>
Task<string> GetAddress();
/// <summary>
/// Return the address of the signer account (if any, otherwise return GetAddress).
/// </summary>
/// <returns>The address of the signer account.</returns>
Task<string> GetSignerAddress();
/// <summary>
/// Return the WalletProvider you added above.
/// </summary>
/// <returns>The WalletProvider.</returns>
WalletProvider GetProvider();
/// <summary>
/// Return the WalletProvider of the signer account (if any, otherwise return GetProvider).
/// </summary>
/// <returns>The WalletProvider of the signer account.</returns>
WalletProvider GetSignerProvider();
/// <summary>
/// Return the Web3 Nethereum provider for the main account - must override Task<RpcResponseMessage> SendAsync.
/// </summary>
/// <returns>The Web3 Nethereum provider for the main account.</returns>
Task<Web3> GetWeb3();
/// <summary>
/// Return the Web3 Nethereum provider for the signer account (if any, otherwise return GetWeb3).
/// </summary>
/// <returns>The Web3 Nethereum provider for the signer account.</returns>
Task<Web3> GetSignerWeb3();
/// <summary>
/// Return whether the wallet is currently connected (e.g. Web3 != null).
/// </summary>
/// <returns>True if the wallet is connected; otherwise, false.</returns>
Task<bool> IsConnected();
/// <summary>
/// Prepares the wallet for a network switch and returns an actionable response.
/// </summary>
/// <param name="newChainId">The new chain ID to switch to.</param>
/// <param name="newRpc">The new RPC endpoint to switch to.</param>
/// <returns>A <see cref="NetworkSwitchAction"/> indicating the action to be taken.</returns>
Task<NetworkSwitchAction> PrepareForNetworkSwitch(BigInteger newChainId, string newRpc);
}
public enum NetworkSwitchAction
{
/// <summary>
/// Indicates that the network switch can proceed. The SDK should continue with the wallet_switchEthereumChain RPC call.
/// </summary>
ContinueSwitch,
/// <summary>
/// Indicates that the wallet has already handled the network switch internally. There's no need to make the wallet_switchEthereumChain RPC call.
/// </summary>
Handled,
/// <summary>
/// Indicates that the network switching feature is completely unsupported for the current wallet implementation.
/// </summary>
Unsupported
}
}
Step 3 - Instantiate Your Wallet in ThirdwebSession.Connect
In the ThirdwebSession.cs file, instantiate your wallet in the Connect method.
switch (walletConnection.provider)
{
// ...
case WalletProvider.MyWalletProvider:
ActiveWallet = new MyWallet();
break;
// ...
}
Advanced
WalletConnection
If your wallet implementation requires additional data for the constructor or connect methods, you can add fields to the WalletConnection
class in the Wallet.cs
file.
public class WalletConnection
{
// ...
public string additionalData;
public WalletConnection(/* ... */, string additionalData = null)
{
// ...
this.additionalData = additionalData;
}
}
Nethereum Web3 Provider
All RPC calls will pass through Nethereum's Web3
provider.
You'll need four classes to fully implement the provider, here's a Smart Wallet example:
Extending IAccount
using Nethereum.JsonRpc.Client;
using Nethereum.RPC.Accounts;
using Nethereum.RPC.AccountSigning;
using Nethereum.RPC.NonceServices;
using Nethereum.RPC.TransactionManagers;
namespace Thirdweb.AccountAbstraction
{
public class SmartWalletAccount : IAccount
{
private readonly SmartWallet _wallet;
private readonly IClient _client;
public string Address
{
get { return _wallet.Accounts[0]; }
}
public ITransactionManager TransactionManager { get; }
public INonceService NonceService { get; set; }
public IAccountSigningService AccountSigningService { get; }
public IClient Client
{
get { return _client; }
}
public SmartWalletAccount(SmartWallet wallet, IClient client)
{
_wallet = wallet;
_client = client;
TransactionManager = new SmartWalletTransactionManager(this);
NonceService = new InMemoryNonceService(Address, client);
AccountSigningService = new AccountSigningService(client);
}
}
}
Extending ClientBase
using System;
using System.Linq;
using System.Threading.Tasks;
using Nethereum.JsonRpc.Client;
using Nethereum.JsonRpc.Client.RpcMessages;
namespace Thirdweb.AccountAbstraction
{
public class SmartWalletClient : ClientBase
{
private SmartWallet _smartWallet;
public SmartWalletClient(SmartWallet smartWallet)
{
this._smartWallet = smartWallet;
}
private static readonly Random rng = new Random();
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static long GenerateRpcId()
{
var date = (long)((DateTime.UtcNow - UnixEpoch).TotalMilliseconds) * (10L * 10L * 10L);
var extra = (long)Math.Floor(rng.NextDouble() * (10.0 * 10.0 * 10.0));
return date + extra;
}
protected override async Task<RpcResponseMessage> SendAsync(RpcRequestMessage message, string route = null)
{
message.Id = GenerateRpcId();
return await _smartWallet.Request(message);
}
protected override Task<RpcResponseMessage[]> SendAsync(RpcRequestMessage[] requests)
{
return Task.WhenAll(requests.Select(r => SendAsync(r)));
}
}
}
Extending TransactionManagerBase
(not used in practice)
namespace Thirdweb.AccountAbstraction
{
public class SmartWalletTransactionManager : Nethereum.RPC.TransactionManagers.TransactionManager
{
public SmartWalletTransactionManager(SmartWalletAccount account)
: base(account.Client)
{
Account = account;
}
}
}
Finally, an extension to create a Web3
provider
using Nethereum.Web3;
namespace Thirdweb.AccountAbstraction
{
public static class SmartWalletNEthereumExtensions
{
public static Web3 CreateWeb3(this SmartWallet smartWallet)
{
var client = new SmartWalletClient(smartWallet);
var account = new SmartWalletAccount(smartWallet, client);
return new Web3(account, client);
}
}
}
Submitting Your Wallet
If you think your wallet implementation would be useful to others, please consider sharing it by opening a PR to the thirdweb-dev/unity-sdk
repository.