Awarizon
DOCS/@awarizon/web3

@awarizon/web3

Core SDK. Works in Node 18+, Deno, Bun, Edge runtimes, and any browser bundler. Zero framework dependencies.

new AwarizonWeb3(config)

Creates an SDK instance. All .contract() calls share the same connection pool.

TS
import { AwarizonWeb3 } from "@awarizon/web3"

const awarizon = new AwarizonWeb3({
  chain:   "base",         // required — target EVM chain
  apiKey:  "awz_live_...", // required — from your dashboard
  timeout: 30_000,         // optional, ms  (default: 30 000)
  retries: 3,              // optional      (default: 3)
})
chainSupportedChainREQEVM chain identifier string. See Supported Chains.
apiKeystringREQAPI key from the dashboard. Format: "awz_live_..."
timeoutnumberRequest timeout in milliseconds. Default 30 000.
retriesnumberAutomatic retries on transient RPC errors. Default 3.
rpcUrlstringCustom RPC endpoint override.
signerWalletClientPre-connected viem WalletClient — equivalent to calling connectWallet() after construction.

awz.contract(options) → ContractInstance

Binds the SDK to a deployed contract. Returns a fully-typed ContractInstance where every ABI function is a direct method call.

TS
const token = await awarizon.contract({
  address: "0xContractAddress", // EIP-55 checksum address
  abi:     ABI_ARRAY,            // standard JSON ABI
})

// Every ABI function becomes a direct method — reads and writes
const symbol  = await token.symbol()                   // string
const balance = await token.balanceOf(ownerAddress)    // bigint
const tx      = await token.transfer(to, amount)       // TransactionResult

ERC Standard Clients

Load ERC-20, ERC-721, and ERC-1155 contracts with zero ABI imports. Every method is fully typed — your editor autocompletes the entire standard interface.

TS
// ── ERC-20 Token ──────────────────────────────────────────────────────────────
const usdc = await awarizon.erc20("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")

const name     = await usdc.name()              // "USD Coin"
const symbol   = await usdc.symbol()            // "USDC"
const decimals = await usdc.decimals()          // 6
const supply   = await usdc.totalSupply()       // bigint
const balance  = await usdc.balanceOf(owner)    // bigint
const allowed  = await usdc.allowance(owner, spender)

await usdc.transfer(recipient, 1_000_000n)      // 1 USDC
await usdc.approve(spender, 500_000n)
await usdc.transferFrom(from, to, 1_000_000n)

// ── ERC-721 NFT Collection ────────────────────────────────────────────────────
const nft = await awarizon.erc721("0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D")

const owner = await nft.ownerOf(1n)             // `0x${string}`
const uri   = await nft.tokenURI(1n)            // "ipfs://..."
const count = await nft.balanceOf(wallet)       // bigint

await nft.safeTransferFrom(from, to, 1n)
await nft.approve(operator, 1n)
await nft.setApprovalForAll(operator, true)

// ── ERC-1155 Multi-Token ──────────────────────────────────────────────────────
const items = await awarizon.erc1155("0x...")

const qty     = await items.balanceOf(user, 42n)
const uri     = await items.uri(42n)
const batched = await items.balanceOfBatch([u1, u2], [1n, 2n])

await items.safeTransferFrom(from, to, 42n, 1n, "0x")
await items.setApprovalForAll(operator, true)

Contract Registry

Register contracts once by name at startup. Reference them anywhere in your app without repeating addresses or ABIs.

TS
// Register at startup — returns `this` for chaining
awarizon
  .register("USDC",  { address: "0x...", abi: erc20Abi  })
  .register("Vault", { address: "0x...", abi: vaultAbi  })
  .register("NFT",   { address: "0x...", abi: erc721Abi })

// Use by name anywhere — address and ABI never repeated again
const usdc  = await awarizon.use("USDC")
const vault = await awarizon.use("Vault")
const nft   = await awarizon.use("NFT")

await usdc.transfer(recipient, 100_000n)
await vault.deposit(1_000n)

awarizon.registeredContracts() // ["USDC", "Vault", "NFT"]
awarizon.unregister("Vault")

Reading state

Call any view or pure function directly. Zero gas, no wallet required.

TS
// Single return value
const balance  = await contract.balanceOf(ownerAddress) // bigint
const symbol   = await contract.symbol()                // string
const decimals = await contract.decimals()              // bigint

// Tuple return — destructure directly
const [reserve0, reserve1] = await contract.getReserves()

// Struct — returned as Record<string, unknown>
const position = await contract.positions(tokenId)

Writing transactions

nonpayable and payable functions return a TransactionResult immediately after broadcast. Call .wait() to block until confirmed.

TS
// Broadcast and get hash immediately
const tx = await contract.transfer(recipientAddress, 1_000_000n)
console.log("broadcasted:", tx.hash) // 0x...

// Wait for on-chain confirmation (1 block)
const receipt = await tx.wait()
console.log("block:",    receipt.blockNumber)
console.log("gas used:", receipt.gasUsed)
console.log("status:",   receipt.status)  // "success" | "reverted"

// Payable function — attach ETH via options object (last arg)
const tx2 = await contract.deposit({ value: 1_000_000_000_000_000n }) // 0.001 ETH

// Custom gas limit
const tx3 = await contract.mint(quantity, { value: price, gas: 200_000n })

Events

Subscribe to real-time contract events. The SDK returns an unsubscribe function — always call it on cleanup.

TS
// Subscribe to a named event
const unsubscribe = contract.on("Transfer", (log) => {
  console.log("from:",  log.args.from)
  console.log("to:",    log.args.to)
  console.log("value:", log.args.value)
})

// Unsubscribe (e.g. on component unmount)
unsubscribe()

// Wildcard — receive all events from this contract
const unsubAll = contract.on("*", (log) => {
  console.log(log.name, log.args)
})

Gas estimation

Simulate a write before broadcasting to get the estimated gas cost.

TS
const gas = await contract.estimateGas(
  "transfer",        // function name
  recipientAddress,  // ...args
  1_000_000n,
)
console.log("estimated gas:", gas) // 21000n

Built-in Wallet

The SDK ships a full self-custodial wallet — generate new wallets, import from a 12 or 24-word BIP-39 mnemonic, or load from a raw private key. No browser extension or external library required.

TS
// ── Create a brand-new wallet ─────────────────────────────────────────────────
const created = await awarizon.wallet.create()
console.log(created.address)    // "0xAbCd..."
console.log(created.privateKey) // "0x..."  ← returned once — store it securely

// ── Import from a BIP-39 mnemonic (12 or 24 words) ────────────────────────────
const fromPhrase = await awarizon.wallet.importMnemonic(
  "word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12",
  0, // optional HD account index (default 0)
)

// ── Import from a hex private key ─────────────────────────────────────────────
const fromKey = await awarizon.wallet.importPrivateKey("0x...")

// Once any wallet is loaded, all write calls use it automatically:
const tx = await usdc.transfer(recipient, 1_000_000n)
WALLET ACCESSORS
wallet.address()AddressReturns the active wallet address. Throws WalletNotConnectedError if no wallet is loaded.
wallet.isConnected()booleanTrue when any wallet — internal or external — is active.
wallet.hasInternalWallet()booleanTrue when a locally generated or imported key/mnemonic wallet is loaded.
wallet.hasExternalWallet()booleanTrue when an external WalletClient (wagmi, RainbowKit) has been injected.
wallet.disconnect()voidClears all wallet state — both internal keys and external connections.
getWalletInfo()WalletInfoReturns { address, chain, isExternal } — useful for displaying connected wallet state.
⚠️

Never store private keys or mnemonic phrases in plaintext, localStorage, or client-side variables. On mobile use expo-secure-store via @awarizon/react-native. On server, load from environment variables only.

External Wallets — wagmi & RainbowKit

Inject any viem-compatible WalletClient from wagmi or RainbowKit. A one-line sync component keeps the Awarizon SDK in lockstep with whatever wallet the user selects in wagmi — account switches and chain changes included.

wagmi — useWalletClient()

TSX
import { useEffect } from "react"
import { useWalletClient } from "wagmi"
import { useSDK } from "@awarizon/react"

// Drop this inside any component that lives under both <WagmiProvider> and <AwarizonProvider>
function WalletSync() {
  const awarizon = useSDK()
  const { data: walletClient } = useWalletClient()

  useEffect(() => {
    if (walletClient) awarizon.connectWallet(walletClient)
    else              awarizon.disconnectWallet()
  }, [walletClient, awarizon])

  return null  // renders nothing — purely a sync effect
}

RainbowKit — WalletSync + ConnectButton

TSX
import { useEffect } from "react"
import { useWalletClient } from "wagmi"
import { ConnectButton } from "@rainbow-me/rainbowkit"
import { useSDK } from "@awarizon/react"

// Syncs the active wagmi wallet into Awarizon whenever it changes
function WalletSync() {
  const awarizon = useSDK()
  const { data: walletClient } = useWalletClient()
  useEffect(() => {
    if (walletClient) awarizon.connectWallet(walletClient)
    else              awarizon.disconnectWallet()
  }, [walletClient, awarizon])
  return null
}

// In your layout — WalletSync is invisible, ConnectButton handles the UI
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <WalletSync />          {/* keeps Awarizon in sync with RainbowKit */}
      <ConnectButton />       {/* RainbowKit's button — shows connected address */}
      {children}
    </>
  )
}

EIP-1193 — Browser Wallet Direct Connect

Connect any injected browser wallet (MetaMask, Rabby, Brave, Coinbase, Phantom) directly via connectEIP1193Provider() — no wagmi, no extra dependencies.

// One call — requests accounts + subscribes to account/chain events
await awarizon.connectEIP1193Provider(window.ethereum, {
  name: "MetaMask", // optional display name
})

// All subsequent writes use this wallet
const { hash } = await token.transfer("0x...", 100n)

// Detect the injected wallet
function detectWalletName(p: Record<string, unknown>): string {
  if (p.isMetaMask && !p.isRabby) return "MetaMask"
  if (p.isRabby)                  return "Rabby"
  if (p.isBraveWallet)            return "Brave Wallet"
  if (p.isCoinbaseWallet)         return "Coinbase Wallet"
  if (p.isPhantom)                return "Phantom"
  return "Browser Wallet"
}
💡

If you use <ConnectButton /> from @awarizon/react, this is handled for you automatically — including wallet detection, WalletConnect QR, and the account popup.

WalletConnect — MetaMask · Trust · Rainbow · Coinbase

WalletConnect v2 is built into the SDK — no RainbowKit or wagmi required. Pass walletConnectProjectId at init to unlock direct connections to MetaMask Mobile, Trust Wallet, Rainbow, Coinbase Wallet, Uniswap, and any other WalletConnect-compatible app. Get a free project ID at cloud.walletconnect.com.

TS
const awarizon = new AwarizonWeb3({
  chain:                  "base",
  apiKey:                 "awz_live_...",
  walletConnectProjectId: "YOUR_WC_PROJECT_ID",  // from cloud.walletconnect.com
})

Calling connectWalletConnect() fires onDisplayUri with a wc:// URI string. On desktop, render it as a QR code. On mobile, turn it into a deep link that opens the user's wallet app directly.

Desktop — QR code (user scans from mobile wallet app)

import QRCode from "react-qr-code"  // or any QR library
import { useState } from "react"
import { useSDK } from "@awarizon/react"

function ConnectDesktop() {
  const awarizon  = useSDK()
  const [uri, setUri] = useState<string | null>(null)

  async function connect() {
    await awarizon.connectWalletConnect({
      onDisplayUri: (wcUri) => setUri(wcUri),
    })
    setUri(null)  // clear QR once connected
  }

  if (uri) return (
    <div>
      <p>Scan with MetaMask, Trust, Rainbow, Coinbase, or any wallet app</p>
      <QRCode value={uri} size={240} />
    </div>
  )

  return <button onClick={connect}>Connect Wallet</button>
}

Mobile — deep link buttons (opens the wallet app directly)

import { useState } from "react"
import { useSDK } from "@awarizon/react"

// WalletConnect v2 deep-link prefixes — each wallet registers its own URI scheme
const WALLETS = [
  { name: 'MetaMask',        scheme: 'metamask://wc?uri=',   icon: '🦊' },
  { name: 'Trust Wallet',    scheme: 'trust://wc?uri=',      icon: '🛡️' },
  { name: 'Rainbow',         scheme: 'rainbow://wc?uri=',    icon: '🌈' },
  { name: 'Coinbase Wallet', scheme: 'cbwallet://wc?uri=',   icon: '🔵' },
  { name: 'Uniswap Wallet',  scheme: 'uniswap://wc?uri=',   icon: '🦄' },
]

function ConnectMobile() {
  const awarizon   = useSDK()
  const [uri, setUri] = useState<string | null>(null)

  async function startConnect() {
    await awarizon.connectWalletConnect({
      onDisplayUri: (wcUri) => setUri(wcUri),
    })
    setUri(null)  // clear once approved
  }

  if (uri) {
    return (
      <div>
        <p>Choose your wallet:</p>
        {WALLETS.map(w => (
          <a
            key={w.name}
            href={`${w.scheme}${encodeURIComponent(uri)}`}
            style={{ display: "block", margin: "8px 0" }}
          >
            {w.icon} Open in {w.name}
          </a>
        ))}
      </div>
    )
  }

  return <button onClick={startConnect}>Connect Mobile Wallet</button>
}
💡

Detect navigator.userAgent at runtime to show QR (desktop) or deep-link buttons (mobile) automatically. <ConnectButton /> from @awarizon/react does this for you — it renders the correct UI for each platform without any extra setup.

MetaMask Mobilemetamask://wc?uri=...iOS · Android
Trust Wallettrust://wc?uri=...iOS · Android
Rainbowrainbow://wc?uri=...iOS · Android
Coinbase Walletcbwallet://wc?uri=...iOS · Android
Uniswap Walletuniswap://wc?uri=...iOS · Android
Any WC v2 walletUniversal QR code fallbackAll platforms

Live Wallet & Chain Tracking

When connected via connectEIP1193Provider() or connectWalletConnect(), the SDK subscribes to provider events automatically. No polling — the SDK state stays in sync with whatever the user does in their wallet app.

AUTO-HANDLED PROVIDER EVENTS
accountsChangedstring[]New account selected → rebuilds WalletClient, fires onWalletChange.
accountsChanged[]Wallet locked / disconnected → calls disconnectExternal().
chainChangedstringUser switched networks → fires onChainChange.
disconnect—Provider disconnected → calls disconnectExternal().
TS
// Subscribe outside React if needed
const unsub  = awarizon.wallet.onWalletChange(({ address, type }) => {
  console.log("wallet:", address, type)  // "external" | "internal" | "none"
})
const unsub2 = awarizon.wallet.onChainChange(({ chainId }) => {
  console.log("chain:", chainId)
})
unsub(); unsub2()
💡

In React, useWallet() subscribes to both events automatically — address, chainId, and isChainMismatch update in real time without any setup.

Multicall — batch reads

Batch multiple view/pure reads into a single RPC round-trip via multicall3. Critical for dashboards that need many values at once — 10 reads becomes 1 request.

TS
// 3 reads → 1 network request
const [balance, symbol, decimals] = await awarizon.multicall([
  { address: USDC, abi: erc20Abi, method: "balanceOf", args: [userAddress] },
  { address: USDC, abi: erc20Abi, method: "symbol"                         },
  { address: USDC, abi: erc20Abi, method: "decimals"                       },
])

// Dashboard pattern — whole portfolio in one shot
const [ethBal, usdcBal, daiSupply, nftOwner] = await awarizon.multicall([
  { address: WETH, abi: erc20Abi,  method: "balanceOf", args: [wallet] },
  { address: USDC, abi: erc20Abi,  method: "balanceOf", args: [wallet] },
  { address: DAI,  abi: erc20Abi,  method: "totalSupply"               },
  { address: NFT,  abi: erc721Abi, method: "ownerOf",   args: [1n]     },
])