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?#
dotnet add package LayerHop --version 0.6.0For 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).
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:
https://github.com/layerhop/layerhop-csharp.git?path=src/LayerHop#v0.6.0or 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.
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.xmlthat preserves theLayerHopassembly, so the JSON models survive stripping at any level. There is noSystem.Reflection.Emit, nodynamicand 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, soHttpClient's default handler does not work there. Passnew HttpClient(handler)with a smallHttpMessageHandlerthat forwards toUnityWebRequest; 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):
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:
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