rn-latam-anchor-kit
v0.1.1
Published
React Native SDK for fiat-crypto ramp (BRL/PIX on Stellar) via Etherfuse — hooks and themable UI
Maintainers
Readme
rn-latam-anchor-kit helps React Native apps onboard Brazilian customers and
move value between BRL via PIX and Stellar assets through Etherfuse. Use the
complete themed flows, compose individual screens, or build your own interface
on top of the same typed hooks and client.
- Onboarding: identity, documents, hosted IDV, KYC status, wallet, and PIX account.
- On-ramp: BRL via PIX to a Stellar asset.
- Off-ramp: Stellar asset to BRL via PIX, including wallet preflight and XDR refresh.
- ESM, CommonJS, and TypeScript declarations.
- Zero runtime dependencies; native capabilities are injected by the host app.
- Portuguese UI copy by default, with theme and copy overrides.
Installation
npm install rn-latam-anchor-kitCompatibility
| Requirement | Supported |
| ------------ | ----------- |
| React | >= 19.0.0 |
| React Native | >= 0.78.0 |
| Blockchain | Stellar |
| Fiat rail | BRL via PIX |
React and React Native are peer dependencies. The SDK does not install a WebView, wallet, document picker, QR renderer, or clipboard implementation. Add only the native packages used by your app.
Security model
Read this section before integrating the SDK.
Production API keys must not be embedded in the app
An Etherfuse API key is organization-scoped. A key bundled into an APK or IPA can be extracted and may expose every ramp customer under that organization.
- Direct API access is supported for sandbox development.
- Production traffic should go through infrastructure you control by injecting
a custom
TransportintoEtherfuseClient.
Hosted IDV tokens must be minted by your backend
Etherfuse's hosted /idv flow uses a short-lived partner-signed JWT. The
signing key must stay on your backend. The SDK receives only a
launchTokenProvider callback that requests the token from your server.
Wallet keys stay with the host app
The SDK receives unsigned Stellar XDRs. Your wallet integration signs and
submits them through signAndSubmitTransaction; the SDK never reads or stores
private keys.
Configure the providers
The example below is intentionally configured for Etherfuse sandbox. Replace the placeholder adapters with your app's implementations.
import type { PropsWithChildren } from 'react';
import { WebView } from 'react-native-webview';
import {
EtherfuseClient,
EtherfuseProvider,
EtherfuseThemeProvider,
SANDBOX_BASE_URL,
type LaunchTokenProvider,
type SignAndSubmitStellarTransaction,
} from 'rn-latam-anchor-kit';
const client = new EtherfuseClient({
apiKey: 'YOUR_SANDBOX_API_KEY',
baseUrl: SANDBOX_BASE_URL,
});
const launchTokenProvider: LaunchTokenProvider = async (customerId) => {
const response = await fetch(
'https://your-api.example/etherfuse/launch-token',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ customerId }),
}
);
if (!response.ok) throw new Error('Could not create the launch token');
const body = (await response.json()) as { token: string };
return body.token;
};
const signAndSubmitTransaction: SignAndSubmitStellarTransaction = async (
unsignedXdr
) => {
// Your wallet owns signing and Horizon submission.
return walletAdapter.signAndSubmit(unsignedXdr); // => { hash: string }
};
export function SdkRoot({ children }: PropsWithChildren) {
return (
<EtherfuseProvider
client={client}
WebViewComponent={WebView}
launchTokenProvider={launchTokenProvider}
signAndSubmitTransaction={signAndSubmitTransaction}
>
<EtherfuseThemeProvider>{children}</EtherfuseThemeProvider>
</EtherfuseProvider>
);
}react-native-webview is required only when rendering hosted Etherfuse
content. The document picker, QR code, and clipboard integrations are supplied
to the specific screens that use them.
Use the flows
Each flow exposes one hook as its state machine. Render a screen from the
hook's current step, or ignore the provided screens and build your own UI.
Onboarding
const onboarding = useEtherfuseOnboarding({
customerId,
displayName: 'Ana Silva',
email: '[email protected]',
});| onboarding.step | Provided screen |
| ----------------- | ------------------------ |
| identity | IdentityScreen |
| documents | DocumentsScreen |
| launch | LaunchScreen |
| awaiting | AwaitingApprovalScreen |
| rejected | RejectedScreen |
| approved | ApprovedScreen |
| bank-account | BankAccountScreen |
| done | Return to your app |
DocumentsScreen receives a host-supplied pickDocuments() callback. Files
are uploaded as multipart form data, never base64 JSON.
On-ramp: BRL to Stellar
const onramp = useEtherfuseOnramp({
customerId,
bankAccountId,
walletAddress,
assetSymbol: 'TESOURO',
});| onramp.step | Provided screen |
| ------------- | ---------------------------- |
| value | OnrampValueScreen |
| review | OnrampReviewScreen |
| payment | PaymentInstructionsScreen |
| awaiting | AwaitingConfirmationScreen |
| claim | ClaimTokensScreen |
| done | OnrampCompletedScreen |
The asset symbol is resolved through GET /ramp/assets; issuer identifiers are
never hardcoded. PaymentInstructionsScreen accepts optional
QRCodeComponent and onCopy adapters. A claim step appears only when
Etherfuse returns a stellarClaimTransaction for a first-time wallet.
Off-ramp: Stellar to BRL
const offramp = useEtherfuseOfframp({
customerId,
bankAccountId,
walletAddress,
assetSymbol: 'TESOURO',
});Render OfframpPreflightScreen until preflightStatus === 'ok', then route by
offramp.step:
| State | Provided screen |
| ----------------------- | ------------------------ |
| Preflight not ready | OfframpPreflightScreen |
| value | OfframpValueScreen |
| review | OfframpReviewScreen |
| sign / broadcasting | OfframpSignBurnScreen |
| settling | OfframpSettlingScreen |
| done | OfframpCompletedScreen |
Off-ramp requires an existing trustline, sufficient asset balance, and enough XLM for Stellar reserves and fees. The hook checks Horizon before quoting and refreshes expiring burn XDRs while the user is signing.
Build a custom UI
The provided screens are optional. Hook results contain the state, actions, statuses, and normalized errors needed for a fully custom interface.
function CustomOnramp() {
const ramp = useEtherfuseOnramp({
customerId,
bankAccountId,
walletAddress,
assetSymbol: 'TESOURO',
});
return (
<YourAmountForm
loading={ramp.quoteStatus === 'submitting'}
error={ramp.error?.userMessage}
onSubmit={ramp.requestQuote}
/>
);
}For lower-level control, use EtherfuseClient directly:
const { assets } = await client.listAssets({
blockchain: 'stellar',
currency: 'BRL',
wallet: walletAddress,
});
const order = await client.getOrder(orderId);The client provides retries only for transient statuses, client-generated idempotency IDs, conflict recovery, WebSocket order tracking, and polling fallback.
Customize the UI
Every exported screen reads design tokens from EtherfuseThemeProvider.
Overrides merge by token group, so changing one value preserves the rest of
the default theme.
<EtherfuseThemeProvider
theme={{
colors: {
primary: '#00A868',
background: '#0B0B0F',
},
radius: { md: 12 },
}}
>
<YourRampFlow />
</EtherfuseThemeProvider>The default theme uses the ChatPay Go Labs palette. nocturneTheme is also
exported as a complete alternative. Fonts default to the platform system font;
load custom fonts in the host app before naming them in the theme.
Visible copy defaults to Brazilian Portuguese. Screen props expose overrides for titles, descriptions, actions, and other user-facing text.
Handle errors
All SDK errors extend EtherfuseError and expose safe fields for application
logic and UI. Raw API response bodies never cross the client boundary.
try {
await client.getOrder(orderId);
} catch (error) {
if (error instanceof EtherfuseError) {
console.log(error.status, error.retryable, error.userMessage);
}
}Available subclasses include authentication, validation, not-found, conflict,
transient, network, quote-expired, and Stellar submission errors. When Horizon
rejects a wallet submission, throw StellarSubmitError with its result code so
the off-ramp hook can recover from tx_too_late by requesting a fresh XDR.
Sandbox notes
- Create a business organization in the Etherfuse sandbox; personal organizations cannot create API keys.
- Sandbox on-ramps are capped at 500 in the quoted currency.
- Asset identifiers differ between sandbox and production. Resolve them from
GET /ramp/assets. - A customer can have only one BRL bank account.
simulateFiatReceived()is guarded and unavailable against production URLs.
Public API
The root package exports:
- Core:
EtherfuseClient,EtherfuseSocket,FetchTransport, Horizon preflight helpers, environment URLs, and idempotency helpers. - Context:
EtherfuseProvider,useEtherfuseClient, anduseEtherfuseContext. - Flows:
useEtherfuseOnboarding,useEtherfuseOnramp, anduseEtherfuseOfframp. - UI: onboarding, on-ramp, off-ramp, quote, status, preflight, hosted launch, and signing components.
- Theming:
EtherfuseThemeProvider,useTheme,defaultTheme,nocturneTheme,mergeTheme, and all public token types. - Errors and request/response types used by the supported Etherfuse endpoints.
All public types are exported from the package root. See
src/index.ts
for the authoritative export list.
Project documentation
Development
npm ci
npm run release:checkrelease:check runs TypeScript, ESLint, Prettier, the full test suite,
requirement traceability, and the ESM/CommonJS/type build. The same command
runs automatically before every npm publish.
Issues and feature requests are tracked in the
GitHub issue tracker.
License
MIT — Copyright 2026 ChatPay Go Labs.
