Skip to main content
Requires xyz.dynamic.unitysdk.solana package and com.solana.unity_sdk for transaction building. Message signing works without these packages.

Sign message

Message signing works without the Solana extension package:
string signature = await DynamicSDK.Instance.Networks.Solana.SignMessage(
    wallet.Id, "Hello World");

Send SOL (versioned transaction)

using Solana.Unity.Programs;
using Solana.Unity.Rpc.Models;
using Solana.Unity.Wallet;

var solana = DynamicSDK.Instance.Networks.Solana;
var rpcClient = solana.CreateConnection();

// Get recent blockhash
var blockHashResult = await rpcClient.GetLatestBlockHashAsync();
var blockHash = blockHashResult.Result.Value.Blockhash;

var fromKey = new PublicKey(wallet.Address);
var toKey = new PublicKey("RecipientAddress");
var lamports = (ulong)(0.01 * 1_000_000_000); // 0.01 SOL

// Build versioned (v0) transaction
var tx = new VersionedTransaction
{
    RecentBlockHash = blockHash,
    FeePayer = fromKey,
    Instructions = new List<TransactionInstruction>
    {
        SystemProgram.Transfer(fromKey, toKey, lamports)
    },
    AddressTableLookups = new List<MessageAddressTableLookup>()
};

var message = tx.CompileMessage();

// Prepend 1 empty signature slot for the SDK to sign
var txBytes = new byte[1 + 64 + message.Length];
txBytes[0] = 1; // 1 signature slot (compact-u16)
// bytes 1..64 stay zero (empty signature placeholder)
Buffer.BlockCopy(message, 0, txBytes, 65, message.Length);

string txHash = await solana.SignAndSendTransaction(
    wallet.Id, txBytes, SolanaTransactionType.Versioned);

Send SPL token (versioned transaction)

using Solana.Unity.Programs;
using Solana.Unity.Rpc.Models;
using Solana.Unity.Wallet;

var solana = DynamicSDK.Instance.Networks.Solana;
var rpcClient = solana.CreateConnection();

var blockHashResult = await rpcClient.GetLatestBlockHashAsync();
var blockHash = blockHashResult.Result.Value.Blockhash;

var ownerKey = new PublicKey(wallet.Address);
var recipientKey = new PublicKey("RecipientAddress");
var mintKey = new PublicKey("TokenMintAddress"); // e.g. USDC mint

// Derive Associated Token Accounts
var sourceAta = AssociatedTokenAccountProgram
    .DeriveAssociatedTokenAccount(ownerKey, mintKey);
var destAta = AssociatedTokenAccountProgram
    .DeriveAssociatedTokenAccount(recipientKey, mintKey);

int decimals = 6; // USDC has 6 decimals
var rawAmount = (ulong)(1.5 * Math.Pow(10, decimals)); // 1.5 USDC

var tx = new VersionedTransaction
{
    RecentBlockHash = blockHash,
    FeePayer = ownerKey,
    Instructions = new List<TransactionInstruction>
    {
        // Create destination ATA if it doesn't exist (idempotent)
        AssociatedTokenAccountProgram.CreateAssociatedTokenAccount(
            ownerKey, recipientKey, mintKey),
        // Transfer SPL tokens
        TokenProgram.Transfer(sourceAta, destAta, rawAmount, ownerKey)
    },
    AddressTableLookups = new List<MessageAddressTableLookup>()
};

var message = tx.CompileMessage();

var txBytes = new byte[1 + 64 + message.Length];
txBytes[0] = 1;
Buffer.BlockCopy(message, 0, txBytes, 65, message.Length);

string txHash = await solana.SignAndSendTransaction(
    wallet.Id, txBytes, SolanaTransactionType.Versioned);

Common token mint addresses

TokenNetworkMint Address
USDCMainnet (101)EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDCDevnet (103)4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

Complete example

using DynamicSDK.Core;
using Solana.Unity.Programs;
using Solana.Unity.Rpc.Models;
using Solana.Unity.Wallet;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;

public class SolanaTransactionManager : MonoBehaviour
{
    private BaseWallet _solWallet;
    
    private void Start()
    {
        _solWallet = DynamicSDK.Instance.Wallets.UserWallets
            .FirstOrDefault(w => w.Chain.ToUpper() == "SOL");
    }
    
    public async void SendSol(string toAddress, double amount)
    {
        if (_solWallet == null)
        {
            Debug.LogError("No Solana wallet available");
            return;
        }
        
        try
        {
            var solana = DynamicSDK.Instance.Networks.Solana;
            var rpcClient = solana.CreateConnection();
            
            var blockHashResult = await rpcClient.GetLatestBlockHashAsync();
            var blockHash = blockHashResult.Result.Value.Blockhash;
            
            var fromKey = new PublicKey(_solWallet.Address);
            var toKey = new PublicKey(toAddress);
            var lamports = (ulong)(amount * 1_000_000_000);
            
            var tx = new VersionedTransaction
            {
                RecentBlockHash = blockHash,
                FeePayer = fromKey,
                Instructions = new List<TransactionInstruction>
                {
                    SystemProgram.Transfer(fromKey, toKey, lamports)
                },
                AddressTableLookups = new List<MessageAddressTableLookup>()
            };
            
            var message = tx.CompileMessage();
            var txBytes = new byte[1 + 64 + message.Length];
            txBytes[0] = 1;
            Buffer.BlockCopy(message, 0, txBytes, 65, message.Length);
            
            string txHash = await solana.SignAndSendTransaction(
                _solWallet.Id, txBytes, SolanaTransactionType.Versioned);
            
            Debug.Log($"Transaction sent: {txHash}");
        }
        catch (Exception ex)
        {
            Debug.LogError($"Transaction failed: {ex.Message}");
        }
    }
    
    public async void SignMessage(string message)
    {
        if (_solWallet == null)
        {
            Debug.LogError("No Solana wallet available");
            return;
        }
        
        string signature = await DynamicSDK.Instance.Networks.Solana.SignMessage(
            _solWallet.Id, message);
        
        Debug.Log($"Signature: {signature}");
    }
}

Next steps