npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nexus-cross/crossx-sdk-react

v1.0.9

Published

CROSSx React SDK - React Hooks and Components for Embedded Wallet

Downloads

798

Readme

@nexus-cross/crossx-sdk-react

CROSSx Embedded Wallet을 React 앱에 통합하기 위한 Provider + Hooks 패키지.

wagmi를 사용하는 경우 @nexus-cross/crossx-sdk-wagmi를 대신 사용하세요. 이 패키지는 wagmi 없이 순수 React만으로 CROSSx 지갑을 사용할 때 적합합니다.


패키지 구조

@nexus-cross/crossx-sdk-core     ← 핵심 SDK (인증, 서명, 트랜잭션)
@nexus-cross/crossx-sdk-react    ← React Hooks & Provider (이 패키지)
@nexus-cross/crossx-sdk-wagmi    ← wagmi Connector (wagmi 사용 시)

설치

# pnpm (monorepo 내부)
pnpm add @nexus-cross/crossx-sdk-react

# npm
npm install @nexus-cross/crossx-sdk-react

peerDependenciesreact ^18.0.0이 필요합니다.


환경 변수

.env 파일에 아래 값을 설정합니다. SDK 내부에서 자동으로 읽습니다.

# OAuth 서비스 URL (로그인용) — 필수
VITE_OAUTH_SERVICE_URL=https://dev-cross-wallet-oauth.crosstoken.io

# JWT 검증 API URL — 필수
VITE_AUTH_API_URL=https://dev-cross-auth.crosstoken.io

# Embedded Wallet Gateway URL (지갑 기능용) — 필수
VITE_WALLET_GATEWAY_URL=https://dev-embedded-wallet-gateway.crosstoken.io/api/v1

URL은 SDKConfig에 노출하지 않습니다. 환경 변수로만 전환 가능합니다. (외부 개발사에는 위 설정을 알리지 않을 예정)


Quick Start

1. Provider 설정

앱 최상위에 CROSSxProvider를 배치합니다. config에는 SDKConfig 객체를 전달합니다.

// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { CROSSxProvider } from '@nexus-cross/crossx-sdk-react';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <CROSSxProvider config={{}}>
      <App />
    </CROSSxProvider>
  </React.StrictMode>
);

Provider가 마운트되면 내부적으로 createCROSSxSDK(config)sdk.init()을 자동 호출합니다. 기존 세션이 있으면 자동으로 복원됩니다.

2. 로그인 / 로그아웃 — useAuth

import { useAuth } from '@nexus-cross/crossx-sdk-react';

function LoginButton() {
  const { signIn, signOut, isAuthenticated, isLoading, error } = useAuth();

  if (isLoading) return <p>처리 중...</p>;

  return isAuthenticated ? (
    <button onClick={signOut}>로그아웃</button>
  ) : (
    <div>
      <button onClick={signIn}>로그인</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

3. 서명 / 전송 — useWallet

import { useWallet } from '@nexus-cross/crossx-sdk-react';
import { ChainId } from '@nexus-cross/crossx-sdk-react';

function WalletActions() {
  const { address, signMessage, sendTransaction, isLoading, error } = useWallet();

  const handleSign = async () => {
    // chainId는 CAIP-2 형식 문자열 (예: 'eip155:612055')
    const result = await signMessage(ChainId.CROSS_MAINNET, 'Hello CROSSx!');
    console.log('signature:', result.signature);
  };

  const handleSend = async () => {
    const result = await sendTransaction(ChainId.CROSS_MAINNET, {
      from: address!,
      to: '0x920A31f0E48739C3FbB790D992b0690f7F5C42ea',
      value: '0x2386f26fc10000', // 0.01 CROSS (hex wei)
      gasLimit: '0x5208',
      maxFeePerGas: '0x77359400',
      maxPriorityFeePerGas: '0x3b9aca00',
    });
    console.log('txHash:', result.txHash);
  };

  return (
    <div>
      <p>주소: {address ?? '—'}</p>
      <button onClick={handleSign} disabled={isLoading}>메시지 서명</button>
      <button onClick={handleSend} disabled={isLoading}>전송</button>
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
}

4. SDK 직접 접근 — useCROSSx

hooks로 커버되지 않는 기능(잔액 조회, nonce 조회, RPC 요청 등)은 useCROSSx로 SDK 인스턴스에 직접 접근합니다.

import { useCROSSx } from '@nexus-cross/crossx-sdk-react';

function AdvancedPanel() {
  const { sdk, isInitialized, isAuthenticated, walletAddress } = useCROSSx();

  const handleGetBalance = async () => {
    if (!sdk) return;
    const { formatted } = await sdk.getBalance('eip155:612055');
    console.log('잔액:', formatted, 'CROSS');
  };

  const handleSignTypedData = async () => {
    if (!sdk) return;
    const result = await sdk.signTypedData('eip155:612055', {
      types: {
        EIP712Domain: [
          { name: 'name', type: 'string' },
          { name: 'version', type: 'string' },
          { name: 'chainId', type: 'uint256' },
          { name: 'verifyingContract', type: 'address' },
        ],
        Permit: [
          { name: 'owner', type: 'address' },
          { name: 'spender', type: 'address' },
          { name: 'value', type: 'uint256' },
          { name: 'nonce', type: 'uint256' },
          { name: 'deadline', type: 'uint256' },
        ],
      },
      primaryType: 'Permit',
      domain: {
        name: 'MyToken',
        version: '1',
        chainId: 612055,
        verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
      },
      message: {
        owner: walletAddress,
        spender: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
        value: '1000000000000000000',
        nonce: '0',
        deadline: '1234567890',
      },
    });
    console.log('typed data signature:', result.signature);
  };

  const handleRpcCall = async () => {
    if (!sdk) return;
    // eth_call로 ERC-20 balanceOf 직접 호출
    const data = '0x70a08231000000000000000000000000' +
      walletAddress!.slice(2).toLowerCase().padStart(64, '0');
    const raw = await sdk.walletRpc(
      'eth_call',
      [{ to: '0x9f85c7b5d7637e18f946cc8af9c131318c6833d9', data }, 'latest'],
      'eip155:612044'
    );
    console.log('balanceOf result:', raw);
  };

  if (!isInitialized) return <p>초기화 중...</p>;

  return (
    <div>
      <p>인증: {isAuthenticated ? '✅' : '❌'}</p>
      <p>주소: {walletAddress ?? '—'}</p>
      <button onClick={handleGetBalance}>잔액 조회</button>
      <button onClick={handleSignTypedData}>EIP-712 서명</button>
      <button onClick={handleRpcCall}>eth_call (tUSDT)</button>
    </div>
  );
}

API Reference

<CROSSxProvider>

| Prop | 타입 | 설명 | |------|------|------| | config | SDKConfig | SDK 설정 객체 | | children | ReactNode | 자식 컴포넌트 |

SDKConfig 주요 필드:

| 필드 | 타입 | 기본값 | 설명 | |------|------|--------|------| | theme | 'light' \| 'dark' | 'light' | 확인 모달 테마 | | themeTokens | ConfirmationTokenOverride | — | 모달 색상 커스터마이징 | | oauthDisplayMode | 'popup' \| 'modal' | 'popup' | OAuth 로그인 표시 방식 | | useMockWallet | boolean | false | Mock 지갑 사용 (개발용) | | debug | boolean | true | 디버그 로그 출력 |

ConfirmationTokenOverride 구조:

themeTokens: {
  light?: ConfirmationThemeTokens;  // 라이트 모드 오버라이드
  dark?:  ConfirmationThemeTokens;  // 다크 모드 오버라이드
}

ConfirmationThemeTokens 오버라이드 가능 필드:

| 필드 | Semantic 토큰 | 기본값 (light / dark) | |------|------|------| | primary | Primary | #019D92 / #019D92 | | secondary | Secondary | #E70077 / #E70077 | | onPrimary | OnPrimary | #FFFFFF / #FFFFFF | | borderDefault | Border/Default | rgba(18,18,18,0.05) / rgba(255,255,255,0.05) | | borderSubtle | Border/Subtle | rgba(18,18,18,0.1) / rgba(255,255,255,0.1) | | textPrimary | TextIcon/Primary | #121212 / #FFFFFF | | textSecondary | TextIcon/Secondary | rgba(18,18,18,0.7) / rgba(255,255,255,0.7) | | textTertiary | TextIcon/Tertiary | rgba(18,18,18,0.5) / rgba(255,255,255,0.5) | | surfaceDefault | Surface/default | rgba(18,18,18,0.05) / rgba(255,255,255,0.05) | | bg | Surface/BG | #FFFFFF / #121212 |


useAuth()

인증(로그인/로그아웃) 관련 상태와 액션을 제공합니다.

const {
  isAuthenticated,  // boolean — 현재 인증 상태
  isLoading,        // boolean — 비동기 작업 진행 중
  error,            // string | null — 마지막 에러 메시지
  signIn,           // () => Promise<AuthResult> — OAuth 로그인
  signOut,          // () => Promise<void> — 로그아웃
} = useAuth();

signIn() 호출 시 OAuth 팝업/모달이 열립니다. 성공하면 내부적으로 window.location.reload()가 호출되어 Provider 상태가 갱신됩니다.


useWallet()

지갑 주소, 메시지 서명, 트랜잭션 전송 기능을 제공합니다.

const {
  address,          // string | null — 지갑 주소 (0x...)
  isLoading,        // boolean — 비동기 작업 진행 중
  error,            // string | null — 마지막 에러 메시지
  signMessage,      // (chainId, message) => Promise<SignMessageResult>
  sendTransaction,  // (chainId, tx) => Promise<TransactionResult>
} = useWallet();

signMessage(chainId, message)

| 파라미터 | 타입 | 예시 | |----------|------|------| | chainId | string | 'eip155:612055' 또는 ChainId.CROSS_MAINNET | | message | string | 'Hello CROSSx!' |

반환: { chainId, signature, message, address }

sendTransaction(chainId, tx)

| 파라미터 | 타입 | 설명 | |----------|------|------| | chainId | string | CAIP-2 체인 ID | | tx | EvmTransactionRequest | 트랜잭션 객체 |

EvmTransactionRequest 주요 필드:

| 필드 | 타입 | 필수 | 설명 | |------|------|:----:|------| | from | string | O | 보내는 주소 | | to | string | O | 받는 주소 | | value | string | | hex wei (예: '0x2386f26fc10000') | | data | string | | calldata hex | | gasLimit | string | | gas limit hex | | maxFeePerGas | string | | EIP-1559 max fee | | maxPriorityFeePerGas | string | | EIP-1559 priority fee | | nonce | number | | 트랜잭션 순서 |

반환: { chainId, txHash, status }


useCROSSx()

SDK 인스턴스와 전역 상태에 직접 접근합니다.

const {
  sdk,              // CROSSxSDK | null — SDK 인스턴스
  isInitialized,    // boolean — init() 완료 여부
  isAuthenticated,  // boolean — 인증 상태
  walletAddress,    // string | null — 지갑 주소
} = useCROSSx();

sdk 인스턴스를 통해 호출할 수 있는 주요 메서드:

| 메서드 | 설명 | |--------|------| | sdk.signMessage(chainId, message) | EIP-191 메시지 서명 | | sdk.signTypedData(chainId, typedData) | EIP-712 Typed Data 서명 | | sdk.signTransaction(chainId, tx) | 트랜잭션 서명 (전송 없음) | | sdk.sendTransaction(chainId, tx) | 트랜잭션 서명 + 전송 | | sdk.sendTransactionAndWait(chainId, tx) | 전송 + Receipt 폴링 | | sdk.getBalance(chainId) | 네이티브 잔액 조회 | | sdk.getNonce(chainId) | 현재 nonce 조회 | | sdk.walletRpc(method, params, chainId) | 범용 JSON-RPC 호출 | | sdk.getProvider(chainId) | EIP-1193 Provider 반환 | | sdk.createWallet() | 수동 지갑 생성 | | sdk.setTheme('dark') | 확인 모달 테마 런타임 전환 (themeTokens 오버라이드 유지) |


ChainId 상수

체인 ID 오타 방지를 위한 상수를 제공합니다.

import { ChainId } from '@nexus-cross/crossx-sdk-react';

ChainId.CROSS_MAINNET  // 'eip155:612055'
ChainId.CROSS_TESTNET  // 'eip155:612044'

wagmi와의 차이

| | @nexus-cross/crossx-sdk-react | @nexus-cross/crossx-sdk-wagmi | |---|---|---| | 의존성 | React만 필요 | wagmi + viem + @tanstack/react-query | | 접근 방식 | CROSSx 전용 hooks | wagmi 표준 hooks (useAccount, useConnect 등) | | EIP-1193 | 필요 시 sdk.getProvider() | 자동 제공 (wagmi connector) | | 적합 대상 | CROSSx 지갑만 사용하는 앱 | 멀티 지갑(MetaMask 등) 지원 앱 |

wagmi를 사용하는 경우 @nexus-cross/crossx-sdk-wagmicreateCROSSxConnector를 사용하세요. wagmi 예제는 examples/wagmi-app을 참고하세요.


확인 모달 (Confirmation)

서명/전송 시 자동으로 확인 모달이 표시됩니다.

  • Message Sign — "Signature Request" 모달 (Cancel / Confirm)
  • EIP-712 Typed Data — 구조화된 key-value 표시
  • Transaction Sign/Send — 수신자, 금액, 수수료 표시

모바일(480px 이하)에서는 자동으로 하단 바텀시트로 전환됩니다.

테마 설정

초기화 시 고정:

<CROSSxProvider config={{ theme: 'dark' }}>

런타임 전환:

const { sdk } = useCROSSx();
sdk?.setTheme('dark'); // 다음 모달부터 적용. themeTokens 오버라이드는 유지됨

색상 커스터마이징

themeTokens.light / themeTokens.dark 로 각 모드를 독립적으로 오버라이드합니다. 지정하지 않은 항목은 해당 모드의 기본값을 유지합니다.

<CROSSxProvider
  config={{
    theme: 'light',
    themeTokens: {
      light: {
        primary: '#FF6B35',  // 버튼·강조색 (기본: #019D92)
        bg:      '#F5F0EB',  // 카드 배경색 (기본: #FFFFFF)
      },
      dark: {
        primary: '#FF6B35',
        bg:      '#1A0A00',  // 카드 배경색 (기본: #121212)
      },
    },
  }}
>

특정 모드만 지정하는 것도 가능합니다.

// 라이트 모드만 커스터마이징
themeTokens: { light: { primary: '#FF6B35' } }

마이그레이션 (자동)

이전 CROSSx 네이티브 앱 사용자가 로그인하면, 기존 백업이 감지될 경우 PIN 입력 팝업이 자동으로 표시되어 Embedded Wallet로 마이그레이션됩니다.

별도 코드 작성이 필요 없습니다.


트러블슈팅

useCROSSx must be used within CROSSxProvider

앱 최상위에 <CROSSxProvider>가 빠져있습니다.

OAuth 팝업이 열리지 않음

브라우저의 팝업 차단을 확인하세요. 또는 config.oauthDisplayMode = 'modal'로 변경하세요.

로그인 후 상태가 업데이트되지 않음

현재 useAuth().signIn()은 성공 시 window.location.reload()를 호출합니다. SPA 라우터와 충돌하는 경우 useCROSSx()로 직접 상태를 관리하세요.


라이센스

MIT