@eternl/hub-dapp-sdk

中文文档
0.55.0发布于 2026/11/10BUSL-1.1TypeScriptESM + CJS近 30 天下载 2280

Eternl Hub SDK为dApp开发者服务.

中文使用文档
文档目录19 节
  1. 安装
  2. 如何连接
  3. 安全模型: 您必须显示 SAS6
  4. 快速启动(框架-不可知核心)
  5. 加入钱包显示的代码
  6. 渲染 QR 代码/ 深链接
  7. NATS认证(生产时需要)
  8. 反应
  9. EternlConnectionBadge (反应)道具
  10. 阴道
  11. EternlConnectionBadge (Vue) 道具和事件
  12. EternlSession 参考文献
  13. DAppConnectionOptions
  14. 处理错误
  15. 公用事业结余
  16. 会话持久性和配对钱包( C)
  17. 子路径导出
  18. 类型脚本
  19. 许可证

@eternl/hub-dapp-sdk (英语)

将您的 dApp 连接到 Cardano 钱包 埃特尔枢纽 ——不需要浏览器扩展. 对齐发生在一个使用短连接代码(或QR/深连接)的中继端到端加密通道上,由此产生的会话暴露出一个 CIP- 30 兼容的 API 加上几个Eternl扩展.

SDK飞船是框架不可知核加薄, 只有状态 反应和Vue的帮助者.

安装

npm install @eternl/hub-dapp-sdk
# or
pnpm add @eternl/hub-dapp-sdk
# or
yarn add @eternl/hub-dapp-sdk

react 财务报告和审定财务报表 vue 为: 选项 端点依赖性 – 只安装您的应用程序使用的一个 。 核心切入点@eternl/hub-dapp-sdk两者都不需要.

如何连接

有一个 没有 EternlConnect.connect() 外相为相. 你用两个核心功能驱动流动:

函数作用回返
createDAppConnection(options)供养者. DApp生成代码并等待钱包加入.(单位:千美元) DAppConnectionHandle (单位:千美元){ code, dappId, expiresAt, channel, waitForWallet, cancel }(中文(简体) )
joinWithCode(code, options)验出者. DApp 输入钱包显示的代码 .(单位:千美元) Promise<EternlSession>

大多数 dApps 使用 createDAppConnection: 显示 handle.code 到用户,然后 await handle.waitForWallet() 接收 EternlSession。 。 。 .

安全模型: 您必须显示 SAS6

期间 waitForWallet,连接到 status: 'sas_ready' 带有 6 位数的短验证字符串( S)sas6(中文(简体) )。您的密码 必须使 sas6 切换到用户 这样他们就可以比较一下钱包中的数字.

如果两个代码吻合,没有男中音取代握手键. 这种比较是配对协议的核心防御—— 不要跳过渲染它. 一样 sas6 也出现在随后的 'confirmed' 活动.

快速启动(框架-不可知核心)

import { createDAppConnection } from '@eternl/hub-dapp-sdk';
import type { ConnectionStatusEvent, EternlSession } from '@eternl/hub-dapp-sdk';

async function connect(): Promise<EternlSession> {
  const handle = await createDAppConnection({
    appName: 'My DApp',
    appUrl: 'https://mydapp.com',
    appIcon: 'https://mydapp.com/icon.png',
    permissions: ['read', 'sign'],
    network: 'mainnet',
    // Required against production hubs — see "NATS authentication" below.
    getAuthenticator: myGetAuthenticator,
  });

  // 1. Show the connection code to the user. They enter it in their wallet.
  console.log('Enter this code in Eternl:', handle.code);
  console.log('Code expires at:', new Date(handle.expiresAt).toLocaleTimeString());

  // 2. Wait for the wallet. The onStatus callback drives your UI, and it is
  //    where the SAS6 verification step surfaces.
  const session = await handle.waitForWallet((event: ConnectionStatusEvent) => {
    switch (event.status) {
      case 'waiting_for_wallet':
        console.log('Waiting for a wallet to enter the code…');
        break;
      case 'wallet_connected':
        console.log('Wallet connected, exchanging keys…');
        break;
      case 'sas_ready':
        // SECURITY-CRITICAL: render event.sas6 to the user so they can compare
        // it with the 6 digits shown in their wallet before continuing.
        console.log('Verify these digits match your wallet:', event.sas6);
        break;
      case 'awaiting_user':
        console.log(`${event.walletName ?? 'Wallet'}: user is selecting accounts…`);
        break;
      case 'confirmed':
        console.log('Connection confirmed.');
        break;
      case 'error':
      case 'cancelled':
      case 'timeout':
        console.log('Connection ended:', event.status, event.message);
        break;
    }
  });

  // 3. Use the session (CIP-30 + Eternl extensions).
  const networkId = await session.getNetworkId();
  const ada = await session.getAdaBalance();
  console.log('Connected:', session.activeAccount.name, networkId, `${ada} ADA`);

  return session;
}

要在钱包加入前中止, 请打电话 handle.cancel()。 。 。 .

加入钱包显示的代码

如果你的流转相反 钱包 显示代码, 使用 joinWithCode数字 :

import { joinWithCode } from '@eternl/hub-dapp-sdk';

const session = await joinWithCode(userEnteredCode, {
  appName: 'My DApp',
  permissions: ['read', 'sign'],
  getAuthenticator: myGetAuthenticator,
});

handle.code 是能展示的神话 如果您想要将其编码为可扫描的QR或移动深链接,请构建一个 PairingV2Payload 并使用编码器 :

import { generateQRData, generateDeepLink } from '@eternl/hub-dapp-sdk';
import type { PairingV2Payload } from '@eternl/hub-dapp-sdk';

const payload: PairingV2Payload = {
  v: 2,
  type: 'dapp',
  pin: handle.code,
  expiresAt: handle.expiresAt,
};

const qrString = generateQRData(payload);     // "eternl-pair:<base64url(JSON)>" — feed to any QR encoder
const deepLink = generateDeepLink(payload);   // "eternl://pair?p=<base64url(JSON)>"

NATS认证(生产时需要)

与一个硬化的NATS经纪人对流 默认拒绝匿名连接。您必须提供认证器:

  • 提供 getAuthenticator - 一个 async () => Authenticator | undefined 从 @nats-io/nats-core。 。 。 .
  • 若为 getAuthenticator 省略(或决定 undefined和 unsafeAllowAnonymousNats 未设置, 连接呼叫 掷出 EternlConnectError 与 code === ErrorCode.AUTH_REQUIRED。 。 。 .
import { createDAppConnection, EternlConnectError, ErrorCode } from '@eternl/hub-dapp-sdk';

try {
  const handle = await createDAppConnection({
    appName: 'My DApp',
    getAuthenticator: async () => myAuthenticator, // from @nats-io/nats-core
  });
  // …
} catch (err) {
  if (err instanceof EternlConnectError && err.code === ErrorCode.AUTH_REQUIRED) {
    console.error('No NATS authenticator was supplied.');
  }
}

unsafeAllowAnonymousNats: true 是一个 仅供本地/dev经纪人使用. 对制作经纪人而言,匿名连接无声地降级——会话看起来对齐了,但不能发布——所以SDK默认拒绝.

注意:在标准生产设置中,SDK可以在内部铸造一个瞄准符,所以你可能不需要 getAuthenticator 一点也不 以您的中枢部署确认哪个模式适用 。 不管怎样,规则是: 一个真正的连接需要认证, 或者它扔 AUTH_REQUIRED。 。 。 .

反应

反应帮助者是 只有状态。 。 。 . useEternl() 已经 没有 连接 —— 它维持会话并暴露反应状态. 你的应用电话 createDAppConnection 编号 : joinWithCode 然后将会议交给 setSession。 。 。 .

useEternl() 返回 :

{
  session: EternlSession | null;
  error: EternlConnectError | null;
  isConnected: boolean;
  activeAccount: AccountInfo | null;
  accounts: readonly AccountInfo[];
  setSession: (session: EternlSession) => void;   // install a connected session
  disconnect: () => Promise<void>;
  switchAccount: (accountId: string) => Promise<void>;
}

驱动模式——与核心连接,然后 setSession数字 :

import { useState } from 'react';
import { useEternl, EternlConnectionBadge } from '@eternl/hub-dapp-sdk/react';
import { createDAppConnection } from '@eternl/hub-dapp-sdk';
import type { ConnectionStatusEvent } from '@eternl/hub-dapp-sdk';

function App() {
  const { isConnected, activeAccount, setSession, disconnect } = useEternl();
  const [code, setCode] = useState<string | null>(null);
  const [sas6, setSas6] = useState<string | null>(null);

  async function handleConnect() {
    const handle = await createDAppConnection({
      appName: 'My DApp',
      permissions: ['read', 'sign'],
      getAuthenticator: myGetAuthenticator,
    });

    setCode(handle.code); // show the code to the user

    const session = await handle.waitForWallet((event: ConnectionStatusEvent) => {
      if (event.status === 'sas_ready') {
        // SECURITY-CRITICAL: show this so the user can compare with their wallet.
        setSas6(event.sas6 ?? null);
      }
    });

    setSession(session); // hand the connected session to the hook
    setCode(null);
    setSas6(null);
  }

  if (!isConnected) {
    return (
      <div>
        <button onClick={handleConnect}>Connect Wallet</button>
        {code && <p>Enter this code in Eternl: <strong>{code}</strong></p>}
        {sas6 && <p>Verify these digits match your wallet: <strong>{sas6}</strong></p>}
      </div>
    );
  }

  return (
    <div>
      <p>Connected: {activeAccount?.name}</p>
      <button onClick={disconnect}>Disconnect</button>
      {/* Floating badge with account switcher + disconnect */}
      <EternlConnectionBadge
        position="top-right"
        onAccountChange={(account) => console.log('Switched to:', account.name)}
        onDisconnect={() => console.log('Disconnected')}
      />
    </div>
  );
}

EternlConnectionBadge (反应)道具

interface EternlConnectionBadgeProps {
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; // default 'top-right'
  className?: string;
  style?: React.CSSProperties;
  onAccountChange?: (account: AccountInfo) => void;
  onDisconnect?: () => void;
}

徽章读取其状态 从相同的 useEternl() singleton),因此它使活期账户并提供一个账户切换器/断开一次 setSession 已被调用.

阴道

Vue 混凝土也一样 只有状态 —— 说 useEternl() 已经 没有 连接。 您驱动与核心函数的连接并调用 setSession。 。 。 .

useEternl() 返回(refs为被动反应):

{
  session: Readonly<Ref<EternlSession | null>>;
  error: Readonly<Ref<EternlConnectError | null>>;
  isConnected: ComputedRef<boolean>;
  activeAccount: ComputedRef<AccountInfo | null>;
  accounts: ComputedRef<readonly AccountInfo[]>;
  setSession: (session: EternlSession) => void;   // install a connected session
  disconnect: () => Promise<void>;
  switchAccount: (accountId: string) => Promise<void>;
}

SFC 中的驱动模式 :

<script setup lang="ts">
import { ref } from 'vue';
import { useEternl, EternlConnectionBadge } from '@eternl/hub-dapp-sdk/vue';
import { createDAppConnection } from '@eternl/hub-dapp-sdk';
import type { ConnectionStatusEvent } from '@eternl/hub-dapp-sdk';

const { isConnected, activeAccount, setSession, disconnect } = useEternl();
const code = ref<string | null>(null);
const sas6 = ref<string | null>(null);

async function handleConnect() {
  const handle = await createDAppConnection({
    appName: 'My DApp',
    permissions: ['read', 'sign'],
    getAuthenticator: myGetAuthenticator,
  });

  code.value = handle.code; // show the code to the user

  const session = await handle.waitForWallet((event: ConnectionStatusEvent) => {
    if (event.status === 'sas_ready') {
      // SECURITY-CRITICAL: show this so the user can compare with their wallet.
      sas6.value = event.sas6 ?? null;
    }
  });

  setSession(session); // hand the connected session to the composable
  code.value = null;
  sas6.value = null;
}
</script>

<template>
  <div v-if="!isConnected">
    <button @click="handleConnect">Connect Wallet</button>
    <p v-if="code">Enter this code in Eternl: <strong>{{ code }}</strong></p>
    <p v-if="sas6">Verify these digits match your wallet: <strong>{{ sas6 }}</strong></p>
  </div>

  <div v-else>
    <p>Connected: {{ activeAccount?.name }}</p>
    <button @click="disconnect">Disconnect</button>

    <!-- Floating badge with account switcher + disconnect -->
    <EternlConnectionBadge
      position="top-right"
      @account-change="(account) => console.log('Switched to:', account.name)"
      @disconnect="() => console.log('Disconnected')"
    />
  </div>
</template>

EternlConnectionBadge (Vue) 道具和事件

  • 道具 : position (单位:千美元)'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'默认值 'top-right', (中文(简体) ), className (弦声).
  • 活动 : account-change (包括新的 AccountInfo, (中文(简体) ), disconnect。 。 。 .

EternlSession 参考文献

一旦连接, 会话是您的 CIP- 30 表面加上一些 Eternl 扩展 。 除非注意,否则所有方法都是协同的.

状态( 只读属性)

财产类型页:1
pairIdstring本次会话的64hex配对标识符.
transportTransportType使用中的基础运输.
expiresAtDate届会结束后.
isConnectedboolean交通运输互联互通.
walletOnlineboolean配对钱包目前是否可以到达 .
accessGrantedboolean钱包是否允许此 dApp 访问( 同意层, 区别于 isConnected) (中文(简体) ).
grantedCategoriesReadonlyArray<'core' | 'governance' | 'extension'>给予钱包的方法分类 .
grantedPermissionsreadonly Permission[]获得CIP-30/CIP-95许可.
deviceInfoWalletDeviceInfo | null连接钱包的设备信息 .
availableAccountsreadonly AccountInfo[]钱包共享所有账户 .
activeAccountAccountInfo当前活动账户 .

CIP-30 核心项目

session.getNetworkId();                       // Promise<number>
session.getUtxos(amount?, paginate?);         // Promise<string[] | null>
session.getBalance();                         // Promise<string>  (CBOR-encoded value)
session.getUsedAddresses(paginate?);          // Promise<string[]>
session.getUnusedAddresses();                 // Promise<string[]>
session.getChangeAddress();                   // Promise<string>
session.getRewardAddresses();                 // Promise<string[]>
session.signTx(txCbor, partialSign?);         // Promise<string>  (witness set / signed tx CBOR)
session.signData(address, payloadHex);        // Promise<DataSignature>
session.submitTx(txCbor);                     // Promise<string>  (tx hash)

CIP-30 扩展

session.getExtensions();                      // Promise<{ cip: number }[]>
session.getCollateral(params?);               // Promise<string[] | null>

CIP-95 治理

session.getPubDRepKey();                      // Promise<string>
session.getRegisteredPubStakeKeys();          // Promise<string[]>
session.getUnregisteredPubStakeKeys();        // Promise<string[]>

永久扩展

session.getAdaBalance();                      // Promise<number>  — balance in ADA, already parsed
session.requestPresenceCheck();               // Promise<boolean> — actively probe wallet reachability
session.switchAccount(accountId);             // Promise<void>
session.disconnect();                         // Promise<void>    — tear down this session
session.remove();                             // Promise<void>    — disconnect and forget the pairing

活动 —— 说 session.on(event, listener) 返回未订阅的函数 :

const off = session.on('accountChange', (account) => { /* AccountInfo */ });
session.on('accountsChanged', (e) => { /* { accounts, activeAccountId } */ });
session.on('walletStatusChange', (e) => { /* { online: boolean } */ });
session.on('connectionChange', (e) => { /* { isConnected: boolean } */ });
session.on('accessGranted', (e) => { /* { grantedCategories, autoEstablishAllowed, walletName? } */ });
session.on('accessDenied', (e) => { /* { reason: string } */ });
session.on('peerPresenceC', (e) => { /* fast (~5s) presence event; requires enablePresenceC */ });
session.on('error', (err) => { /* Error */ });
off(); // unsubscribe

这个 peerPresenceC 仅当您通过时发生火灾 enablePresenceC: true 内 DAppConnectionOptions它提供比后遗症心跳路径更快的(~5s)离线检测,并与其并列运行.

DAppConnectionOptions

共享 createDAppConnection 财务报告和审定财务报表 joinWithCode数字 :

interface DAppConnectionOptions {
  appName: string;                 // required — shown to the user
  appUrl?: string;                 // defaults to window.location.origin
  appIcon?: string;
  permissions?: string[];          // e.g. ['read', 'sign'] (default)
  network?: string;                // defaults to 'mainnet'
  hubUrl?: string;                 // NATS WebSocket URL; defaults to the public hub
  hubRestUrl?: string;             // Hub REST URL; defaults to the public hub
  getAuthenticator?: () => Promise<Authenticator | undefined>; // from @nats-io/nats-core
  unsafeAllowAnonymousNats?: boolean; // dev/local only — see "NATS authentication"
  enablePresenceC?: boolean;       // opt into fast raw-stream presence (peerPresenceC)
}

处理错误

import { EternlConnectError, ErrorCode } from '@eternl/hub-dapp-sdk';

try {
  const handle = await createDAppConnection({ appName: 'My DApp', getAuthenticator });
  const session = await handle.waitForWallet();
} catch (error) {
  if (error instanceof EternlConnectError) {
    switch (error.code) {
      case ErrorCode.AUTH_REQUIRED: /* no NATS authenticator supplied */ break;
      case ErrorCode.TIMEOUT:       /* code expired before a wallet joined */ break;
      case ErrorCode.REJECTED:      /* user rejected the connection */ break;
      case ErrorCode.CANCELLED:     /* connection was cancelled */ break;
      case ErrorCode.TRANSPORT_ERROR: /* network / NATS error */ break;
      default: console.error(error.code, error.message);
    }
  }
}

公用事业结余

import {
  parseLovelaceBalance,
  lovelaceToAda,
  formatAda,
  parseAndFormatAda,
} from '@eternl/hub-dapp-sdk';

const lovelace = parseLovelaceBalance(await session.getBalance()); // CBOR → lovelace
const ada = lovelaceToAda(lovelace);                                // 1_000_000 → 1
const formatted = formatAda(lovelace);                              // "1.234567 ADA"
const display = parseAndFormatAda(await session.getBalance());      // CBOR → "1.234567 ADA"

(单位:千美元)session.getAdaBalance() 以一召而为你们的教诲

会话持久性和配对钱包( C)

核心还输出帮助者坚持并恢复连接而无需重新铺平:

import {
  hasStoredSession,
  getStoredSessionInfo,
  restoreSession,
  clearStoredSession,
  getPairedWallets,
  reconnectToWallet,
  removePairedWallet,
  clearPairedWallets,
} from '@eternl/hub-dapp-sdk';

if (hasStoredSession()) {
  const session = await restoreSession({ getAuthenticator: myGetAuthenticator });
}

const wallets = getPairedWallets(); // PairedWallet[]
const session = await reconnectToWallet(wallets[0].walletId, { getAuthenticator: myGetAuthenticator });

restoreSession 财务报告和审定财务报表 reconnectToWallet 接受相同的建议 getAuthenticator 编号 : unsafeAllowAnonymousNats 验证上述选项,并同样投出 AUTH_REQUIRED 没有他们.

子路径导出

导入路径目录
@eternl/hub-dapp-sdk核心 : createDAppConnection, (中文(简体) ), joinWithCode, (中文(简体) ), generateQRData, (中文(简体) ), generateDeepLink、会话/助演者/助演者, Permission, (中文(简体) ), EternlConnectError, (中文(简体) ), ErrorCode,平衡公用事业,以及各类公用事业.
@eternl/hub-dapp-sdk/reactuseEternl, (中文(简体) ), EternlConnectionBadge (状态唯一的反应帮助者).
@eternl/hub-dapp-sdk/vueuseEternl, (中文(简体) ), EternlConnectionBadge (仅限状态Vue 3助攻).

类型脚本

软件包已完全输入 。 实际出口类型包括:

import type {
  // Core connection
  DAppConnectionOptions,
  DAppConnectionHandle,
  DAppSession,
  ConnectionStatus,
  ConnectionStatusEvent,
  PairingV2Payload,

  // Session
  EternlSession,
  AccountInfo,
  ConnectionState,

  // Events
  SessionEventType,
  SessionEventListener,
  ConnectionChangeEvent,
  WalletStatusEvent,
  AccessGrantedEvent,
  AccessDeniedEvent,
  AccountsChangedEvent,

  // Wallet device
  WalletDeviceInfo,
  WalletPlatform,

  // Paired wallets
  PairedWallet,

  // CIP-30
  NetworkType,
  TransportType,
  Paginate,
  DataSignature,
  CollateralParams,
} from '@eternl/hub-dapp-sdk';

// Value exports
import { Permission, EternlConnectError, ErrorCode } from '@eternl/hub-dapp-sdk';

先前记录 EternlConnectConfig 财务报告和审定财务报表 ConnectionRequest 类型做 没有 已存在,不导出.

许可证

商业来源许可证1.1(BUSL-1.1) (中文(简体) ). 见 自由主义。 。 。 .

许可是Tastenkunst GmbH。 额外使用赠款 : None - 你可以复制、修改和制作 非生产 使用此软件包。 生产/商业用途需要从塔斯滕昆斯特获得单独的许可证。 在"更改日期"(每个版本首次出版的四周年)上,该版本会转换为GPL-2.0-或-后期.

联系人 : https://tastenkunst.com/ (中文(简体) )

版本与发布记录

@eternl/hub-dapp-sdk 共发布 65 个版本,首次发布于 2026/05/08;过去 12 个月发布了 65 个版本。

版本发布日期距今
0.55.0latest2026/11/103 天前
0.54.02026/11/103 天前
0.53.02026/11/103 天前
0.52.02026/11/103 天前
0.51.02026/11/104 天前
0.50.02026/11/094 天前

关于 @eternl/hub-dapp-sdk 的常见问题

@eternl/hub-dapp-sdk 是什么?

Eternl Hub SDK为dApp开发者服务.

如何安装 @eternl/hub-dapp-sdk?

在项目目录运行 npm install @eternl/hub-dapp-sdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@nats-io/nats-core(3.4.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@eternl/hub-dapp-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@eternl/hub-dapp-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@eternl/hub-dapp-sdk 可以商用吗?

@eternl/hub-dapp-sdk 使用 BUSL-1.1 许可。声明的许可为 BUSL-1.1,请阅读许可原文确认使用条件。以上为概括,不构成法律意见,请以许可原文为准。

@eternl/hub-dapp-sdk 的下载量有多少?

根据 npm 官方下载统计,@eternl/hub-dapp-sdk 在 2026/10/14 – 2026/11/12 这 30 天内被下载 2,280 次,日均约 76 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@eternl/hub-dapp-sdk 还在维护吗?

最新版本 0.55.0 发布于 2026/11/10(3 天前);至今共发布 65 个版本,其中过去 12 个月发布了 65 个;首次发布于 2026/05/08。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。