The First Open-Source Unity-Solana Wallet with NFT support
The Unity-Solana Wallet is an essential bridge connecting game developers and the Solana blockchain. With Solana’s quick and low-cost transactions, games can start using blockchain technology like never before - in real-time. Thousands of developers will bring millions of players into the Solana ecosystem. This will, in turn, make the Solana projects benefit from an increased number of participants, and cross-protocol interoperability in games will take us beyond the current understanding of DeFi.
Unity-Solana Wallet uses Solnet's implementation .NET SDK, but we had to modify the library to make it Unity compatible with .NET Standard 2.0 and .NET 4.x. Solnet is Solana's .NET SDK to integrate with the .NET ecosystem. Solnet.
- Create/Backup wallet with mnemonic phrase
- Account handling
- Transaction building
- SOL balance
- SPL-token balances
- SPL-token transfers
- Basic UI examples
- WebSocket subscription
- Save and load mnemonics from local txt file
- Save private key in txt file
- Newtonsoft.Json
- Chaos.NaCl.Standard
- Portable.BouncyCastle
- Zxing
- Native File Picker
- Standalone File Browser
- Multiple wallet accounts
- Camera support with QR code scanning for token transfers
- Improved UI for in-game easy integration
- Metaplex NFT / NFT-PRO support with GameObjects
- Token swaps
- NFT swaps
- One-click in-game currency creator
- Themed UI support
- Metaplex auctions for in-game store items
- Clone this repository outside of the main Unity project
- Go to Package Manager in your project
- Click on the plus in the top left corner and select "Add package from disk"
- Select package.json file from a cloned dir
- Once the package is installed, in the Package Manager inspector you will have Samples. Click on Import
- If you have an older version of Unity that doesn't have imported Newtonsoft.Json just import it.
- After importing the wallet Unity will throw unity-plastic error. Just restart Unity.
- Create a new scene.
- Import WalletController prefab into your scene.
- Set Client Source (Mainnet/Testnet/Devnet/Custom uri) and Storage Method (Json/Simple txt) on SimpleWallet script in WalletController prefab.
- If you use custom URI be careful to use WS/WSS instead of HTTP/HTTPS because WebSocket does not work with HTTP / HTTPS.
- To save mnemonics in JSON format, select the JSON storage method, and if you want to save it as a regular string, select Simple Txt.
- If you want to use mnemonics saved in JSON format, you must deserialize it first. You have an example in ReGenerateAccountScreen.cs in the ResolveMnemonicsByType method.
- Create new Canvas
- Import WalletHolder prefab into the Canvas or if you want your design just import wallet prefab and customize the scene like we did with WalletHolder.
- If you have already logged in to your wallet, then your mnemonics are stored and encrypted in memory with your password and you can log in with that password. Otherwise you have to create or restore a wallet.
- You now have automatically generated mnemonics and to successfully create a wallet you must enter a password with which the mnemonics will be encrypted. I recommend that you use the Save Mnemonics option and save them to a text file. Then press create button to create a wallet.
- If you have saved mnemonics and want to recreate a wallet with it, load them by pressing Load Mnemonics button and generate the password again. Now your wallet is regenerated and the amount of SOL and NFT will be reloaded.
- After you successfully logged in / generated / regenerated a wallet you will automatically be transferred to the wallet screen. Now you are shown SOL balance and your NFT's and you are automatically subscribed to the account via the websocket. This allows you to track changes in your account (automatic refreshing of SOL balance when a balance changes, etc..).
- To facilitate testing, there is an Airdrop option in the Recieve section. Click on the Airdrop button, return to the Wallet Screen and wait a few seconds to see the change in SOL balance.
- To complete the transaction enter the wallet pubkey and the amount you want to send. Then return to the wallet screen and wait a few seconds for the SOL Balance to refresh.
- This class is located at Packages -> Solana Wallet -> Runtime -> codebase -> WalletBaseComponent.cs
public async void CreateAccount(Account account, string toPublicKey = "", long ammount = 1000)
- First create keypair(private key and public key),
- Then create blockHash from activeRpcClient,
- Initialize transaction
- Send transaction
public SolanaRpcClient StartConnection(EClientUrlSource clientUrlSource, string customUrl = "")
- For starting RPC connection call StartConnection and forward clientSource.
- Function returns new connected RPC client.
- Call example
StartConnection(clientSource);
public Wallet GenerateWalletWithMenmonic(string mnemonics)
- First check forwarded mnemonics validity.
- Encrypt mnemonics with password
- Create new wallet from mnemonics
- Subscribe to WebSocket
- Save mnemonics and encrypted mnemonics in memory
- Call example
SimpleWallet.instance.GenerateWalletWithMenmonic(_simpleWallet.LoadPlayerPrefs(_simpleWallet.MnemonicsKey));
public bool LoginCheckMnemonicAndPassword(string password)
- Try to encrypt decrypted mnemonics with typed password.
- Return true or false
- Call example
private void LoginChecker()
{
if (_simpleWallet.LoginCheckMnemonicAndPassword(_passwordInputField.text))
{
SimpleWallet.instance.GenerateWalletWithMenmonic(_simpleWallet.LoadPlayerPrefs(_simpleWallet.MnemonicsKey));
MainThreadDispatcher.Instance().Enqueue(() => { _simpleWallet.StartWebSocketConnection(); });
manager.ShowScreen(this, "wallet_screen");
this.gameObject.SetActive(false);
}
else
{
SwitchButtons("TryAgain");
}
}
public async Task<double> GetSolAmmount(Account account)
- Returns sol amount of forwarded account
- Call example
double sol = await SimpleWallet.instance.GetSolAmmount(SimpleWallet.instance.wallet.GetAccount(0));
public async Task<RequestResult<string>> TransferSol(string toPublicKey, long ammount = 10000000)
- Executes sol transaction from one account to another one for forwarded amount.
- Call example
private async void TransferSol()
{
RequestResult<string> result = await SimpleWallet.instance.TransferSol(toPublic_txt.text, long.Parse(ammount_txt.text));
HandleResponse(result);
}
public async Task<RequestResult<string>> TransferToken(string sourceTokenAccount, string toWalletAccount, Account sourceAccountOwner, string tokenMint, long ammount = 1)
- Executes SOL transaction from one account to another one
- Call example
private async void TransferToken()
{
RequestResult<string> result = await SimpleWallet.instance.TransferToken(
transferTokenAccount.pubkey,
toPublic_txt.text,
SimpleWallet.instance.wallet.GetAccount(0),
transferTokenAccount.Account.Data.Parsed.Info.Mint,
long.Parse(ammount_txt.text));
HandleResponse(result);
}
public async Task<string> RequestAirdrop(Account account, ulong ammount = 1000000000)
- Send 1 sol to our wallet (this is for testing).
- Call example
airdrop_btn.onClick.AddListener(async () => {
await SimpleWallet.instance.RequestAirdrop(SimpleWallet.instance.wallet.GetAccount(0));
});
public async Task<TokenAccount[]> GetOwnedTokenAccounts(Account account)
- Returns array of tokens on the account
- Call example
TokenAccount[] result = await SimpleWallet.instance.GetOwnedTokenAccounts(SimpleWallet.instance.wallet.GetAccount(0));
public void DeleteWalletAndClearKey()
- Unsubscribe from WebSocket events
- Delete used wallet
public void StartWebSocketConnection()
- Starts WebSocket connection when user is logged in.
- This class is located at Packages -> Solana Wallet -> Runtime -> UnityWebSocket -> WebSocketService.cs
public void StartConnection(string address)
- For WebSocket to work we must first create a connection calling StartConnection from WebSocketService.cs and forward address
- In this function we create new WebSocket, then subscribe to events and open WebSocket connection.
- Call example
webSocketService.StartConnection(GetWebsocketConnectionURL(clientSource));
public void SubscribeToWalletAccountEvents(string pubKey)
- To subscribe Account on WebSocket events call function SubscribeToWalletAccountEvents and forward Wallet Pub key
- First set subscriptionTypeReference to know which event we are processing (in this case it is accountSubscribe).
- Then call SendParameter and forward parameter for account subscription.
- Call example
webSocketService.SubscribeToWalletAccountEvents(wallet.Account.GetPublicKey);
public void UnSubscribeToWalletAccountEvents()
- To unsubscribe Account from WebSocket events call function UnsubscribeToWalletAccountEvents
- First set subscriptionTypeReference to know which event we are processing (in this case it is accountUnsubscribe).
- Then call SendParameter and forward parameter for account unsubscription.
- Call example
public void StartWebSocketConnection()
{
if (webSocketService.Socket != null) return;
webSocketService.StartConnection(GetWebsocketConnectionURL(clientSource));
}
private void OnMessage(object sender, MessageEventArgs e)
- To respond to websocket events we use WebSocket actions that we call in OnMessage function
- Depending on the SubscriptionTypeReference, we deserialize the message into a model.
- Invoke WebSocketAction
- Then subscribe the desired functionality to the action
WebSocketActions.WebSocketAccountSubscriptionAction += CheckSubscription;
public void CloseConnection()
{
if (_socket == null) return;
_socket.CloseAsync();
}
-To close WebSocket connection call CloseConnection from WebSocketService.cs
- This class is located at Packages -> Solana Wallet -> Runtime -> codebase -> nft -> Nft.cs
public static async Task<Nft> TryGetNftData(string mint, SolanaRpcClient connection, bool tryUseLocalContent = true)
- Returns all data for one NFT and save file to persistance data path
- Call example
Nft.Nft nft = await Nft.Nft.TryGetNftData(item.Account.Data.Parsed.Info.Mint, SimpleWallet.instance.activeRpcClient, true);
public static Nft TryLoadNftFromLocal(string mint)
- Returns nft data from local machine if it exists.
- Call example
if (tryUseLocalContent)
{
Nft nft = TryLoadNftFromLocal(mint);
if (nft != null)
{
return nft;
}
}
public static Solnet.Wallet.PublicKey CreateAddress(List<byte[]> seed, string programId)
- Create NFT's public key from seed and programId
- Call example
try
{
seeds[3] = new[] { (byte)nonce };
publicKey = CreateAddress(seeds, programId);
return publicKey;
}
public static Solnet.Wallet.PublicKey FindProgramAddress(string mintPublicKey, string programId = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s")
- Returns metaplex data pubkey from mint pubkey and programId
- Call example
Solnet.Wallet.PublicKey metaplexDataPubKey = FindProgramAddress(mint);
public static async Task<T> GetMetaplexJsonData<T>(string jsonUrl)
- Returns metaplex json data from forwarded jsonUrl
private static Texture2D Resize(Texture2D texture2D, int targetX, int targetY)
- Compress nft image to target height and width.
- Call example
Texture2D compressedTexture = Resize(texture, 75, 75);
This project is licensed under the MIT License - see the LICENSE file for details