GIWA Chain SDK for React Native - Expo and React Native CLI compatible
npm install giwa-react-native-wallet


GIWA Chain SDK for React Native - Expo and React Native CLI compatible
| Feature | Hook | Description |
|---------|------|-------------|
| Wallet Management | useGiwaWallet | Create, recover, import, export wallets |
| Balance Query | useBalance | Check ETH and token balances |
| Transactions | useTransaction | Send ETH transactions |
| Token Operations | useTokens | ERC-20 token transfers and queries |
| Flashblocks | useFlashblocks | ~200ms fast preconfirmation |
| Dojang (EAS) | useDojang | On-chain attestation service |
| Faucet | useFaucet | Testnet ETH faucet |
| Network Info | useNetworkInfo | Network status and feature availability |
| Biometric Auth | useBiometricAuth | Face ID / Touch ID / Fingerprint |
| Secure Storage | - | iOS Keychain / Android Keystore |
These features are fully implemented in the SDK, but require smart contract deployment by the GIWA team.
| Feature | Hook | Status | Official Docs |
|---------|------|--------|---------------|
| L1 Bridge | useBridge | ENS contracts not deployed | GIWA Docs |
| GIWA ID | useGiwaId | L1 Bridge contract not deployed | GIWA Docs |
> 💡 Note: L2 Standard Bridge (l2StandardBridge) is available on OP Stack. Full L1↔L2 bridging requires L1 contract deployment.
>
> For bridge operations, you can use Superbridge in the meantime.
``bashnpm
npm install giwa-react-native-wallet expo-secure-store expo-local-authentication react-native-get-random-values
> Tip: You can also use
npx expo install for automatic Expo SDK version compatibility.$3
`bash
npm
npm install giwa-react-native-wallet react-native-keychain react-native-get-random-valuesyarn
yarn add giwa-react-native-wallet react-native-keychain react-native-get-random-valuespnpm
pnpm add giwa-react-native-wallet react-native-keychain react-native-get-random-values
``bash
iOS setup
cd ios && pod install
`Quick Start
$3
`tsx
import { GiwaProvider } from "giwa-react-native-wallet";export default function App() {
return (
{" "}
{/ config is optional, defaults to testnet /}
);
}
`$3
`tsx
import { useGiwaWallet } from "giwa-react-native-wallet";function WalletScreen() {
const { wallet, createWallet, recoverWallet } = useGiwaWallet();
// createWallet() - no parameters required
const handleCreate = async () => {
const { wallet, mnemonic } = await createWallet();
console.log("Address:", wallet.address);
// Save mnemonic securely!
};
return (
{wallet ? (
Address: {wallet.address}
) : (
)}
);
}
`$3
`tsx
import { useBalance } from "giwa-react-native-wallet";function BalanceScreen() {
// useBalance() - no parameters required, uses connected wallet
const { formattedBalance, refetch } = useBalance();
return (
Balance: {formattedBalance} ETH
);
}
`$3
`tsx
import { useTransaction } from "giwa-react-native-wallet";function SendScreen() {
const { sendTransaction, waitForReceipt, isLoading } = useTransaction();
const handleSend = async () => {
const hash = await sendTransaction({
to: "0x...",
value: "0.1", // ETH
});
const receipt = await waitForReceipt(hash);
console.log("Confirmed in block:", receipt.blockNumber);
};
return (
);
}
`$3
`tsx
import { useFlashblocks } from "giwa-react-native-wallet";function FastTransactionScreen() {
const { sendTransaction, getAverageLatency } = useFlashblocks();
const handleSend = async () => {
const { preconfirmation, result } = await sendTransaction({
to: "0x...",
value: BigInt("100000000000000000"), // 0.1 ETH in wei
});
console.log("Preconfirmed at:", preconfirmation.preconfirmedAt);
const receipt = await result.wait();
console.log("Confirmed at:", receipt.blockNumber);
console.log("Average latency:", getAverageLatency(), "ms");
};
return ;
}
`$3
`tsx
import { useGiwaId } from "giwa-react-native-wallet";function GiwaIdScreen() {
const { resolveAddress, resolveName } = useGiwaId();
const handleResolve = async () => {
// GIWA ID to address
const address = await resolveAddress("alice.giwa.id");
console.log("Address:", address);
// Address to GIWA ID
const name = await resolveName("0x...");
console.log("Name:", name);
};
return ;
}
`API Reference
$3
| Hook | Description | Status |
| ------------------ | --------------------------------------------------------- | ------ |
|
useGiwaWallet | Wallet management (create, recover, import, export) | ✅ |
| useBalance | ETH balance queries | ✅ |
| useTransaction | Send ETH transactions | ✅ |
| useTokens | ERC-20 token operations | ✅ |
| useBridge | L1↔L2 bridge operations | 🚧 |
| useFlashblocks | Fast preconfirmation transactions | ✅ |
| useGiwaId | GIWA ID (ENS) resolution | 🚧 |
| useDojang | Attestation verification | ✅ |
| useFaucet | Testnet faucet | ✅ |
| useNetworkInfo | Network status and feature availability | ✅ |
| useBiometricAuth | Biometric authentication (Face ID, Touch ID, Fingerprint) | ✅ |> 🚧 = Coming soon (contract deployment pending). See GIWA Docs for updates.
$3
`tsx
// Minimal - all defaults
// With options (all optional)
network="testnet" // optional, default: 'testnet'
initTimeout={10000} // optional, default: 10000 (ms)
onError={(e) => console.error(e)} // optional
>
`$3
You can override default network endpoints:
`tsx
config={{
network: 'testnet',
endpoints: {
rpcUrl: 'https://my-custom-rpc.example.com',
flashblocksRpcUrl: 'https://my-flashblocks-rpc.example.com',
flashblocksWsUrl: 'wss://my-flashblocks-ws.example.com',
explorerUrl: 'https://my-explorer.example.com',
},
}}
>
`Access endpoints at runtime:
`tsx
import { useNetworkInfo } from "giwa-react-native-wallet";function MyComponent() {
const { rpcUrl, flashblocksRpcUrl, flashblocksWsUrl, explorerUrl } =
useNetworkInfo();
// Use the resolved endpoints
}
`Network Selection
$3
`tsx
import { useNetworkInfo } from "giwa-react-native-wallet";function NetworkStatus() {
const {
network,
isTestnet,
isReady,
hasWarnings,
warnings,
isFeatureAvailable,
unavailableFeatures,
} = useNetworkInfo();
return (
Network: {network}
Testnet: {isTestnet ? "Yes" : "No"}
Ready: {isReady ? "Yes" : "No"}
{hasWarnings && (
Warnings:
{warnings.map((w, i) => (
- {w}
))}
)}
{/ Check specific feature availability /}
{!isFeatureAvailable("giwaId") && (
GIWA ID is not available on this network
)}
);
}
`$3
When using mainnet with TBD (not yet deployed) contracts, the SDK will log warnings:
`
[GIWA SDK] Network "mainnet" has 4 warning(s):
1. [WARNING] Mainnet is not fully ready. 4 feature(s) unavailable.
2. [WARNING] bridge: L1 Bridge contract is TBD
3. [WARNING] giwaId: ENS Registry/Resolver contracts are TBD
4. [WARNING] dojang: EAS/Schema Registry contracts are TBD
[GIWA SDK] Consider using "testnet" for development and testing.
`Network Information
$3
| Property | Value |
| --------------------- | ----------------------------------------- |
| Chain ID | 91342 |
| RPC URL |
https://sepolia-rpc.giwa.io |
| Flashblocks RPC | https://sepolia-rpc-flashblocks.giwa.io |
| Flashblocks WebSocket | wss://sepolia-rpc-flashblocks.giwa.io |
| Block Explorer | https://sepolia-explorer.giwa.io |
| Currency | ETH |$3
> ⚠️ Mainnet is currently under development. Please use testnet.
| Property | Value |
| -------------- | -------------------- |
| Chain ID | - |
| RPC URL | - |
| Block Explorer | - |
| Status | 🚧 Under Development |
Security
- Private keys and mnemonics are stored in iOS Keychain / Android Keystore
- Biometric authentication support for sensitive operations
- Secure storage required - SDK will not work without it
Testing
$3
`bash
Install dev dependencies
npm install --save-dev jest @testing-library/react-native @testing-library/react-hooksFor TypeScript
npm install --save-dev @types/jest ts-jest
`$3
`javascript
// jest.config.js
module.exports = {
preset: "react-native",
setupFilesAfterEnv: ["./jest.setup.js"],
transformIgnorePatterns: [
"node_modules/(?!(react-native|@react-native|giwa-react-native-wallet)/)",
],
moduleNameMapper: {
"^giwa-react-native-wallet$":
"/node_modules/giwa-react-native-wallet/dist/index.js",
},
};
`$3
`typescript
// jest.setup.js
jest.mock("expo-secure-store", () => ({
setItemAsync: jest.fn(),
getItemAsync: jest.fn(),
deleteItemAsync: jest.fn(),
isAvailableAsync: jest.fn(() => Promise.resolve(true)),
WHEN_UNLOCKED_THIS_DEVICE_ONLY: "WHEN_UNLOCKED_THIS_DEVICE_ONLY",
}));jest.mock("react-native-keychain", () => ({
setGenericPassword: jest.fn(() => Promise.resolve(true)),
getGenericPassword: jest.fn(() => Promise.resolve({ password: "test" })),
resetGenericPassword: jest.fn(() => Promise.resolve(true)),
getSupportedBiometryType: jest.fn(() => Promise.resolve("FaceID")),
ACCESS_CONTROL: { BIOMETRY_CURRENT_SET_OR_DEVICE_PASSCODE: "BIOMETRY" },
ACCESSIBLE: { WHEN_UNLOCKED_THIS_DEVICE_ONLY: "WHEN_UNLOCKED" },
BIOMETRY_TYPE: { FACE_ID: "FaceID", TOUCH_ID: "TouchID" },
}));
`$3
#### Testing Wallet Creation
`typescript
// __tests__/wallet.test.ts
import { renderHook, act } from "@testing-library/react-hooks";
import { useGiwaWallet } from "giwa-react-native-wallet";
import { GiwaProvider } from "giwa-react-native-wallet";const wrapper = ({ children }) => (
{children}
);
describe("useGiwaWallet", () => {
it("should create a new wallet", async () => {
const { result } = renderHook(() => useGiwaWallet(), { wrapper });
await act(async () => {
const { wallet, mnemonic } = await result.current.createWallet();
expect(wallet.address).toMatch(/^0x[a-fA-F0-9]{40}$/);
expect(mnemonic.split(" ")).toHaveLength(12);
});
});
it("should recover wallet from mnemonic", async () => {
const { result } = renderHook(() => useGiwaWallet(), { wrapper });
const testMnemonic =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
await act(async () => {
const wallet = await result.current.recoverWallet(testMnemonic);
expect(wallet.address).toBe("0x9858EfFD232B4033E47d90003D41EC34EcaEda94");
});
});
it("should throw error for invalid mnemonic", async () => {
const { result } = renderHook(() => useGiwaWallet(), { wrapper });
await expect(
result.current.recoverWallet("invalid mnemonic phrase")
).rejects.toThrow("Invalid recovery phrase.");
});
});
`#### Testing Balance Hook
`typescript
// __tests__/balance.test.ts
import { renderHook, waitFor } from "@testing-library/react-hooks";
import { useBalance } from "giwa-react-native-wallet";// Mock viem client
jest.mock("viem", () => ({
...jest.requireActual("viem"),
createPublicClient: () => ({
getBalance: jest.fn(() => Promise.resolve(BigInt("1000000000000000000"))),
}),
}));
describe("useBalance", () => {
it("should fetch ETH balance", async () => {
const { result } = renderHook(() => useBalance("0x1234..."), { wrapper });
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.formattedBalance).toBe("1.0");
expect(result.current.balance).toBe(BigInt("1000000000000000000"));
});
});
`#### Testing Token Transfer
`typescript
// __tests__/tokens.test.ts
import { renderHook, act } from "@testing-library/react-hooks";
import { useTokens } from "giwa-react-native-wallet";describe("useTokens", () => {
it("should transfer tokens", async () => {
const { result } = renderHook(() => useTokens(), { wrapper });
await act(async () => {
const hash = await result.current.transfer(
"0xTokenAddress...",
"0xRecipient...",
"100"
);
expect(hash).toMatch(/^0x[a-fA-F0-9]{64}$/);
});
});
it("should get token balance", async () => {
const { result } = renderHook(() => useTokens(), { wrapper });
await act(async () => {
const balance = await result.current.getBalance("0xTokenAddress...");
expect(balance.token.symbol).toBeDefined();
expect(balance.formattedBalance).toBeDefined();
});
});
});
`#### Testing GIWA ID Resolution
`typescript
// __tests__/giwaId.test.ts
import { renderHook, act } from "@testing-library/react-hooks";
import { useGiwaId } from "giwa-react-native-wallet";describe("useGiwaId", () => {
it("should resolve GIWA ID to address", async () => {
const { result } = renderHook(() => useGiwaId(), { wrapper });
await act(async () => {
const address = await result.current.resolveAddress("alice.giwa.id");
expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/);
});
});
it("should return null for non-existent GIWA ID", async () => {
const { result } = renderHook(() => useGiwaId(), { wrapper });
await act(async () => {
const address = await result.current.resolveAddress(
"nonexistent.giwa.id"
);
expect(address).toBeNull();
});
});
});
`$3
`typescript
// __tests__/integration/fullFlow.test.ts
import { renderHook, act } from "@testing-library/react-hooks";
import {
useGiwaWallet,
useBalance,
useTransaction,
} from "giwa-react-native-wallet";describe("Full Wallet Flow", () => {
it("should complete full transaction flow", async () => {
// 1. Create wallet
const { result: walletResult } = renderHook(() => useGiwaWallet(), {
wrapper,
});
let walletAddress: string;
await act(async () => {
const { wallet } = await walletResult.current.createWallet();
walletAddress = wallet.address;
});
// 2. Check balance
const { result: balanceResult } = renderHook(
() => useBalance(walletAddress),
{ wrapper }
);
await waitFor(() => {
expect(balanceResult.current.balance).toBeDefined();
});
// 3. Send transaction (if balance > 0)
if (balanceResult.current.balance > 0n) {
const { result: txResult } = renderHook(() => useTransaction(), {
wrapper,
});
await act(async () => {
const hash = await txResult.current.sendTransaction({
to: "0xRecipient...",
value: "0.001",
});
expect(hash).toBeDefined();
});
}
});
});
`$3
`typescript
// e2e/wallet.e2e.ts
describe("Wallet E2E", () => {
beforeAll(async () => {
await device.launchApp();
}); it("should create wallet and show address", async () => {
await element(by.id("create-wallet-button")).tap();
await expect(element(by.id("wallet-address"))).toBeVisible();
});
it("should show balance after wallet creation", async () => {
await expect(element(by.id("balance-display"))).toBeVisible();
});
});
`$3
`bash
Run all tests
npm testRun with coverage
npm test -- --coverageRun specific test file
npm test -- wallet.test.tsRun in watch mode
npm test -- --watch
``- React >= 19.0.0
- React Native >= 0.77.0
- Expo SDK >= 53 (for Expo projects)
- expo-secure-store >= 15.0.0 (Expo)
- expo-local-authentication >= 14.0.0 (Expo, for biometrics)
- react-native-keychain >= 9.2.0 (React Native CLI)
- react-native-get-random-values >= 1.11.0
| Resource | URL |
| ------------------------ | --------------------------------------------------------- |
| GIWA Documentation | https://docs.giwa.io |
| SDK Documentation | https://dev-eyoungmin.github.io/giwa-react-native-wallet/ |
| Sample App (GitHub) | https://github.com/dev-eyoungmin/giwa-react-native-samples |
| Testnet Faucet | https://faucet.giwa.io |
| Block Explorer (Testnet) | https://sepolia-explorer.giwa.io |
| Block Explorer (Mainnet) | - |
| Bridge (Superbridge) | https://superbridge.app |
MIT