# @eternl/hub-dapp-sdk 中文文档

规范地址：https://npmcn.com/package/%40eternl/hub-dapp-sdk/

版本：0\.55\.0
npm 元数据更新时间：2026-11-10T15:46:42.715Z
中文译文更新时间：2026-11-14T06:20:26.997Z
软件包许可：BUSL-1\.1

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40eternl/hub-dapp-sdk
原文对照：https://npmcn.com/package/%40eternl/hub-dapp-sdk/?lang=original
项目主页：https://github.com/Tastenkunst/eternl-hub-dapp-sdk#readme
源码仓库：https://github.com/Tastenkunst/eternl-hub-dapp-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：291 kB，20 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：1 个
- peer 依赖：vue 3\.5\.44，react ^18\.0\.0 \|\| ^19\.0\.0，@nats-io/nats-core 3\.4\.0
- 首次发布：2026-05-08T13:46:53.076Z
- 最新版本发布：2026-11-10T15:46:42.331Z
- 近 30 天下载量：2280（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：65（近 12 个月 65 个）

## 中文简介

Eternl Hub SDK为dApp开发者服务.

## 安装

```sh
npm install @eternl/hub-dapp-sdk
```

也可以使用：`pnpm add @eternl/hub-dapp-sdk`、`yarn add @eternl/hub-dapp-sdk`、`bun add @eternl/hub-dapp-sdk`

## README

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

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

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

## 安装

```bash
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'` 活动.

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

```typescript
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`数字 :

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

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

### 渲染 QR 代码/ 深链接

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

```typescript
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`**。 。 。 .

```typescript
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()` 返回 :

```typescript
{
  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`数字 :

```tsx
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` (反应)道具

```typescript
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为被动反应):

```typescript
{
  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 中的驱动模式 :

```vue
<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 |
| --- | --- | --- |
| `pairId` | `string` | 本次会话的64hex配对标识符. |
| `transport` | `TransportType` | 使用中的基础运输. |
| `expiresAt` | `Date` | 届会结束后. |
| `isConnected` | `boolean` | 交通运输互联互通. |
| `walletOnline` | `boolean` | 配对钱包目前是否可以到达 . |
| `accessGranted` | `boolean` | 钱包是否允许此 dApp 访问( 同意层, 区别于 `isConnected`) (中文(简体) ). |
| `grantedCategories` | `ReadonlyArray<'core' | 'governance' | 'extension'>` | 给予钱包的方法分类 . |
| `grantedPermissions` | `readonly Permission[]` | 获得CIP-30/CIP-95许可. |
| `deviceInfo` | `WalletDeviceInfo | null` | 连接钱包的设备信息 . |
| `availableAccounts` | `readonly AccountInfo[]` | 钱包共享所有账户 . |
| `activeAccount` | `AccountInfo` | 当前活动账户 . |

**CIP-30 核心项目**

```typescript
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 扩展**

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

**CIP-95 治理**

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

**永久扩展**

```typescript
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)` 返回未订阅的函数 :

```typescript
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`数字 :

```typescript
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)
}
```

## 处理错误

```typescript
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);
    }
  }
}
```

## 公用事业结余

```typescript
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)

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

```typescript
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/react` | `useEternl`, (中文(简体) ), `EternlConnectionBadge` (状态唯一的反应帮助者). |
| `@eternl/hub-dapp-sdk/vue` | `useEternl`, (中文(简体) ), `EternlConnectionBadge` (仅限状态Vue 3助攻). |

## 类型脚本

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

```typescript
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`) (中文(简体) ). 见 [自由主义](https://npmcn.com/package/%40eternl/hub-dapp-sdk/LICENSE)。 。 。 .

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

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

## 常见问题

### @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 和提交记录判断。

