LayerHopDocsLaunch app (opens in a new tab)

C# SDK (.NET and Unity)

The LayerHop NuGet package is an async C# client for .NET 6+ and Unity 2021.3+ (netstandard2.1, Newtonsoft.Json), IL2CPP-safe, with typed models, order watching and webhook verification.

LayerHop is the C# client for the LayerHop REST API, for .NET 6+ and Unity 2021.3+, mirroring the Go and TypeScript SDKs. It targets netstandard2.1, its only dependency is Newtonsoft.Json 13 (which Unity ships as com.unity.nuget.newtonsoft-json), every method is an async Task that takes a CancellationToken, and you can inject your own HttpClient.

The SDK never holds keys. It returns unsigned transactions; your wallet integration (Nethereum, Solnet, the Solana Unity SDK, Reown AppKit for Unity, a KMS…) signs and broadcasts them.

How do I install it?#

Terminal
dotnet add package LayerHop --version 0.6.0

For Unity, see How do I install it in Unity?. The source lives in layerhop/layerhop-csharp.

Quickstart#

Amounts are integer strings in base units ("100000000" = 100 USDC).

C#
using LayerHop;

using var lh = new LayerHopClient(new LayerHopOptions { Integrator = "myapp" }); // ApiKey, BaseUrl

// 1. Quote
var q = await lh.QuoteAsync(new QuoteParams
{
    SrcChain = "base", SrcToken = "USDC", Amount = "100000000",
    DstChain = "solana", DstToken = "SOL",
    Sender = "0xYourEvmAddress", Recipient = "YourSolanaAddress",
});

// 2. Build the unsigned transaction
var tx = await lh.BuildTxAsync(q.Best.QuoteId);

// 3. Sign + send with YOUR wallet
string txHash = tx.IsEvm
    ? await SignEvm(tx.AsEvm())   // send Approval first when set, then the tx, then ExtraTxs in order
    : await SignSvm(tx.AsSvm());  // base64 SerializedTx; SubmitViaApi → sign only, then lh.SubmitAsync(..)

// 4. Report the broadcast
await lh.ReportSubmittedAsync(q.Best.QuoteId, txHash);

// 5. Watch until filled / refunded / failed
await foreach (var st in lh.WatchOrder(q.Best.Provider, txHash))
    Console.WriteLine(st.State);

WatchOrderAsync(provider, id, onChange: s => …) does the same and returns the final status; both throw LayerHopWatchTimeoutException after 30 minutes by default. Gas on arrival: set GasOnArrival = true or GasOnArrivalUsd = 5 and check q.Best.GasOnArrival (Gas on arrival).

How do I install it in Unity?#

In Window > Package Manager > + > Add package from git URL… enter:

Terminal
https://github.com/layerhop/layerhop-csharp.git?path=src/LayerHop#v0.6.0

or add "com.layerhop.sdk": "https://github.com/layerhop/layerhop-csharp.git?path=src/LayerHop#v0.6.0" to Packages/manifest.json. Unity resolves the com.unity.nuget.newtonsoft-json dependency itself, and the auto-referenced LayerHop assembly definition lets your scripts using LayerHop; right away.

C#
using LayerHop;
using UnityEngine;

public class SwapButton : MonoBehaviour
{
    private readonly LayerHopClient _lh = new LayerHopClient(new LayerHopOptions { Integrator = "mygame" });

    public async void OnClick()
    {
        try
        {
            var q = await _lh.QuoteAsync(new QuoteParams
            {
                SrcChain = "solana", SrcToken = "USDC", Amount = "5000000",
                DstChain = "solana", DstToken = "SOL",
                Sender = PlayerWallet.Address, Recipient = PlayerWallet.Address,
            });
            var tx = (await _lh.BuildTxAsync(q.Best.QuoteId)).AsSvm();
            // Hand tx.SerializedTx to your wallet integration to sign and send, then:
            // await _lh.ReportSubmittedAsync(q.Best.QuoteId, signature);
        }
        catch (LayerHopApiException e)
        {
            Debug.LogWarning($"{e.Code}: {e.ApiMessage} ({e.RequestId})");
        }
    }

    private void OnDestroy() => _lh.Dispose();
}

await in a MonoBehaviour resumes on the main thread, so you can update UI after it; UniTask works too. Never ship an API key inside a game build: quotes, transactions and order status work without one, and checkout sessions, invoices and deposit addresses belong on your backend.

Does it work with IL2CPP and WebGL?#

  • IL2CPP and code stripping: the package ships a link.xml that preserves the LayerHop assembly, so the JSON models survive stripping at any level. There is no System.Reflection.Emit, no dynamic and no runtime code generation; every model is a plain class with a parameterless constructor and explicit [JsonProperty] names. The language level is C# 9.
  • WebGL: browsers only allow UnityWebRequest, so HttpClient's default handler does not work there. Pass new HttpClient(handler) with a small HttpMessageHandler that forwards to UnityWebRequest; the repository README has a copy-paste version. Every other platform works with the default handler.

Which methods are available?#

Area Methods
Assets HealthAsync · GetChainsAsync · GetTokensAsync(chain) · GetPricesAsync(ids)
Swaps QuoteAsync · BuildTxAsync (→ AsEvm() / AsSvm()) · SimulateAsync · SubmitAsync · ReportSubmittedAsync · GetOrderStatusAsync · ListOrdersAsync · WatchOrder / WatchOrderAsync
Checkout CreateCheckoutSessionAsync · GetCheckoutSessionAsync · ListCheckoutSessionsAsync · ExpireCheckoutSessionAsync · QuoteCheckoutSessionAsync
Products CreateCheckoutProductAsync · GetCheckoutProductAsync · ListCheckoutProductsAsync · UpdateCheckoutProductAsync · StartProductCheckoutAsync
Refunds CreateRefundAsync · ListRefundsAsync
Invoices CreateInvoiceAsync · GetInvoiceAsync · ListInvoicesAsync · UpdateInvoiceAsync · SendInvoiceAsync · VoidInvoiceAsync · GetPublicInvoiceAsync · PayInvoiceAsync
Deposit addresses CreateDepositAddressAsync · GetDepositAddressAsync · ListDepositAddressesAsync · RefreshDepositAddressAsync · DisableDepositAddressAsync · SimulateDepositAsync
Subscriptions CreateSubscriptionAsync · GetSubscriptionAsync · ListSubscriptionsAsync · UpdateSubscriptionAsync · CancelSubscriptionAsync · PauseSubscriptionAsync · ResumeSubscriptionAsync · ListSubscriptionInvoicesAsync
Limit orders (Solana, beta) CreateLimitOrderAsync · SubmitLimitOrderAsync · GetLimitOrderAsync · ListLimitOrdersAsync · CancelLimitOrderAsync · SimulateLimitOrderAsync
Misc GetServiceStatusAsync · TriggerWebhookEventAsync

Update*Params send only the properties you set; setting a nullable text field to "" clears it (sends null).

How do I take payments with checkout?#

Run this on your server with an API key (Checkout guide):

C#
var lh = new LayerHopClient(new LayerHopOptions { ApiKey = Environment.GetEnvironmentVariable("LAYERHOP_API_KEY") });
var session = await lh.CreateCheckoutSessionAsync(new CreateCheckoutParams
{
    Amount = "49000000", SettleChain = "base", SettleToken = "USDC",   // 49 USDC on Base
    Recipient = "0xYourPayoutAddress", Reference = "order_123",
    SuccessUrl = "https://shop.example.com/thanks",
});
// Redirect the customer to session.Url; fulfil on the checkout.session.completed webhook.

How do I verify webhooks?#

Verify the LayerHop-Signature header against the raw body before parsing it (Webhooks guide). ASP.NET Core minimal API:

C#
app.MapPost("/webhooks/layerhop", async (HttpRequest req) =>
{
    using var reader = new StreamReader(req.Body);
    var raw = await reader.ReadToEndAsync();
    try
    {
        var evt = Webhooks.ConstructEvent(secret, raw, req.Headers[Webhooks.SignatureHeader]);
        if (evt.Type == WebhookEventTypes.CheckoutSessionCompleted) Fulfil(evt.Data.Session!.Reference);
        return Results.Ok();
    }
    catch (LayerHopWebhookSignatureException) { return Results.BadRequest(); }
});

Webhooks.VerifySignature(secret, body, header) returns a bool (5-minute tolerance by default, several v1 values accepted, constant-time comparison). Webhooks.Sign(secret, body, timestamp) builds a header for your own tests.

How are errors reported?#

Every non-2xx response throws LayerHopApiException with a stable Code (QUOTE_EXPIRED, NO_ROUTE…, see Errors), the HTTP Status, the RequestId to quote to support, and DocsUrl. Network failures throw HttpRequestException.

How do I use test mode?#

Use an lh_test_… key and every call is served by the sandbox: live-priced quotes, placeholder transactions (tx.Sandbox == true, never broadcast them) and orders that fill in about 10 seconds. See Test mode.

Last updated