diff --git a/docs/pages/cn/explanation/sdk-overview.mdx b/docs/pages/cn/explanation/sdk-overview.mdx index efea774..f8a5979 100644 --- a/docs/pages/cn/explanation/sdk-overview.mdx +++ b/docs/pages/cn/explanation/sdk-overview.mdx @@ -1,14 +1,14 @@ --- source_path: explanation/sdk-overview.mdx -source_sha: efcb1b7d8aaf0782573a67c4964ac01904c220ae +source_sha: de079c165ba2efc991a66b7134e00a6e9b5073f4 title: "Stable SDK" -description: "使用类型化的 TypeScript SDK,只需几行代码即可在 Stable 上传输 USDT0、跨链桥接和兑换代币。" +description: "使用类型化的 TypeScript SDK,只需几行代码即可在 Stable 上转移 USDT0、跨链桥接、兑换代币和赚取金库收益。" diataxis: "explanation" --- # Stable SDK -`@stablechain/sdk` 是 Stable 官方的 TypeScript 客户端。它封装了 Viem,提供了一个小巧、类型化的 API,用于处理你最常使用的操作:传输 USDT0、在链之间进行桥接以及在 Stable 上兑换代币。路由、授权、小数位和链切换都已为你处理好。 +`@stablechain/sdk` 是 Stable 的官方 TypeScript 客户端。它将 viem 封装成一个小的、类型化的 API,用于您最常进行的操作:转移 USDT0、跨链桥接、兑换代币以及在 Stable 上赚取收益。路由、批准、小数位数和链切换都为您处理。 ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -32,29 +32,32 @@ txHash: 0x8f3a...2d41 ## SDK 功能 -- **`transfer`**:发送原生 USDT0 或 Stable 上的任何 ERC-20 代币。Gas 费会自动以 USDT0 支付。 -- **`quoteBridge` / `bridge`**:跨链传输。USDT0 → USDT0 使用 LayerZero,其他所有代币使用 LI.FI。系统会自动为你选择路由。 -- **`quoteSwap` / `swap`**:通过 LI.FI 进行同链代币兑换,ERC-20 授权在内部处理。 +- **`transfer`**:发送原生 USDT0 或 Stable 上的任何 ERC-20。Gas 费会自动以 USDT0 支付。 +- **`quoteBridge` / `bridge`**:通过 LI.FI 进行跨链传输,LI.FI 会为您选择桥接路线。批准和链切换在内部处理。 +- **`quoteSwap` / `swap`**:通过 LI.FI 进行同链代币兑换,ERC-20 批准在内部处理。 +- **`earn`**:将 USDT0 存入 Morpho V2 金库以赚取收益,提现或赎回,并领取 Merkl 激励奖励。批准和适配器解除分配都为您处理。 +- **`createStableReader`**:一个只读客户端,用于获取金库年化收益率 (APY)、您的头寸、收益预测和即时提现可用性检查。无需签名器。 -SDK 已发布到 npm,名称为 [`@stablechain/sdk`](https://www.npmjs.com/package/@stablechain/sdk),并要求 `viem >= 2.0.0` 作为 peer dependency。 +SDK 发布在 npm 上,名称为 [`@stablechain/sdk`](https://www.npmjs.com/package/@stablechain/sdk),并要求 `viem >= 2.0.0` 作为对等依赖项。 -## 何时使用(何时不使用) +## 何时使用(以及何时不使用) -当你想要一个类型化、规范化的客户端,并隐藏路由和授权样板文件时,请使用 SDK。当你需要直接控制交易构建、自定义 gas 策略或进行 transfer/bridge/swap 之外的合约调用时,请降级到原始的 Viem 或 Ethers。 +当您需要一个类型化、规范化的客户端来隐藏路由和批准样板文件时,请使用 SDK。当您需要直接控制交易构造、自定义 Gas 策略或转账/桥接/兑换之外的合同调用时,请降级到原始 viem 或 ethers。 :::note -SDK 使用任何兼容 Viem 的签名器进行签名:私钥 `Account`、浏览器 `Transport`(如 `custom(window.ethereum)`)或预构建的 `WalletClient`(例如,wagmi 的 `useWalletClient` 返回的客户端)。 +SDK 使用任何兼容 viem 的签名器进行签名:私钥 `Account`、浏览器 `Transport`(例如 `custom(window.ethereum)`)或预构建的 `WalletClient`(例如,wagmi 的 `useWalletClient` 返回的客户端)。 ::: -## 从这里开始 +## 从此处开始 -- [**快速入门**](/cn/tutorial/sdk-quickstart):安装 SDK 并在测试网上运行你的首次转账、桥接和兑换。 +- [**快速入门**](/cn/tutorial/sdk-quickstart):安装 SDK 并在测试网上运行您的第一个转账、桥接和兑换。 +- [**使用 SDK 赚取收益**](/cn/how-to/earn-yield):存款到金库,查看您的 APY 和头寸,提款并领取奖励。 - [**SDK 参考**](/cn/reference/sdk):所有方法、配置选项、枚举和错误类。 -- [**与 viem 一起使用**](/cn/how-to/sdk-with-viem):服务器端账户、浏览器钱包以及自带的 `WalletClient`。 -- [**与 wagmi 一起使用**](/cn/how-to/sdk-with-wagmi):使用 `useWalletClient` 和 hooks 将 SDK 连接到 React 应用程序。 +- [**与 viem 配合使用**](/cn/how-to/sdk-with-viem):服务器端账户、浏览器钱包和自带 `WalletClient`。 +- [**与 wagmi 配合使用**](/cn/how-to/sdk-with-wagmi):使用 `useWalletClient` 和 hooks 将 SDK 连接到 React 应用程序。 -## 下一步推荐 +## 下一步建议 - [**从 npm 安装**](https://www.npmjs.com/package/@stablechain/sdk):在 npmjs.com 上查看软件包并检查最新版本。 - [**连接到 Stable**](/cn/reference/connect):主网和测试网的链 ID、RPC 端点和浏览器。 -- [**资助测试网钱包**](/cn/how-to/use-faucet):在运行快速入门之前,从水龙头获取测试网 USDT0。 +- [**资助测试网钱包**](/cn/how-to/use-faucet):在运行快速入门之前从水龙头获取测试网 USDT0。 diff --git a/docs/pages/cn/how-to/earn-yield.mdx b/docs/pages/cn/how-to/earn-yield.mdx new file mode 100644 index 0000000..3c3bfe4 --- /dev/null +++ b/docs/pages/cn/how-to/earn-yield.mdx @@ -0,0 +1,178 @@ +--- +source_path: how-to/earn-yield.mdx +source_sha: 03f36a00122cecc73b90075f5f9276cfd84121ee +title: "用 SDK 赚取收益" +description: "使用 Stable SDK 将 USDT0 存入 Morpho 资金库,读取年化收益率和头寸,提款,并领取 Merkl 奖励。" +diataxis: "how-to" +--- + +# 用 SDK 赚取收益 + +在 Stable 主网将 USDT0 存入 Morpho V2 资金库以赚取收益,然后读取您的头寸并提款。 `@stablechain/sdk` 的 `earn` 方法处理 ERC-20 授权、存款和提款时的适配器解除分配,因此您每个操作只需调用一个方法。 + +涉及两个客户端。`createStable` 为您提供用于存款和提款的签名客户端。`createStableReader` 为您提供只读客户端,用于查询年化收益率、头寸和可提款性,无需签名器。 + +## 先决条件 + +- Node.js 20 或更高版本,以及已安装的 `@stablechain/sdk` 和 `viem`。请参阅 [SDK 快速入门](/cn/tutorial/sdk-quickstart)。 +- 具有 Stable 主网 USDT0 的签名器。要转入资金,请使用 [`stable.bridge`](/cn/reference/sdk#bridgeparams) 或其中一个[支持的桥接器](/cn/reference/bridges)。 +- 资金库地址。SDK 将默认主网资金库导出为 `STABLE_VAULT_ADDRESS`。 + +:::note +赚取收益的示例在主网上运行。SDK 不提供测试网资金库地址,因此 `Network.Testnet` 仅适用于您自己部署的资金库。 +::: + +## 1. 使用资金库创建签名客户端 + +将 `earn: { vault }` 传递给 `createStable`。如果没有它,存款和提款方法将抛出 `StableValidationError`。 + +```ts +import { createStable, Network, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +import { privateKeyToAccount } from "viem/accounts"; + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); + +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); +``` + +```text +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } +``` + +## 2. 存款前查看年化收益率 + +创建只读读取器并获取资金库当前的净年化收益率。读取器需要您要读取的地址和资金库,但不需要签名器。 + +```ts +import { createStableReader } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: account.address, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); + +const { apy, native, rewards } = await reader.earn.getYield(); +console.log("净年化收益率:", (apy * 100).toFixed(2) + "%"); +``` + +```text +净年化收益率: 5.80% +``` + +`apy` 是扣除费用后的总净年化收益率,包括奖励提升。`native` 是底层市场的独立收益率,`rewards` 则分解了每个奖励代币的年化百分比贡献。所有值均为小数,因此 `0.058` 表示 5.8%。 + +要估算一段时间内的收益,请使用 `preview`: + +```ts +const { projectedYield } = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +console.log("1000 美元 30 天预测收益:", projectedYield.toFixed(2)); +``` + +```text +1000 美元 30 天预测收益: 4.64 +``` + +## 3. 存款 + +向资金库提供资产。SDK 发送授权(或钱包支持时的许可签名),然后是存款,并等待收据。 + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +console.log("存款交易哈希:", txHash); +``` + +```text +存款交易哈希: 0x8f3a...2d41 +``` + +金额是人类可读的。除非您传递 `tokenDecimals`,否则底层资产的小数位将在链上获取。为了限制单次存款消耗的资金库闲置流动性,请传递 `depositBuffer`(例如 `0.8` 表示 80%);如果金额超过上限,SDK 会在发送前抛出错误。 + +:::tip +传递 `idempotencyKey` 可使存款安全重试。如果使用相同键的调用已在进行中,SDK 将返回相同的 Promise,而不是发送第二个交易。 +::: + +## 4. 读取您的头寸 + +获取您的份额余额及其当前资产价值。 + +```ts +const position = await reader.earn.position(); +console.log("份额:", position.sharesFormatted, "价值", position.assets); +``` + +```text +份额: 100 价值 100.02 +``` + +`shares` 是原始余额,`sharesFormatted` 是其人类可读值。`assets` 是这些份额目前以底层单位计的价值。 + +## 5. 提款或赎回 + +提取特定数量的底层资产,或赎回一定数量的份额。当资金库的闲置流动性不足时,SDK 会自动从资金库的适配器中解除分配以弥补差额。 + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +console.log("提款交易哈希:", txHash); +``` + +```text +提款交易哈希: 0xabcd...7890 +``` + +要按份额数量赎回,请使用 `stable.earn.redeem({ shares: 25 })`。要首先检查某个数量是否可以立即取出,请调用读取器: + +```ts +const { isInstant, availableLiquidity } = await reader.earn.withdrawability({ amount: 50 }); +console.log("即时提款:", isInstant, "可用流动性:", availableLiquidity); +``` + +```text +即时提款: true 可用流动性: 12500.5 +``` + +当 `isInstant` 为 `false` 时,提款仍然成功;它只是需要适配器解除分配,这由 `withdraw` 和 `redeem` 为您处理。要手动控制从哪些适配器中提取,请使用 [`forceWithdraw` / `forceRedeem`](/cn/reference/sdk#forcewithdrawparams--forceredeemparams)。 + +## 6. 领取奖励 + +资金库除了原生收益外,还可以赚取 Merkl 奖励代币。获取待领取奖励,然后领取它们。这些方法需要签名器,但不需要 `earn` 资金库配置。 + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +if (rewards.length > 0) { + const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); + console.log("已领取", tokenCount, "个代币:", txHash); +} +``` + +```text +已领取 1 个代币: 0xabcd...7890 +``` + +:::note +Merkl 大约每两小时将奖励提交到其 merkle 树。仍在等待更新的奖励尚不能领取,如果调用 `claim()` 时没有可领取的奖励,则会抛出 `StableValidationError`。对于浏览器,请将 `merklApiBase` 设置为您自己服务器上的代理,以避免 Merkl API 的 CORS 限制。 +::: + +## 不在 SDK 中签名? + +如果您在其他地方签名交易(一个中继器,一个免 gas 流程,或一个多重签名),构建 calldata 而不发送它: + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`steps` 是一个有序的交易列表,一个可选的授权,然后是存款。`prepareWithdrawCalldata` 和 `prepareRedeemCalldata` 各自返回一个 `{ to, data, value, chainId }` 交易。 + +## 接下来去哪里 + +- [**SDK 参考**](/cn/reference/sdk#stableearn):完整的赚取收益参数、返回类型和读取器方法。 +- [**与 wagmi 一起使用**](/cn/how-to/sdk-with-wagmi):将 SDK 集成到 React 应用程序中,以便用户从浏览器钱包存款。 +- [**SDK 概述**](/cn/explanation/sdk-overview):何时使用 SDK 以及签名模式如何工作。 diff --git a/docs/pages/cn/how-to/sdk-with-viem.mdx b/docs/pages/cn/how-to/sdk-with-viem.mdx index 9066af8..9fb7a5d 100644 --- a/docs/pages/cn/how-to/sdk-with-viem.mdx +++ b/docs/pages/cn/how-to/sdk-with-viem.mdx @@ -1,6 +1,6 @@ --- source_path: how-to/sdk-with-viem.mdx -source_sha: 48be50a291feef6843b9074ab105a81c375d5845 +source_sha: 7e53a41e2fc3a48b2ad38135b67d4d980e43e492 title: "将 SDK 与 viem 结合使用" description: "通过三种方式使用 @stablechain/sdk 进行签名:私钥账户、浏览器传输或预构建的 viem WalletClient。" diataxis: "how-to" @@ -10,11 +10,11 @@ diataxis: "how-to" `@stablechain/sdk` 基于 viem 构建。`createStable` 接受三种签名模式,您可以根据代码运行环境进行选择:在服务器端使用私钥,在浏览器端使用用户钱包,或者使用您已经构建的 `WalletClient`(例如,在 wagmi 应用程序中)。 -本指南将演示每种模式的端到端实现。 +本指南将端到端地展示每种模式。 ## 服务器端:私钥 `Account` -使用 viem 中的 `privateKeyToAccount` 函数,通过后端持有的私钥进行签名。 +使用 viem 的 `privateKeyToAccount` 通过您的后端持有的私钥进行签名。 ```ts import "dotenv/config"; @@ -43,7 +43,7 @@ console.log(txHash); ## 浏览器端:来自钱包的 `Transport` -将 `custom(window.ethereum)`(或任何 EIP-1193 provider)作为 `transport` 传递。SDK 会构建 `WalletClient` 并从 provider 读取签名者地址。 +将 `custom(window.ethereum)`(或任何 EIP-1193 provider)作为 `transport` 传入。SDK 会从传输中构建一个 `WalletClient`,并以您传递给 `transfer` 的 `from` 地址进行签名。 ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -68,12 +68,16 @@ const { txHash } = await stable.transfer({ ``` :::warning -`transfer`、`bridge` 和 `swap` 会调用 `switchChain` 将钱包切换到正确的网络。如果用户拒绝,SDK 会抛出 `StableTransactionError` 并带有 `phase: "switch_chain"` 字段。请捕获此错误并向用户显示重试选项。 +传输模式仅适用于 `transfer`,因为 `transfer` 接受一个显式的 `from` 参数。`swap`、`quoteSwap`、`bridge` 和 `earn` 方法从 `account` 或 `walletClient` 读取签名者地址,当两者都未设置时会抛出 `StableValidationError`。对于这些方法,请从同一个提供者构建一个 `WalletClient`(下一节)。 +::: + +:::warning +`transfer`、`bridge` 和 `swap` 调用 `switchChain` 将钱包切换到正确的网络。如果用户拒绝,SDK 会抛出 `StableTransactionError` 并带有 `phase: "switch_chain"`。捕获它并向用户显示重试选项。 ::: ## 引入您自己的 `WalletClient` -当您已经拥有一个 `WalletClient`(例如,来自 wagmi 或自定义签名器)时,可以直接传递它。它会优先于 `account` 和 `transport`。 +当您已经拥有一个 `WalletClient`(例如,来自 wagmi 或自定义签名器)时,直接传递它。它优先于 `account` 和 `transport`。 ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -101,14 +105,14 @@ const { txHash } = await stable.transfer({ from, to: "0xRecipient", amount: 5 }) ## 选择一种模式 -| **模式** | **何时使用** | +| **模式** | **使用场景** | | :--- | :--- | -| `account` | 后端服务、脚本、代理,任何您持有私钥的地方。 | -| `transport` | 浏览器应用程序,用户通过 MetaMask 或无 wagmi 的自定义流程进行签名。 | -| `walletClient` | 您已经有一个配置好的 `WalletClient` (wagmi、RainbowKit、ConnectKit)。 | +| `account` | 后端服务、脚本、代理,以及任何您持有密钥的地方。 | +| `transport` | 仅使用显式 `from` 调用 `transfer` 的浏览器应用程序。 | +| `walletClient` | 您已经有一个配置好的 `WalletClient` (wagmi, RainbowKit, ConnectKit)。 | ## 接下来推荐 - [**与 wagmi 结合使用**](/cn/how-to/sdk-with-wagmi):通过 wagmi 钩子将 SDK 集成到 React 应用程序中。 -- [**SDK 参考**](/cn/reference/sdk):所有配置字段、方法、枚举和错误类。 +- [**SDK 参考**](/cn/reference/sdk):每个配置字段、方法、枚举和错误类。 - [**SDK 快速入门**](/cn/tutorial/sdk-quickstart):在测试网上运行您的第一个转账、桥接和兑换。 diff --git a/docs/pages/cn/how-to/sdk-with-wagmi.mdx b/docs/pages/cn/how-to/sdk-with-wagmi.mdx index d9c06ab..bd744ec 100644 --- a/docs/pages/cn/how-to/sdk-with-wagmi.mdx +++ b/docs/pages/cn/how-to/sdk-with-wagmi.mdx @@ -1,14 +1,14 @@ --- source_path: how-to/sdk-with-wagmi.mdx -source_sha: 396745c5590db9fb3f8b0677d67b69579aeeb55a +source_sha: 2c4c2bcb4189c3ae47884b82bf8433ee0a18ae5e title: "将 SDK 与 wagmi 结合使用" -description: "将 @stablechain/sdk 引入 React 应用中,使用 wagmi 的 useWalletClient, useAccount, 和 useChainId 钩子。" +description: "将 @stablechain/sdk 接入 React 应用,使用 wagmi 的 useWalletClient、useAccount 和 useChainId 钩子。" diataxis: "how-to" --- # 将 SDK 与 wagmi 结合使用 -`createStable` 接受一个 viem `WalletClient`,这正是 wagmi 的 `useWalletClient` 返回的。您像往常一样通过 wagmi 连接钱包,然后在钱包客户端更改时记忆一个 `StableClient`。 +`createStable` 接受一个 viem `WalletClient`,这正是 wagmi 的 `useWalletClient` 返回的。像往常一样,你通过 wagmi 连接钱包,然后在钱包客户端更改时记忆一个 `StableClient`。 本指南假设使用 wagmi v2 和 `@tanstack/react-query`。 @@ -35,9 +35,9 @@ export const wagmiConfig = createConfig({ WagmiConfig { chains: [988, 2201], connectors: [injected] } ``` -## 2. 构建一个返回 `StableClient` 的 Hook +## 2. 构建一个返回 `StableClient` 的钩子 -针对当前的 `WalletClient` 记忆一个 `StableClient`。当钱包客户端的身份发生变化时重新创建它。 +根据当前的 `WalletClient` 记忆一个 `StableClient`。当钱包客户端的身份发生变化时重新创建它。 ```tsx import { useMemo } from "react"; @@ -55,7 +55,7 @@ export function useStable(network: Network = Network.Mainnet): StableClient | nu ``` :::warning -`useWalletClient()` 在用户连接之前返回 `undefined`。在调用 SDK 方法之前务必进行防御性检查,否则解构的 `walletClient` 将为假值,并且 `createStable` 将没有签名者。 +`useWalletClient()` 在用户连接之前返回 `undefined`。在调用 SDK 方法之前务必进行防御性编程,否则解构的 `walletClient` 将为假,并且 `createStable` 将没有签名者。 ::: ## 3. 在组件中使用它 @@ -82,7 +82,7 @@ export function PayButton() { return ( ); } @@ -92,9 +92,9 @@ export function PayButton() { Sent: 0x8f3a...2d41 ``` -## 4. 从 React 执行桥接和兑换 +## 4. 从 React 执行跨链桥接和兑换 -同一个 `stable` 实例处理桥接和兑换。在 effect 或 `useQuery` 中获取报价,然后点击执行。 +同一个 `stable` 实例处理跨链桥接和兑换。在 effect 或 `useQuery` 中获取报价,然后点击执行。捕获 `StableQuoteError`:LI.FI 在 Stable 上的 DEX 覆盖范围仍然有限,没有路由的交易对会抛出“No available quotes for the requested transfer”错误。 ```tsx const stable = useStable(Network.Mainnet); @@ -126,8 +126,8 @@ const onSwap = async () => { 使用 `useQuery` 缓存报价效果很好:将 `quoteSwap` / `quoteBridge` 作为查询函数传递,并将缓存的 `quote` 转发到 `swap` / `bridge`。当提供了报价时,SDK 会跳过其内部的报价调用。 ::: -## 下一步建议 +## 接下来推荐 - [**SDK 参考**](/cn/reference/sdk):每个方法、配置字段和错误类。 -- [**与 viem 结合使用**](/cn/how-to/sdk-with-viem):并排比较三种签名模式。 -- [**SDK 快速入门**](/cn/tutorial/sdk-quickstart):在测试网上运行您的首次转账、桥接和兑换。 +- [**与 viem 一起使用**](/cn/how-to/sdk-with-viem):并排比较三种签名模式。 +- [**SDK 快速入门**](/cn/tutorial/sdk-quickstart):在测试网上运行你的第一个转账、跨链桥接和兑换。 diff --git a/docs/pages/cn/reference/sdk.mdx b/docs/pages/cn/reference/sdk.mdx index 242b9a1..6554fc0 100644 --- a/docs/pages/cn/reference/sdk.mdx +++ b/docs/pages/cn/reference/sdk.mdx @@ -1,14 +1,14 @@ --- source_path: reference/sdk.mdx -source_sha: 64d437cfd1ea8f5c39da592e6dfad6bb6ab8b3bf +source_sha: cb2522d5ba3ff23ce6506a1bdc182e119b247b59 title: "SDK 参考" -description: "@stablechain/sdk 的完整参考:createStable、StableClient 方法、枚举、链配置和错误类。" +description: "@stablechain/sdk 完整参考:createStable、createStableReader、transfer、bridge、swap、earn 和 yield 方法,枚举和错误类。" diataxis: "reference" --- # SDK 参考 -`@stablechain/sdk` 的完整概览。有关详细介绍,请参阅 [SDK 快速入门](/cn/tutorial/sdk-quickstart)。 +`@stablechain/sdk` 的完整表面。这涵盖了来自 [`createStable`](#createstableconfig) 的签名客户端,来自 [`createStableReader`](#createstablereaderconfig) 的只读客户端,以及共享的枚举、常量和错误类。有关详细说明,请参阅 [SDK 快速入门](/cn/tutorial/sdk-quickstart)。有关以任务为中心的收益指南,请参阅 [使用 SDK 赚取收益](/cn/how-to/earn-yield)。 ## 安装 @@ -20,7 +20,7 @@ npm install @stablechain/sdk viem added 2 packages, audited 3 packages in 2s ``` -`viem >= 2.0.0` 是对等依赖。 +`viem >= 2.0.0` 是一个对等依赖。 ## `createStable(config)` @@ -33,7 +33,7 @@ const stable = createStable({ network: Network.Mainnet, account }); ``` ```text -StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } ``` ### `StableConfig` @@ -45,14 +45,16 @@ StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } | `account` | `viem.Account` | | 服务器端签名器(例如 `privateKeyToAccount`)。 | | `transport` | `viem.Transport` | | 浏览器钱包传输(例如 `custom(window.ethereum)`)。 | | `walletClient` | `viem.WalletClient` | | 预构建的钱包客户端。优先于 `account` 和 `transport`。 | +| `earn` | `{ vault: string }` | | Morpho V2 保险库配置。调用 `stable.earn` 存款、取款、赎回和准备 calldata 方法时需要。 | +| `merklApiBase` | `string` | `https://api.merkl.xyz` | Merkl 奖励 API 基础 URL。覆盖以通过您自己的服务器代理并避免浏览器 CORS 限制。 | -请提供 `account`、`transport` 或 `walletClient` 中的一个。 +提供 `account`、`transport` 或 `walletClient` 中的一个。`earn` 字段是可选的:转账、跨链桥和掉期在没有它也可以工作。保险库存款和取款需要它,如果您在没有配置保险库的情况下调用它们,SDK 会抛出 `StableValidationError`。 ## `StableClient` ### `transfer(params)` -在 Stable 上发送原生 USDT0 或任何 ERC-20。切换钱包到 Stable 链,在缺失时链上获取代币小数位数,并等待回执。 +在 Stable 上发送原生 USDT0 或任何 ERC-20。将钱包切换到 Stable 链,当缺失时在链上获取代币小数位数,并等待收据。 ```ts const { txHash } = await stable.transfer({ @@ -70,35 +72,39 @@ const { txHash } = await stable.transfer({ | :--- | :--- | :--- | | `from` | `string` | 发送方地址。 | | `to` | `string` | 接收方地址。 | -| `amount` | `number` | 人类可读金额。 | +| `amount` | `number` | 人类可读的数量。 | | `token` | `string?` | ERC-20 合约地址。原生 USDT0 则省略。 | -| `tokenDecimals` | `number?` | 小数位数。省略时从链上获取。 | +| `tokenDecimals` | `number?` | 小数位数。省略时在链上获取。 | 返回 `OperationResult` (`{ txHash, toAmount? }`)。 ### `quoteBridge(params)` -桥接预览。只读。无签名,无 gas。 +预览跨链桥。只读。无签名,无 gas。 ```ts const quote = await stable.quoteBridge({ fromChain: Chain.Ethereum, toChain: Chain.Stable, - fromToken: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", - toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // 以太坊上的 USDT + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // Stable 上的 USDT0 amount: 100, }); ``` ```text -{ toAmount: 99.94 } +{ toAmount: 99.73 } ``` 返回 `BridgeQuote`。 +:::note +LI.FI 仅为 [`Chain`](#chain) 枚举中的主网链提供报价。`Chain.Sepolia` 和 `Chain.StableTestnet` 被 `StableQuoteError` 拒绝,并且 `CHAIN_CONFIGS` 中的某些代币地址(例如以太坊上的 USDT0)在 LI.FI 的报价拒绝列表中。如有疑问,请使用链的规范代币进行报价。 +::: + ### `bridge(params)` -跨链桥接代币。SDK 选择路由:USDT0 → USDT0 使用 LayerZero,其他使用 LI.FI。传入预取的 `quote` 可跳过内部引用调用。 +通过 LI.FI 进行跨链桥接代币,LI.FI 会选择桥接路由。SDK 会处理 ERC-20 批准并在发送前将钱包切换到源链。传递预先获取的 `quote` 可跳过内部报价调用。 ```ts const { txHash } = await stable.bridge({ ...bridgeParams, quote }); @@ -114,14 +120,14 @@ const { txHash } = await stable.bridge({ ...bridgeParams, quote }); | `toChain` | `Chain` | 目标链。 | | `fromToken` | `string` | 源代币合约地址。 | | `toToken` | `string` | 目标代币合约地址。 | -| `amount` | `number` | 人类可读金额。 | +| `amount` | `number` | 人类可读的数量。 | | `fromDecimals` | `number?` | 源代币小数位数。默认为 `6`。 | | `recipient` | `string?` | 目标地址。默认为签名者。 | -| `quote` | `BridgeQuote?` | 预取的报价。跳过内部报价调用。 | +| `quote` | `BridgeQuote?` | 预先获取的报价。跳过内部报价调用。 | ### `quoteSwap(params)` -获取 Stable 上的 LI.FI 交换报价。返回预构建的交易请求和批准地址。 +在 Stable 上获取 LI.FI 掉期报价。返回预构建的交易请求和批准地址。 ```ts const quote = await stable.quoteSwap({ @@ -136,6 +142,10 @@ const quote = await stable.quoteSwap({ { toAmount: 99.81, fromAmount: 100000000n, fromToken: "0x8a2B...", approvalAddress: "0x...", transactionRequest: { ... } } ``` +:::note +掉期路由取决于 LI.FI 在 Stable 上的 DEX 覆盖范围,目前仍然有限。当没有针对某个交易对的路由时,`quoteSwap` 和 `swap` 会抛出 `StableQuoteError` 并显示“请求的转账没有可用报价”。 +::: + ### `swap(params)` 通过 LI.FI 在 Stable 上交换代币。自动处理 ERC-20 批准,并在需要时切换钱包的链。 @@ -152,10 +162,275 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); | :--- | :--- | :--- | :--- | | `fromToken` | `string` | | 源代币地址。 | | `toToken` | `string` | | 目标代币地址。 | -| `amount` | `number` | | 人类可读金额。 | +| `amount` | `number` | | 人类可读的数量。 | | `fromDecimals` | `number?` | `6` | 源代币小数位数。 | | `toAddress` | `string?` | signer | 接收方地址。 | -| `quote` | `SwapQuote?` | | 预取的报价。跳过 LI.FI 调用。 | +| `quote` | `SwapQuote?` | | 预取报价。跳过 LI.FI 调用。 | + +## `stable.earn` + +将 USDT0 存入 Morpho V2 Vault 以赚取收益,并领取 Merkl 激励奖励。所有方法都位于 `earn` 命名空间下,并需要一个签名者。存款、取款、赎回和准备 calldata 方法还需要 [`StableConfig`](#stableconfig) 中的 `earn: { vault }`。 + +```ts +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +stable.earn { deposit, bulkDeposit, withdraw, bulkWithdraw, redeem, forceWithdraw, forceRedeem, prepareDepositCalldata, prepareWithdrawCalldata, prepareRedeemCalldata, incentiveRewards } +``` + +金额和份额计数是人类可读的数字。如果您省略资产和份额的小数位数,则会从链上获取。存款、取款、赎回和强制方法返回 `EarnResult` (`{ txHash, idempotencyKey? }`) 并在解析前等待收据。批处理方法返回 `BulkEarnResult`,`incentiveRewards.claim()` 返回 `MerklClaimResult`,将在下面的部分中介绍。 + +### `deposit(params)` + +将资产存入保险库。一次调用即可处理 ERC-20 批准(或钱包支持时的许可签名),然后是存款。 + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +``` + +```text +{ txHash: "0x8f3a...2d41" } +``` + +| **参数** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | 要存入的底层资产,人类可读。 | +| `vault` | `string?` | config vault | 保险库地址覆盖。在 `bulkDeposit` 中跨多个保险库逐项需要。 | +| `tokenDecimals` | `number?` | on-chain | 底层资产小数位数。 | +| `slippageTolerance` | `number?` | `0.0003` | 滑点以小数表示(例如 `0.003` = 0.3%)。默认为 Morpho 的 0.03%。 | +| `depositBuffer` | `number?` | | 此存款可消耗的保险库闲置流动性的最大百分比(例如 `0.8` = 80%)。必须 `> 0` 且 `≤ 1`。如果超出则在发送前抛出。 | +| `idempotencyKey` | `string?` | | 去重键。第二次带在途键的调用返回相同的 Promise。 | + +### `withdraw(params)` + +提取给定数量的底层资产。当闲置资金池流动性不足时,SDK 会自动从资金池的适配器中解除分配以弥补差额。 + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **参数** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | 要提取的底层资产,人类可读。 | +| `tokenDecimals` | `number?` | on-chain | 底层资产的小数位数。 | +| `idempotencyKey` | `string?` | | 去重键。 | + +### `redeem(params)` + +将一定数量的 Vault 份额赎回为基础资产。与 `withdraw` 类似,当闲置流动性不足时,它会自动从 Adapter 中解除分配。 + +```ts +const { txHash } = await stable.earn.redeem({ shares: 25 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **参数** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `shares` | `number` | | 要赎回的 Vault 份额,人类可读。 | +| `shareDecimals` | `number?` | on-chain | Vault 份额小数位数。 | +| `idempotencyKey` | `string?` | | 去重键。 | + +### `bulkDeposit(items, options?)` / `bulkWithdraw(items, options?)` + +按顺序运行多个存款或取款。默认情况下,批量尝试每个项目并返回部分结果。设置 `stopOnError: true` 以在第一次失败时停止并抛出 `StableBulkError`。 + +```ts +const result = await stable.earn.bulkDeposit([ + { amount: 100 }, + { amount: 250, vault: "0xAnotherVault" }, +]); +``` + +```text +{ results: [ { status: "fulfilled", txHash: "0x..." }, ... ], succeeded: 2, failed: 0 } +``` + +`items` 是 `EarnDepositParams`(或 `EarnWithdrawParams`)的数组。`options` 接受: + +| **选项** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `idempotencyKey` | `string?` | | 批处理级别的去重键。 | +| `stopOnError` | `boolean?` | `false` | 在第一次失败时停止并抛出 `StableBulkError`,而不是返回部分结果。 | + +返回 `BulkEarnResult` (`{ results, succeeded, failed, idempotencyKey? }`)。`results` 中的每个条目要么是 `{ status: "fulfilled", txHash, vault?, idempotencyKey? }`,要么是 `{ status: "rejected", error, vault?, idempotencyKey? }`。 + +### `forceWithdraw(params)` / `forceRedeem(params)` + +通过明确控制首先解除分配哪些适配器头寸来取款或赎回。当您需要自己选择来源市场而不是 `withdraw` / `redeem` 中的自动选择时,请使用这些。 + +```ts +const { txHash } = await stable.earn.forceWithdraw({ + amount: 1000, + deallocations: [{ adapter: "0xAdapter", amount: 1000 }], +}); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +两者都接受一个 `deallocations: ForceDeallocation[]` 数组和一个可选的 `tokenDecimals`(用于解除分配数量的底层资产小数位数;如果省略,则在链上获取)。`forceWithdraw` 接受 `amount`;`forceRedeem` 接受 `shares` 和可选的 `shareDecimals`。每个 `ForceDeallocation` 是: + +| **字段** | **类型** | **描述** | +| :--- | :--- | :--- | +| `adapter` | `string` | 要解除分配的适配器合约。 | +| `amount` | `number` | 要解除分配的数量,以底层资产单位表示。 | +| `marketParams` | `object?` | 仅当 Morpho Blue 市场适配器才需要:`{ loanToken, collateralToken, oracle, irm, lltv }` (`lltv` 乘以 1e18)。Vault 适配器则省略。 | + +### `prepareDepositCalldata(params)` / `prepareWithdrawCalldata(params)` / `prepareRedeemCalldata(params)` + +构建事务 calldata,但不签名或发送。使用这些将事务通过中继器、燃气费豁免流或多重签名。 + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`prepareDepositCalldata` 返回 `EarnDepositCalldata` (`{ steps, chainId }`),其中 `steps` 是 `{ to, data, value }` 事务的有序列表:可选的代币批准,然后是存款。`prepareWithdrawCalldata` 和 `prepareRedeemCalldata` 返回 `EarnWithdrawCalldata` (`{ to, data, value, chainId }`),一个单一事务。它们接受与其签名对应物相同的参数。 + +### `incentiveRewards` + +获取并申领签名者的 Merkl 奖励代币。这些方法需要签名者,但不需要 `earn` vault 配置。 + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); +``` + +```text +{ txHash: "0xabcd...7890", tokenCount: 1 } +``` + +`fetch()` 返回 `MerklRewardsResult` (`{ chainId, rewards: MerklReward[] }`),其中包含每个代币的可申领净额。`claim()` 申领当前 Merkl 默克尔树中提交的所有奖励,并返回 `MerklClaimResult` (`{ txHash, tokenCount }`)。 + +:::note +Merkl 大约每两个小时将奖励提交到其默克尔树。仍在等待更新的奖励尚不能申领。调用 `claim()` 且无可申领内容时将抛出 `StableValidationError`。 +::: + +## `createStableReader(config)` + +为 Vault 收益和头寸数据构建一个只读的 `StableReader`。不需要签名者,只需要您要读取的地址。 + +```ts +import { createStableReader, Network } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: "0xUserAddress", + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +StableReader { earn: { getYield, position, preview, withdrawability, incentiveRewards } } +``` + +### `StableReaderConfig` + +| **字段** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `address` | `string` | | 要读取其头寸和奖励的用户地址。必填。 | +| `earn` | `{ vault: string }` | | 要读取的 Vault。必填:如果缺少,`createStableReader` 将抛出 `StableValidationError`。 | +| `network` | `Network?` | `Network.Mainnet` | 目标网络。 | +| `rpc` | `string?` | 公共 RPC | RPC 覆盖。 | +| `merklApiBase` | `string?` | `https://api.merkl.xyz` | Merkl 奖励 API 基础 URL。 | + +## `StableReader` + +### `getYield()` + +返回 Vault 当前的净 APY 和费用明细。 + +```ts +const vaultYield = await reader.earn.getYield(); +``` + +```text +{ apy: 0.058, native: 0.041, rewards: [ { symbol: "MORPHO", address: "0x...", apr: 0.017 } ], performanceFee: 0.1, managementFee: 0 } +``` + +返回 `VaultYield`。`apy` 是扣除奖励提升后总的净 APY;`native` 是不包括奖励的基础市场收益。所有值都是小数(`0.05` = 5%)。 + +### `position()` + +返回已配置地址的当前 Vault 头寸。 + +```ts +const position = await reader.earn.position(); +``` + +```text +{ shares: 100000000n, sharesFormatted: 100, shareDecimals: 6, assets: 101.2, tokenDecimals: 6, assetAddress: "0x..." } +``` + +返回 `VaultPosition`,其中包含原始 `shares` 余额、其 `sharesFormatted` 人类可读值以及这些份额以基础单位表示的 `assets` 值。 + +### `preview(params)` + +使用保险库的实时净 APY,预测一段时间内的收益。 + +```ts +const preview = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +``` + +```text +{ projectedYield: 4.64, apy: 0.058, horizonDays: 30 } +``` + +| **参数** | **类型** | **描述** | +| :--- | :--- | :--- | +| `amount` | `number` | 要预测的本金,人类可读。 | +| `horizonDays` | `number` | 预测周期,以天为单位。 | + +返回 `YieldPreview` (`{ projectedYield, apy, horizonDays }`)。 + +### `withdrawability(params)` + +检查是否可以从保管库的闲置流动性中即时提取一定数量。 + +```ts +const status = await reader.earn.withdrawability({ amount: 5000 }); +``` + +```text +{ isInstant: true, availableLiquidity: 12500.5, tokenDecimals: 6 } +``` + +| **参数** | **类型** | **默认值** | **描述** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | 要测试的数量,人类可读。 | +| `tokenDecimals` | `number?` | on-chain | 基础资产的小数位数。 | + +返回 `WithdrawStatus` (`{ isInstant, availableLiquidity, tokenDecimals }`)。当 `isInstant` 为 `false` 时,提款需要适配器解除分配,`stable.earn.withdraw` 会自动处理。 + +### `incentiveRewards.fetch()` + +读取已配置地址的待处理 Merkl 奖励。与 `stable.earn.incentiveRewards.fetch` 对应的只读方法。 + +```ts +const { rewards } = await reader.earn.incentiveRewards.fetch(); +``` + +```text +{ chainId: 988, rewards: [ { token: { symbol: "MORPHO", ... }, amount: 1.25, rawAmount: "1250000...", proofs: [...] } ] } +``` + +返回 `MerklRewardsResult`。 ## 枚举 @@ -168,7 +443,7 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); ### `Chain` -由 `quoteBridge` 和 `bridge` 使用。每个条目都有相应的 `CHAIN_CONFIGS` 条目。 +由 `quoteBridge` 和 `bridge` 使用。每个条目都有一个相应的 `CHAIN_CONFIGS` 条目。定义两个测试网条目是为了完整性,但 LI.FI 拒绝它们:桥接仅在主网链之间工作。 | **枚举** | **网络** | **链 ID** | | :--- | :--- | :--- | @@ -186,7 +461,7 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); ## `CHAIN_CONFIGS` -由 `Chain` 枚举键入的 `Partial>`。每个条目都公开 `id`、`rpc`、`usdt` 和 `decimals`。当您需要支持链上的规范 USDT 地址而无需硬编码时使用。 +由 `Chain` 枚举键入的 `Partial>`。每个条目都公开 `id`、`rpc`、`usdt` 和 `decimals`。当您需要支持的链上的规范 USDT 地址而无需硬编码时使用。 ```ts import { CHAIN_CONFIGS, Chain } from "@stablechain/sdk"; @@ -198,16 +473,35 @@ console.log(CHAIN_CONFIGS[Chain.Stable]); { id: 988, rpc: "https://rpc.stable.xyz", usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", decimals: 6 } ``` +## 常量 + +规范主网地址,导出以便您不必硬编码。 + +```ts +import { STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +``` + +```text +STABLE_USDT_ADDRESS 0x779ded0c9e1022225f8e0630b35a9b54be713736 +STABLE_VAULT_ADDRESS 0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08 +``` + +| **常量** | **描述** | +| :--- | :--- | +| `STABLE_USDT_ADDRESS` | Stable 主网上的 USDT 代币地址。 | +| `STABLE_VAULT_ADDRESS` | Stable 主网上的默认 Morpho V2 earn vault。传递给 `earn: { vault }`。 | + ## 错误 所有 SDK 错误都扩展了 `StableError`,后者又扩展了 viem 的 `BaseError`。错误携带结构化元数据,因此您可以根据 `error.name` 或 `instanceof` 进行分支。 -| **类** | **抛出条件** | **有用字段** | +| **类** | **抛出时机** | **有用字段** | | :--- | :--- | :--- | -| `StableValidationError` | 参数验证失败(地址错误、非有限金额、不支持的链)。 | `field`, `value` | +| `StableValidationError` | 参数验证失败(地址错误、金额非有限、不支持的链)。 | `field`, `value` | | `StableQuoteError` | 向 LI.FI 发出的报价请求失败。 | `provider`, `httpStatus`, `providerCode`, `body` | | `StableTransactionError` | 链上步骤失败:链切换、批准、发送或回滚。 | `phase`, `txHash`, `chainId`, `revertReason` | -| `StableNetworkError` | 底层 HTTP/RPC 调用失败。 | `url` | +| `StableNetworkError` | 底层 HTTP/RPC 调用失败(例如 Morpho 或 Merkl API)。 | `url` | +| `StableBulkError` | 带有 `stopOnError: true` 的 `bulkDeposit` 或 `bulkWithdraw` 运行,遇到首次失败。 | `results`, `succeeded`, `failed` | ```ts import { StableTransactionError } from "@stablechain/sdk"; @@ -228,8 +522,8 @@ StableTransactionError: transfer: wallet rejected or failed to switch to chain 9 Chain ID: 988 ``` -## 建议下一步 +## 接下来推荐 -- [**SDK 快速入门**](/cn/tutorial/sdk-quickstart):安装 SDK 并在测试网上运行您的首次传输。 -- [**与 viem 结合使用**](/cn/how-to/sdk-with-viem):在私钥、浏览器钱包和预构建的签名器之间切换。 -- [**与 wagmi 结合使用**](/cn/how-to/sdk-with-wagmi):使用挂钩将 SDK 连接到 React 应用程序。 +- [**SDK 快速入门**](/cn/tutorial/sdk-quickstart):安装 SDK 并在测试网上运行您的首次转账。 +- [**与 viem 结合使用**](/cn/how-to/sdk-with-viem):在私钥、浏览器钱包和预建签名器之间切换。 +- [**与 wagmi 结合使用**](/cn/how-to/sdk-with-wagmi):使用 Hooks 将 SDK 连接到 React 应用程序。 diff --git a/docs/pages/cn/tutorial/sdk-quickstart.mdx b/docs/pages/cn/tutorial/sdk-quickstart.mdx index 46535db..b45432f 100644 --- a/docs/pages/cn/tutorial/sdk-quickstart.mdx +++ b/docs/pages/cn/tutorial/sdk-quickstart.mdx @@ -1,36 +1,39 @@ --- source_path: tutorial/sdk-quickstart.mdx -source_sha: 24bdfc4340389e06e1cb06ee6a7f3e1939940c97 +source_sha: c918339737f4e49e3639b4e61c16abf0e1a9376e title: "SDK 快速入门" -description: "安装 Stable SDK,在测试网上发送第一个 USDT0 转账,并预览桥接和互换报价。" +description: "安装 Stable SDK,在测试网上发送您的第一个 USDT0 转账,并预览主网桥接报价。" diataxis: "tutorial" --- # SDK 快速入门 -你将安装 `@stablechain/sdk`,使用私钥创建客户端,在 Stable 测试网上发送 USDT0 转账,并获取桥接和互换报价。总耗时:大约五分钟。 +您将安装 `@stablechain/sdk`,创建一个由私钥签名的客户端,在 Stable 测试网上发送 USDT0 转账,并获取主网桥接报价。总共耗时:大约五分钟。 :::note -Stable 使用 USDT0 作为 Gas 代币。你只需要测试网 USDT0 即可进行交易。无需为单独的原生资产充值。 +Stable 使用 USDT0 作为 Gas 代币。您只需要测试网 USDT0 即可进行交易。无需单独资助原生资产。 ::: ## 前提条件 - Node.js 20 或更高版本 -- 一个包含测试网 USDT0 的测试私钥。请参阅[为你的测试网钱包充值](/cn/how-to/use-faucet)。 +- 一个包含测试网 USDT0 的测试私钥。请参阅[为您的测试网钱包充值](/cn/how-to/use-faucet)。 ## 1. 安装 ```bash mkdir stable-sdk-quickstart && cd stable-sdk-quickstart -npm init -y && npm install @stablechain/sdk viem +npm init -y && npm pkg set type=module +npm install @stablechain/sdk viem dotenv ``` ```text -added 2 packages, audited 3 packages in 2s +added 3 packages, audited 4 packages in 2s ``` -保存你的测试密钥: +`type=module` 允许您在下面的步骤中使用顶层 `await`;如果没有它,`tsx` 将拒绝该脚本。 + +保存您的测试密钥: ```bash echo "PRIVATE_KEY=0xYOUR_TEST_KEY" > .env @@ -59,7 +62,7 @@ console.log("Signer:", account.address); Signer: 0xYourAddress ``` -`createStable` 接受三种签名模式:`account`(服务器端,如上所示)、`transport`(通过 `custom(window.ethereum)` 进行浏览器钱包签名)或 `walletClient`(预构建的 viem `WalletClient`)。有关所有三种模式的详细信息,请参阅[将 SDK 与 viem 结合使用](/cn/how-to/sdk-with-viem)。 +`createStable` 支持三种签名模式:`account`(服务器端,如上所示)、`transport`(通过 `custom(window.ethereum)` 的浏览器钱包)或 `walletClient`(预构建的 viem `WalletClient`)。有关所有三种模式的详细信息,请参阅[将 SDK 与 viem 结合使用](/cn/how-to/sdk-with-viem)。 ## 3. 发送 USDT0 转账 @@ -75,7 +78,7 @@ const { txHash } = await stable.transfer({ console.log("Transfer:", txHash); ``` -运行: +运行它: ```bash npx tsx index.ts @@ -86,55 +89,48 @@ Signer: 0xYourAddress Transfer: 0x8f3a...2d41 ``` -在 [testnet explorer](https://testnet.stablescan.xyz) 上打开哈希以确认。 +在[测试网浏览器](https://testnet.stablescan.xyz)上打开哈希以进行确认。 -## 4. 报价桥接 +## 4. 获取桥接报价 -将 USDT0 从 Ethereum Sepolia 桥接到 Stable Testnet。`quoteBridge` 是一个只读调用,无需签名和 Gas 费用: +桥接和交换路由通过 LI.FI,它支持 Stable 主网但不支持测试网。`quoteBridge` 是一个只读调用(无需签名、无需 Gas、无需资金),因此您可以使用相同的密钥预览主网桥接。追加: ```ts import { Chain } from "@stablechain/sdk"; -const bridgeQuote = await stable.quoteBridge({ - fromChain: Chain.Sepolia, - toChain: Chain.StableTestnet, - fromToken: "0xc4DCC311c028e341fd8602D8eB89c5de94625927", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, +const stableMainnet = createStable({ + network: Network.Mainnet, + account, +}); + +const bridgeQuote = await stableMainnet.quoteBridge({ + fromChain: Chain.Ethereum, + toChain: Chain.Stable, + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // 以太坊上的 USDT + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // Stable 上的 USDT0 + amount: 100, }); console.log("Bridge quote:", bridgeQuote); ``` ```text -Bridge quote: { toAmount: 0.999812 } +Bridge quote: { toAmount: 99.73 } ``` -将报价传递给 `stable.bridge({ ...params, quote })` 以执行。SDK 会为 USDT0 → USDT0 路由选择 LayerZero,并为其他所有路由选择 LI.FI。 - -## 5. 报价互换 - -互换在 Stable 上通过 LI.FI 运行。报价返回预期输出和预构建的交易: - -```ts -const swapQuote = await stable.quoteSwap({ - fromToken: "0x8a2B28364102Bea189D99A475C494330Ef2bDD0B", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, - fromDecimals: 6, -}); +将报价传递给 `stableMainnet.bridge({ ...params, quote })` 以执行。路由通过 LI.FI,它选择桥接路径;SDK 处理批准和链切换。 -console.log("You'll receive:", swapQuote.toAmount, "USDT0"); -``` +## 5. 交换 -```text -You'll receive: 0.998 USDT0 -``` +同链交换遵循相同的模式:`stable.quoteSwap` 返回预期输出和一个预构建的交易,`stable.swap({ ...params, quote })` 执行它,内部处理 ERC-20 批准。 -调用 `stable.swap({ ...params, quote: swapQuote })` 以执行。ERC-20 源的批准是在内部处理的。 +:::note +交换路由取决于 LI.FI 在 Stable 上的 DEX 覆盖范围,目前仍然有限。当某个交易对没有可用的路由时,`quoteSwap` 和 `swap` 会抛出 `StableQuoteError` 并显示“No available quotes for the requested transfer”的错误信息。有关完整的 API,请参阅[`quoteSwap` 在 SDK 参考中](/cn/reference/sdk#quoteswapparams)。 +::: -## 接下来推荐 +## 推荐的下一步 - [**SDK 参考**](/cn/reference/sdk):每个参数、返回类型和错误类。 -- [**与 viem 结合使用**](/cn/how-to/sdk-with-viem):在私钥、浏览器钱包和预构建的 `WalletClient` 签名之间切换。 -- [**与 wagmi 结合使用**](/cn/how-to/sdk-with-wagmi):使用 wagmi 钩子将 SDK 连接到 React 应用。 +- [**使用 SDK 赚取收益**](/cn/how-to/earn-yield):将 USDT0 存入主网保险库,查看您的 APY 和头寸,然后提款。 +- [**与 viem 一起使用**](/cn/how-to/sdk-with-viem):在私钥、浏览器钱包和预构建的 `WalletClient` 签名之间切换。 +- [**与 wagmi 一起使用**](/cn/how-to/sdk-with-wagmi):使用 wagmi 钩子将 SDK 接入 React 应用程序。 diff --git a/docs/pages/en/explanation/sdk-overview.mdx b/docs/pages/en/explanation/sdk-overview.mdx index efcb1b7..de079c1 100755 --- a/docs/pages/en/explanation/sdk-overview.mdx +++ b/docs/pages/en/explanation/sdk-overview.mdx @@ -1,12 +1,12 @@ --- title: "Stable SDK" -description: "Use the typed TypeScript SDK to transfer USDT0, bridge between chains, and swap tokens on Stable in a few lines." +description: "Use the typed TypeScript SDK to transfer USDT0, bridge between chains, swap tokens, and earn vault yield on Stable in a few lines." diataxis: "explanation" --- # Stable SDK -`@stablechain/sdk` is the official TypeScript client for Stable. It wraps viem with a small, typed API for the operations you reach for most: transfer USDT0, bridge between chains, and swap tokens on Stable. Routing, approvals, decimals, and chain switching are handled for you. +`@stablechain/sdk` is the official TypeScript client for Stable. It wraps viem with a small, typed API for the operations you reach for most: transfer USDT0, bridge between chains, swap tokens, and earn yield on Stable. Routing, approvals, decimals, and chain switching are handled for you. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -31,8 +31,10 @@ txHash: 0x8f3a...2d41 ## What the SDK does - **`transfer`**: send native USDT0 or any ERC-20 on Stable. Gas is paid in USDT0 automatically. -- **`quoteBridge` / `bridge`**: cross-chain transfers. LayerZero for USDT0 → USDT0, LI.FI for everything else. Route is picked for you. +- **`quoteBridge` / `bridge`**: cross-chain transfers via LI.FI, which picks the bridge route for you. Approval and chain switching are handled internally. - **`quoteSwap` / `swap`**: same-chain token swaps via LI.FI, with ERC-20 approval handled internally. +- **`earn`**: supply USDT0 into a Morpho V2 vault to earn yield, withdraw or redeem, and claim Merkl incentive rewards. Approvals and adapter deallocations are handled for you. +- **`createStableReader`**: a read-only client for vault APY, your position, yield projections, and instant-withdrawability checks. No signer required. The SDK is published on npm as [`@stablechain/sdk`](https://www.npmjs.com/package/@stablechain/sdk) and requires `viem >= 2.0.0` as a peer dependency. @@ -47,6 +49,7 @@ The SDK signs with any viem-compatible signer: a private-key `Account`, a browse ## Start here - [**Quickstart**](/en/tutorial/sdk-quickstart): Install the SDK and run your first transfer, bridge, and swap on testnet. +- [**Earn yield with the SDK**](/en/how-to/earn-yield): Deposit into a vault, read your APY and position, withdraw, and claim rewards. - [**SDK reference**](/en/reference/sdk): Every method, config option, enum, and error class. - [**Use with viem**](/en/how-to/sdk-with-viem): Server-side accounts, browser wallets, and bring-your-own `WalletClient`. - [**Use with wagmi**](/en/how-to/sdk-with-wagmi): Wire the SDK into a React app with `useWalletClient` and hooks. diff --git a/docs/pages/en/how-to/earn-yield.mdx b/docs/pages/en/how-to/earn-yield.mdx new file mode 100644 index 0000000..03f36a0 --- /dev/null +++ b/docs/pages/en/how-to/earn-yield.mdx @@ -0,0 +1,176 @@ +--- +title: "Earn yield with the SDK" +description: "Deposit USDT0 into a Morpho vault with the Stable SDK, read your APY and position, withdraw, and claim Merkl rewards." +diataxis: "how-to" +--- + +# Earn yield with the SDK + +Supply USDT0 into a Morpho V2 vault on Stable mainnet to earn yield, then read your position and withdraw. The `@stablechain/sdk` `earn` methods handle the ERC-20 approval, the deposit, and adapter deallocations on withdrawal, so you call one method per action. + +Two clients are involved. `createStable` gives you a signing client for deposits and withdrawals. `createStableReader` gives you a read-only client for APY, position, and withdrawability, with no signer. + +## Prerequisites + +- Node.js 20 or later, and `@stablechain/sdk` plus `viem` installed. See the [SDK quickstart](/en/tutorial/sdk-quickstart). +- A signer with USDT0 on Stable mainnet. To move funds in, use [`stable.bridge`](/en/reference/sdk#bridgeparams) or one of the [supported bridges](/en/reference/bridges). +- The vault address. The SDK exports the default mainnet vault as `STABLE_VAULT_ADDRESS`. + +:::note +The earn examples run on mainnet. The SDK ships no testnet vault address, so `Network.Testnet` only works with a vault you deploy yourself. +::: + +## 1. Create a signing client with a vault + +Pass `earn: { vault }` to `createStable`. Without it, the deposit and withdraw methods throw `StableValidationError`. + +```ts +import { createStable, Network, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +import { privateKeyToAccount } from "viem/accounts"; + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); + +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); +``` + +```text +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } +``` + +## 2. Check the APY before depositing + +Create a read-only reader and fetch the vault's current net APY. The reader needs the address you want to read and the vault, but no signer. + +```ts +import { createStableReader } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: account.address, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); + +const { apy, native, rewards } = await reader.earn.getYield(); +console.log("Net APY:", (apy * 100).toFixed(2) + "%"); +``` + +```text +Net APY: 5.80% +``` + +`apy` is the total net APY after fees, including reward boosts. `native` is the underlying market yield on its own, and `rewards` breaks out each reward token's APR contribution. All values are decimals, so `0.058` is 5.8%. + +To estimate what an amount earns over time, use `preview`: + +```ts +const { projectedYield } = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +console.log("30-day projection on 1000:", projectedYield.toFixed(2)); +``` + +```text +30-day projection on 1000: 4.64 +``` + +## 3. Deposit + +Supply assets to the vault. The SDK sends the approval (or a permit signature when the wallet supports it), then the deposit, and waits for the receipt. + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +console.log("Deposit:", txHash); +``` + +```text +Deposit: 0x8f3a...2d41 +``` + +Amounts are human-readable. The underlying asset decimals are fetched on-chain unless you pass `tokenDecimals`. To cap how much of the vault's idle liquidity a single deposit consumes, pass `depositBuffer` (for example `0.8` for 80%); the SDK throws before sending if the amount exceeds the cap. + +:::tip +Pass an `idempotencyKey` to make a deposit safe to retry. If a call with the same key is already in flight, the SDK returns the same Promise instead of sending a second transaction. +::: + +## 4. Read your position + +Fetch your share balance and its current asset value. + +```ts +const position = await reader.earn.position(); +console.log("Shares:", position.sharesFormatted, "worth", position.assets); +``` + +```text +Shares: 100 worth 100.02 +``` + +`shares` is the raw balance and `sharesFormatted` is its human value. `assets` is what those shares are worth in underlying units right now. + +## 5. Withdraw or redeem + +Withdraw a specific amount of underlying assets, or redeem a number of shares. When the vault's idle liquidity is short, the SDK automatically deallocates from the vault's adapters to cover the difference. + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +console.log("Withdraw:", txHash); +``` + +```text +Withdraw: 0xabcd...7890 +``` + +To redeem by share count instead, use `stable.earn.redeem({ shares: 25 })`. To check first whether an amount can come out instantly, call the reader: + +```ts +const { isInstant, availableLiquidity } = await reader.earn.withdrawability({ amount: 50 }); +console.log("Instant:", isInstant, "available:", availableLiquidity); +``` + +```text +Instant: true available: 12500.5 +``` + +When `isInstant` is `false`, the withdrawal still succeeds; it just requires an adapter deallocation, which `withdraw` and `redeem` handle for you. For manual control over which adapters to pull from, use [`forceWithdraw` / `forceRedeem`](/en/reference/sdk#forcewithdrawparams--forceredeemparams). + +## 6. Claim incentive rewards + +Vaults can earn Merkl reward tokens on top of native yield. Fetch pending rewards, then claim them. These methods need a signer but not the `earn` vault config. + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +if (rewards.length > 0) { + const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); + console.log("Claimed", tokenCount, "token(s):", txHash); +} +``` + +```text +Claimed 1 token(s): 0xabcd...7890 +``` + +:::note +Merkl commits rewards to its merkle tree roughly every two hours. Rewards still pending that update cannot be claimed yet, and calling `claim()` with nothing claimable throws `StableValidationError`. From a browser, set `merklApiBase` to a proxy on your own server to avoid CORS limits on the Merkl API. +::: + +## Don't sign in the SDK? + +If you sign transactions elsewhere (a relayer, a gas-waiver flow, or a multisig), build the calldata without sending it: + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`steps` is an ordered list of transactions, an optional approval followed by the deposit. `prepareWithdrawCalldata` and `prepareRedeemCalldata` each return a single `{ to, data, value, chainId }` transaction. + +## Where to go next + +- [**SDK reference**](/en/reference/sdk#stableearn): Every earn parameter, return type, and the reader methods in full. +- [**Use with wagmi**](/en/how-to/sdk-with-wagmi): Wire the SDK into a React app so users deposit from a browser wallet. +- [**SDK overview**](/en/explanation/sdk-overview): When to use the SDK, and how signing modes work. diff --git a/docs/pages/en/how-to/sdk-with-viem.mdx b/docs/pages/en/how-to/sdk-with-viem.mdx index 48be50a..7e53a41 100755 --- a/docs/pages/en/how-to/sdk-with-viem.mdx +++ b/docs/pages/en/how-to/sdk-with-viem.mdx @@ -41,7 +41,7 @@ console.log(txHash); ## Browser-side: `Transport` from a wallet -Pass `custom(window.ethereum)` (or any EIP-1193 provider) as `transport`. The SDK builds the `WalletClient` and reads the signer address from the provider. +Pass `custom(window.ethereum)` (or any EIP-1193 provider) as `transport`. The SDK builds a `WalletClient` from the transport and signs as the `from` address you pass to `transfer`. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -65,6 +65,10 @@ const { txHash } = await stable.transfer({ 0x8f3a...2d41 ``` +:::warning +Transport mode covers `transfer` only, because `transfer` takes an explicit `from`. `swap`, `quoteSwap`, `bridge`, and the `earn` methods read the signer address from `account` or `walletClient` and throw `StableValidationError` when neither is set. For those, build a `WalletClient` from the same provider instead (next section). +::: + :::warning `transfer`, `bridge`, and `swap` call `switchChain` to put the wallet on the right network. If the user rejects, the SDK throws `StableTransactionError` with `phase: "switch_chain"`. Catch it and surface a retry to the user. ::: @@ -102,7 +106,7 @@ const { txHash } = await stable.transfer({ from, to: "0xRecipient", amount: 5 }) | **Mode** | **Use when** | | :--- | :--- | | `account` | Backend services, scripts, agents, anywhere you hold the key. | -| `transport` | Browser apps where the user signs with MetaMask or a wagmi-less custom flow. | +| `transport` | Browser apps that only call `transfer` with an explicit `from`. | | `walletClient` | You already have a configured `WalletClient` (wagmi, RainbowKit, ConnectKit). | ## Next recommended diff --git a/docs/pages/en/how-to/sdk-with-wagmi.mdx b/docs/pages/en/how-to/sdk-with-wagmi.mdx index 396745c..2c4c2bc 100755 --- a/docs/pages/en/how-to/sdk-with-wagmi.mdx +++ b/docs/pages/en/how-to/sdk-with-wagmi.mdx @@ -92,7 +92,7 @@ Sent: 0x8f3a...2d41 ## 4. Bridge and swap from React -The same `stable` instance handles bridge and swap. Fetch the quote in an effect or a `useQuery`, then execute on click. +The same `stable` instance handles bridge and swap. Fetch the quote in an effect or a `useQuery`, then execute on click. Catch `StableQuoteError`: LI.FI's DEX coverage on Stable is still limited, and pairs without a route throw "No available quotes for the requested transfer". ```tsx const stable = useStable(Network.Mainnet); diff --git a/docs/pages/en/reference/sdk.mdx b/docs/pages/en/reference/sdk.mdx index 64d437c..cb2522d 100755 --- a/docs/pages/en/reference/sdk.mdx +++ b/docs/pages/en/reference/sdk.mdx @@ -1,12 +1,12 @@ --- title: "SDK reference" -description: "Complete reference for @stablechain/sdk: createStable, StableClient methods, enums, chain configs, and error classes." +description: "Complete reference for @stablechain/sdk: createStable, createStableReader, transfer, bridge, swap, earn and yield methods, enums, and error classes." diataxis: "reference" --- # SDK reference -Full surface of `@stablechain/sdk`. For a walkthrough, see [SDK quickstart](/en/tutorial/sdk-quickstart). +Full surface of `@stablechain/sdk`. This covers the signing client from [`createStable`](#createstableconfig), the read-only client from [`createStableReader`](#createstablereaderconfig), and the shared enums, constants, and error classes. For a walkthrough, see [SDK quickstart](/en/tutorial/sdk-quickstart). For a task-focused earn guide, see [Earn yield with the SDK](/en/how-to/earn-yield). ## Install @@ -31,7 +31,7 @@ const stable = createStable({ network: Network.Mainnet, account }); ``` ```text -StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } ``` ### `StableConfig` @@ -43,8 +43,10 @@ StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } | `account` | `viem.Account` | | Server-side signer (e.g. `privateKeyToAccount`). | | `transport` | `viem.Transport` | | Browser-wallet transport (e.g. `custom(window.ethereum)`). | | `walletClient` | `viem.WalletClient` | | Pre-built wallet client. Takes precedence over `account` and `transport`. | +| `earn` | `{ vault: string }` | | Morpho V2 vault config. Required to call `stable.earn` deposit, withdraw, redeem, and prepare-calldata methods. | +| `merklApiBase` | `string` | `https://api.merkl.xyz` | Merkl rewards API base URL. Override to proxy through your own server and avoid browser CORS limits. | -Provide one of `account`, `transport`, or `walletClient`. +Provide one of `account`, `transport`, or `walletClient`. The `earn` field is optional: transfer, bridge, and swap work without it. Vault deposits and withdrawals require it, and the SDK throws `StableValidationError` if you call them without a configured vault. ## `StableClient` @@ -82,21 +84,25 @@ Preview a bridge. Read-only. No signature, no gas. const quote = await stable.quoteBridge({ fromChain: Chain.Ethereum, toChain: Chain.Stable, - fromToken: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", - toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT on Ethereum + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // USDT0 on Stable amount: 100, }); ``` ```text -{ toAmount: 99.94 } +{ toAmount: 99.73 } ``` Returns `BridgeQuote`. +:::note +LI.FI serves quotes for the mainnet chains in the [`Chain`](#chain) enum only. `Chain.Sepolia` and `Chain.StableTestnet` are rejected with `StableQuoteError`, and some token addresses in `CHAIN_CONFIGS` (such as USDT0 on Ethereum) are on LI.FI's deny list for quoting. When in doubt, quote with the chain's canonical token. +::: + ### `bridge(params)` -Bridge tokens cross-chain. The SDK picks the route: LayerZero for USDT0 → USDT0, LI.FI for everything else. Pass a pre-fetched `quote` to skip the internal quote call. +Bridge tokens cross-chain via LI.FI, which picks the bridge route. The SDK handles the ERC-20 approval and switches the wallet to the source chain before sending. Pass a pre-fetched `quote` to skip the internal quote call. ```ts const { txHash } = await stable.bridge({ ...bridgeParams, quote }); @@ -134,6 +140,10 @@ const quote = await stable.quoteSwap({ { toAmount: 99.81, fromAmount: 100000000n, fromToken: "0x8a2B...", approvalAddress: "0x...", transactionRequest: { ... } } ``` +:::note +Swap routes depend on LI.FI's DEX coverage on Stable, which is still limited. When no route exists for a pair, `quoteSwap` and `swap` throw `StableQuoteError` with "No available quotes for the requested transfer". +::: + ### `swap(params)` Swap tokens on Stable via LI.FI. Handles ERC-20 approval automatically and switches the wallet's chain when needed. @@ -155,6 +165,271 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); | `toAddress` | `string?` | signer | Recipient address. | | `quote` | `SwapQuote?` | | Pre-fetched quote. Skips LI.FI call. | +## `stable.earn` + +Supply USDT0 into a Morpho V2 vault to earn yield, and claim Merkl incentive rewards. All methods live under the `earn` namespace and require a signer. The deposit, withdraw, redeem, and prepare-calldata methods also require `earn: { vault }` in [`StableConfig`](#stableconfig). + +```ts +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +stable.earn { deposit, bulkDeposit, withdraw, bulkWithdraw, redeem, forceWithdraw, forceRedeem, prepareDepositCalldata, prepareWithdrawCalldata, prepareRedeemCalldata, incentiveRewards } +``` + +Amounts and share counts are human-readable numbers. Asset and share decimals are fetched on-chain when you omit them. The deposit, withdraw, redeem, and force methods return `EarnResult` (`{ txHash, idempotencyKey? }`) and wait for the receipt before resolving. The bulk methods return `BulkEarnResult` and `incentiveRewards.claim()` returns `MerklClaimResult`, described in their sections below. + +### `deposit(params)` + +Supply assets to the vault. Handles the ERC-20 approval (or a permit signature when the wallet supports it), then the deposit, in one call. + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +``` + +```text +{ txHash: "0x8f3a...2d41" } +``` + +| **Param** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | Underlying assets to deposit, human-readable. | +| `vault` | `string?` | config vault | Vault address override. Required per-item in `bulkDeposit` across multiple vaults. | +| `tokenDecimals` | `number?` | on-chain | Underlying asset decimals. | +| `slippageTolerance` | `number?` | `0.0003` | Slippage as a decimal (e.g. `0.003` = 0.3%). Defaults to Morpho's 0.03%. | +| `depositBuffer` | `number?` | | Max fraction of the vault's idle liquidity this deposit may consume (e.g. `0.8` = 80%). Must be `> 0` and `≤ 1`. Throws before sending if exceeded. | +| `idempotencyKey` | `string?` | | Deduplication key. A second call with an in-flight key returns the same Promise. | + +### `withdraw(params)` + +Withdraw a given amount of underlying assets. When idle vault liquidity is short, the SDK automatically deallocates from the vault's adapters to cover the difference. + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **Param** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | Underlying assets to withdraw, human-readable. | +| `tokenDecimals` | `number?` | on-chain | Underlying asset decimals. | +| `idempotencyKey` | `string?` | | Deduplication key. | + +### `redeem(params)` + +Redeem a number of vault shares back to underlying assets. Like `withdraw`, it deallocates from adapters automatically when idle liquidity is short. + +```ts +const { txHash } = await stable.earn.redeem({ shares: 25 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **Param** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `shares` | `number` | | Vault shares to redeem, human-readable. | +| `shareDecimals` | `number?` | on-chain | Vault share decimals. | +| `idempotencyKey` | `string?` | | Deduplication key. | + +### `bulkDeposit(items, options?)` / `bulkWithdraw(items, options?)` + +Run several deposits or withdrawals in sequence. By default the batch attempts every item and returns partial results. Set `stopOnError: true` to halt on the first failure and throw `StableBulkError`. + +```ts +const result = await stable.earn.bulkDeposit([ + { amount: 100 }, + { amount: 250, vault: "0xAnotherVault" }, +]); +``` + +```text +{ results: [ { status: "fulfilled", txHash: "0x..." }, ... ], succeeded: 2, failed: 0 } +``` + +`items` is an array of `EarnDepositParams` (or `EarnWithdrawParams`). `options` accepts: + +| **Option** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `idempotencyKey` | `string?` | | Batch-level deduplication key. | +| `stopOnError` | `boolean?` | `false` | Halt on the first failure and throw `StableBulkError` instead of returning partial results. | + +Returns `BulkEarnResult` (`{ results, succeeded, failed, idempotencyKey? }`). Each entry in `results` is either `{ status: "fulfilled", txHash, vault?, idempotencyKey? }` or `{ status: "rejected", error, vault?, idempotencyKey? }`. + +### `forceWithdraw(params)` / `forceRedeem(params)` + +Withdraw or redeem with explicit control over which adapter positions to deallocate first. Use these when you need to choose the source markets yourself instead of the automatic selection in `withdraw` / `redeem`. + +```ts +const { txHash } = await stable.earn.forceWithdraw({ + amount: 1000, + deallocations: [{ adapter: "0xAdapter", amount: 1000 }], +}); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +Both take a `deallocations: ForceDeallocation[]` array and an optional `tokenDecimals` (underlying asset decimals, used for the deallocation amounts; fetched on-chain when omitted). `forceWithdraw` takes `amount`; `forceRedeem` takes `shares` plus optional `shareDecimals`. Each `ForceDeallocation` is: + +| **Field** | **Type** | **Description** | +| :--- | :--- | :--- | +| `adapter` | `string` | Adapter contract to deallocate from. | +| `amount` | `number` | Amount to deallocate, in underlying asset units. | +| `marketParams` | `object?` | Required only for Morpho Blue market adapters: `{ loanToken, collateralToken, oracle, irm, lltv }` (`lltv` scaled by 1e18). Omit for vault adapters. | + +### `prepareDepositCalldata(params)` / `prepareWithdrawCalldata(params)` / `prepareRedeemCalldata(params)` + +Build the transaction calldata without signing or sending. Use these to route the transaction through a relayer, a gas-waiver flow, or a multisig. + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`prepareDepositCalldata` returns `EarnDepositCalldata` (`{ steps, chainId }`), where `steps` is an ordered list of `{ to, data, value }` transactions: an optional token approval followed by the deposit. `prepareWithdrawCalldata` and `prepareRedeemCalldata` return `EarnWithdrawCalldata` (`{ to, data, value, chainId }`), a single transaction. They accept the same params as their signing counterparts. + +### `incentiveRewards` + +Fetch and claim Merkl reward tokens for the signer. These methods need a signer but not an `earn` vault config. + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); +``` + +```text +{ txHash: "0xabcd...7890", tokenCount: 1 } +``` + +`fetch()` returns `MerklRewardsResult` (`{ chainId, rewards: MerklReward[] }`) with the net claimable amount per token. `claim()` claims every reward committed to the current Merkl merkle tree and returns `MerklClaimResult` (`{ txHash, tokenCount }`). + +:::note +Merkl commits rewards to its merkle tree roughly every two hours. Rewards that are still pending that update cannot be claimed yet. Calling `claim()` with nothing claimable throws `StableValidationError`. +::: + +## `createStableReader(config)` + +Construct a read-only `StableReader` for vault yield and position data. No signer is needed, only the address you want to read. + +```ts +import { createStableReader, Network } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: "0xUserAddress", + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +StableReader { earn: { getYield, position, preview, withdrawability, incentiveRewards } } +``` + +### `StableReaderConfig` + +| **Field** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `address` | `string` | | User address whose position and rewards to read. Required. | +| `earn` | `{ vault: string }` | | Vault to read. Required: `createStableReader` throws `StableValidationError` when it's missing. | +| `network` | `Network?` | `Network.Mainnet` | Target network. | +| `rpc` | `string?` | Public RPC | RPC override. | +| `merklApiBase` | `string?` | `https://api.merkl.xyz` | Merkl rewards API base URL. | + +## `StableReader` + +### `getYield()` + +Return the vault's current net APY and fee breakdown. + +```ts +const vaultYield = await reader.earn.getYield(); +``` + +```text +{ apy: 0.058, native: 0.041, rewards: [ { symbol: "MORPHO", address: "0x...", apr: 0.017 } ], performanceFee: 0.1, managementFee: 0 } +``` + +Returns `VaultYield`. `apy` is the total net APY after fees including reward boosts; `native` is the underlying market yield excluding rewards. All values are decimals (`0.05` = 5%). + +### `position()` + +Return the configured address's current vault position. + +```ts +const position = await reader.earn.position(); +``` + +```text +{ shares: 100000000n, sharesFormatted: 100, shareDecimals: 6, assets: 101.2, tokenDecimals: 6, assetAddress: "0x..." } +``` + +Returns `VaultPosition` with the raw `shares` balance, its `sharesFormatted` human value, and the `assets` value of those shares in underlying units. + +### `preview(params)` + +Project the yield on an amount over a horizon, using the vault's live net APY. + +```ts +const preview = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +``` + +```text +{ projectedYield: 4.64, apy: 0.058, horizonDays: 30 } +``` + +| **Param** | **Type** | **Description** | +| :--- | :--- | :--- | +| `amount` | `number` | Principal to project, human-readable. | +| `horizonDays` | `number` | Projection horizon in days. | + +Returns `YieldPreview` (`{ projectedYield, apy, horizonDays }`). + +### `withdrawability(params)` + +Check whether an amount can be withdrawn instantly from the vault's idle liquidity. + +```ts +const status = await reader.earn.withdrawability({ amount: 5000 }); +``` + +```text +{ isInstant: true, availableLiquidity: 12500.5, tokenDecimals: 6 } +``` + +| **Param** | **Type** | **Default** | **Description** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | Amount to test, human-readable. | +| `tokenDecimals` | `number?` | on-chain | Underlying asset decimals. | + +Returns `WithdrawStatus` (`{ isInstant, availableLiquidity, tokenDecimals }`). When `isInstant` is `false`, the withdrawal needs an adapter deallocation, which `stable.earn.withdraw` handles automatically. + +### `incentiveRewards.fetch()` + +Read pending Merkl rewards for the configured address. Read-only counterpart to `stable.earn.incentiveRewards.fetch`. + +```ts +const { rewards } = await reader.earn.incentiveRewards.fetch(); +``` + +```text +{ chainId: 988, rewards: [ { token: { symbol: "MORPHO", ... }, amount: 1.25, rawAmount: "1250000...", proofs: [...] } ] } +``` + +Returns `MerklRewardsResult`. + ## Enums ### `Network` @@ -166,7 +441,7 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); ### `Chain` -Used by `quoteBridge` and `bridge`. Each entry has a corresponding `CHAIN_CONFIGS` entry. +Used by `quoteBridge` and `bridge`. Each entry has a corresponding `CHAIN_CONFIGS` entry. The two testnet entries are defined for completeness, but LI.FI rejects them: bridging works only between the mainnet chains. | **Enum** | **Network** | **Chain ID** | | :--- | :--- | :--- | @@ -196,6 +471,24 @@ console.log(CHAIN_CONFIGS[Chain.Stable]); { id: 988, rpc: "https://rpc.stable.xyz", usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", decimals: 6 } ``` +## Constants + +Canonical mainnet addresses, exported so you don't hard-code them. + +```ts +import { STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +``` + +```text +STABLE_USDT_ADDRESS 0x779ded0c9e1022225f8e0630b35a9b54be713736 +STABLE_VAULT_ADDRESS 0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08 +``` + +| **Constant** | **Description** | +| :--- | :--- | +| `STABLE_USDT_ADDRESS` | USDT token address on Stable mainnet. | +| `STABLE_VAULT_ADDRESS` | Default Morpho V2 earn vault on Stable mainnet. Pass to `earn: { vault }`. | + ## Errors All SDK errors extend `StableError`, which extends viem's `BaseError`. Errors carry structured metadata so you can branch on `error.name` or `instanceof`. @@ -205,7 +498,8 @@ All SDK errors extend `StableError`, which extends viem's `BaseError`. Errors ca | `StableValidationError` | A parameter fails validation (bad address, non-finite amount, unsupported chain). | `field`, `value` | | `StableQuoteError` | A quote request to LI.FI fails. | `provider`, `httpStatus`, `providerCode`, `body` | | `StableTransactionError` | On-chain step fails: chain switch, approval, send, or revert. | `phase`, `txHash`, `chainId`, `revertReason` | -| `StableNetworkError` | An underlying HTTP/RPC call fails. | `url` | +| `StableNetworkError` | An underlying HTTP/RPC call fails (e.g. the Morpho or Merkl API). | `url` | +| `StableBulkError` | A `bulkDeposit` or `bulkWithdraw` run with `stopOnError: true` hits its first failure. | `results`, `succeeded`, `failed` | ```ts import { StableTransactionError } from "@stablechain/sdk"; diff --git a/docs/pages/en/tutorial/sdk-quickstart.mdx b/docs/pages/en/tutorial/sdk-quickstart.mdx index 24bdfc4..c918339 100755 --- a/docs/pages/en/tutorial/sdk-quickstart.mdx +++ b/docs/pages/en/tutorial/sdk-quickstart.mdx @@ -1,12 +1,12 @@ --- title: "SDK quickstart" -description: "Install the Stable SDK, send your first USDT0 transfer on testnet, and preview a bridge and swap quote." +description: "Install the Stable SDK, send your first USDT0 transfer on testnet, and preview a mainnet bridge quote." diataxis: "tutorial" --- # SDK quickstart -You'll install `@stablechain/sdk`, create a client signed by a private key, send a USDT0 transfer on Stable Testnet, and fetch a bridge and swap quote. Total time: about five minutes. +You'll install `@stablechain/sdk`, create a client signed by a private key, send a USDT0 transfer on Stable Testnet, and fetch a mainnet bridge quote. Total time: about five minutes. :::note Stable uses USDT0 as the gas token. You only need testnet USDT0 to transact. There's no separate native asset to fund. @@ -21,13 +21,16 @@ Stable uses USDT0 as the gas token. You only need testnet USDT0 to transact. The ```bash mkdir stable-sdk-quickstart && cd stable-sdk-quickstart -npm init -y && npm install @stablechain/sdk viem +npm init -y && npm pkg set type=module +npm install @stablechain/sdk viem dotenv ``` ```text -added 2 packages, audited 3 packages in 2s +added 3 packages, audited 4 packages in 2s ``` +`type=module` lets you use top-level `await` in the steps below; without it, `tsx` rejects the script. + Save your test key: ```bash @@ -88,51 +91,44 @@ Open the hash on the [testnet explorer](https://testnet.stablescan.xyz) to confi ## 4. Quote a bridge -Bridge USDT0 from Ethereum Sepolia to Stable Testnet. `quoteBridge` is a read-only call, with no signature and no gas: +Bridge and swap routing goes through LI.FI, which supports Stable Mainnet but no testnets. `quoteBridge` is a read-only call (no signature, no gas, no funds needed), so you can preview a mainnet bridge with the same key. Append: ```ts import { Chain } from "@stablechain/sdk"; -const bridgeQuote = await stable.quoteBridge({ - fromChain: Chain.Sepolia, - toChain: Chain.StableTestnet, - fromToken: "0xc4DCC311c028e341fd8602D8eB89c5de94625927", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, +const stableMainnet = createStable({ + network: Network.Mainnet, + account, +}); + +const bridgeQuote = await stableMainnet.quoteBridge({ + fromChain: Chain.Ethereum, + toChain: Chain.Stable, + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT on Ethereum + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // USDT0 on Stable + amount: 100, }); console.log("Bridge quote:", bridgeQuote); ``` ```text -Bridge quote: { toAmount: 0.999812 } +Bridge quote: { toAmount: 99.73 } ``` -Pass the quote into `stable.bridge({ ...params, quote })` to execute. The SDK picks LayerZero for USDT0 → USDT0 routes and LI.FI for everything else. - -## 5. Quote a swap - -Swaps run on Stable through LI.FI. The quote returns the expected output and a pre-built transaction: - -```ts -const swapQuote = await stable.quoteSwap({ - fromToken: "0x8a2B28364102Bea189D99A475C494330Ef2bDD0B", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, - fromDecimals: 6, -}); +Pass the quote into `stableMainnet.bridge({ ...params, quote })` to execute. Routing goes through LI.FI, which picks the bridge route; the SDK handles approval and chain switching. -console.log("You'll receive:", swapQuote.toAmount, "USDT0"); -``` +## 5. Swaps -```text -You'll receive: 0.998 USDT0 -``` +Same-chain swaps follow the same pattern: `stable.quoteSwap` returns the expected output and a pre-built transaction, and `stable.swap({ ...params, quote })` executes it with ERC-20 approval handled internally. -Call `stable.swap({ ...params, quote: swapQuote })` to execute. Approval for ERC-20 sources is handled internally. +:::note +Swap routes depend on LI.FI's DEX coverage on Stable, which is still limited. When no route exists for a pair, `quoteSwap` and `swap` throw `StableQuoteError` with "No available quotes for the requested transfer". See [`quoteSwap` in the SDK reference](/en/reference/sdk#quoteswapparams) for the full API. +::: ## Next recommended - [**SDK reference**](/en/reference/sdk): Every parameter, return type, and error class. +- [**Earn yield with the SDK**](/en/how-to/earn-yield): Deposit USDT0 into a mainnet vault, check your APY and position, and withdraw. - [**Use with viem**](/en/how-to/sdk-with-viem): Switch between private-key, browser-wallet, and pre-built `WalletClient` signing. - [**Use with wagmi**](/en/how-to/sdk-with-wagmi): Wire the SDK into a React app using wagmi hooks. diff --git a/docs/pages/ko/explanation/sdk-overview.mdx b/docs/pages/ko/explanation/sdk-overview.mdx index 78aaa4b..626faa4 100644 --- a/docs/pages/ko/explanation/sdk-overview.mdx +++ b/docs/pages/ko/explanation/sdk-overview.mdx @@ -1,14 +1,14 @@ --- source_path: explanation/sdk-overview.mdx -source_sha: efcb1b7d8aaf0782573a67c4964ac01904c220ae +source_sha: de079c165ba2efc991a66b7134e00a6e9b5073f4 title: "Stable SDK" -description: "몇 줄의 코드로 Stable에서 USDT0 전송, 체인 간 브릿지, 토큰 스왑을 위해 타입이 지정된 TypeScript SDK를 사용하세요." +description: "몇 줄의 코드로 Stable에서 USDT0 전송, 체인 간 브릿지, 토큰 스왑, 볼트 수익 창출을 위해 타입이 지정된 TypeScript SDK를 사용하세요." diataxis: "explanation" --- # Stable SDK -`@stablechain/sdk`는 Stable용 공식 TypeScript 클라이언트입니다. viem을 래핑하여 가장 많이 사용하는 작업(USDT0 전송, 체인 간 브릿지, Stable에서 토큰 스왑)을 위한 작고 타입이 지정된 API를 제공합니다. 라우팅, 승인, 소수점 처리 및 체인 전환은 자동으로 처리됩니다. +`@stablechain/sdk`는 Stable의 공식 TypeScript 클라이언트입니다. 이 클라이언트는 viem을 래핑하며, USDT0 전송, 체인 간 브릿지, 토큰 스왑, Stable에서 수익 창출과 같이 가장 많이 사용하는 작업에 대해 작고 타입이 지정된 API를 제공합니다. 라우팅, 승인, 소수점 자릿수 및 체인 전환은 자동으로 처리됩니다. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -30,31 +30,34 @@ const { txHash } = await stable.transfer({ txHash: 0x8f3a...2d41 ``` -## SDK가 하는 일 +## SDK의 기능 -- **`transfer`**: 기본 USDT0 또는 Stable의 모든 ERC-20을 전송합니다. 가스는 USDT0으로 자동 지불됩니다. -- **`quoteBridge` / `bridge`**: 크로스체인 전송. USDT0 → USDT0의 경우 LayerZero를, 다른 모든 것에는 LI.FI를 사용합니다. 경로는 자동으로 선택됩니다. -- **`quoteSwap` / `swap`**: LI.FI를 통한 동일 체인 토큰 스왑으로, ERC-20 승인은 내부적으로 처리됩니다. +- **`transfer`**: 기본 USDT0 또는 Stable의 모든 ERC-20을 전송합니다. 가스비는 USDT0으로 자동으로 지불됩니다. +- **`quoteBridge` / `bridge`**: LI.FI를 통해 크로스체인 전송을 수행하며, LI.FI가 브릿지 경로를 선택합니다. 승인 및 체인 전환은 내부적으로 처리됩니다. +- **`quoteSwap` / `swap`**: LI.FI를 통해 동일 체인 토큰 스왑을 수행하며, ERC-20 승인은 내부적으로 처리됩니다. +- **`earn`**: Morpho V2 볼트에 USDT0을 공급하여 수익을 얻고, 출금 또는 상환하며, Merkl 인센티브 보상을 청구합니다. 승인 및 어댑터 할당 해제는 자동으로 처리됩니다. +- **`createStableReader`**: 볼트 APY, 포지션, 예상 수익 및 즉시 인출 가능성 확인을 위한 읽기 전용 클라이언트입니다. 서명자가 필요하지 않습니다. -SDK는 [`@stablechain/sdk`](https://www.npmjs.com/package/@stablechain/sdk)로 npm에 게시되어 있으며 피어 종속성으로 `viem >= 2.0.0`이 필요합니다. +SDK는 [`@stablechain/sdk`](https://www.npmjs.com/package/@stablechain/sdk)라는 이름으로 npm에 게시되어 있으며 피어 의존성으로 `viem >= 2.0.0`을 필요로 합니다. -## 언제 사용해야 할까요 (그리고 언제 사용하지 말아야 할까요) +## 언제 사용해야 하는가 (그리고 언제 사용하지 말아야 하는가) -라우팅 및 승인 상용구를 숨기는 타입이 지정되고 의견이 명확한 클라이언트를 원할 때 SDK를 사용하세요. 트랜잭션 구성, 사용자 지정 가스 전략 또는 전송/브릿지/스왑 외의 컨트랙트 호출에 대한 직접적인 제어가 필요한 경우 원시 viem 또는 ethers로 전환하세요. +라우팅 및 승인 상용구를 숨기는 타입이 지정되고 견고한 클라이언트를 원할 때 SDK를 사용하세요. 트랜잭션 구성, 맞춤형 가스 전략 또는 전송/브릿지/스왑 외부의 컨트랙트 호출에 대한 직접적인 제어가 필요할 때는 원시 viem 또는 ethers로 전환하세요. :::note -SDK는 모든 viem 호환 서명자로 서명합니다: 개인 키 `Account`, `custom(window.ethereum)`과 같은 브라우저 `Transport`, 또는 미리 빌드된 `WalletClient` (예: wagmi의 `useWalletClient`가 반환하는 것). +SDK는 모든 viem 호환 서명자로 서명합니다: 개인 키 `Account`, `custom(window.ethereum)`과 같은 브라우저 `Transport`, 또는 미리 구성된 `WalletClient` (예: wagmi의 `useWalletClient`가 반환하는 클라이언트). ::: -## 시작하기 +## 여기에서 시작하기 - [**빠른 시작**](/ko/tutorial/sdk-quickstart): SDK를 설치하고 테스트넷에서 첫 번째 전송, 브릿지 및 스왑을 실행합니다. -- [**SDK 참조**](/ko/reference/sdk): 모든 메서드, 구성 옵션, 열거형 및 오류 클래스. +- [**SDK로 수익 창출**](/ko/how-to/earn-yield): 볼트에 예치하고, APY와 포지션을 확인하고, 인출하고, 보상을 청구합니다. +- [**SDK 레퍼런스**](/ko/reference/sdk): 모든 메서드, 구성 옵션, 열거형 및 오류 클래스. - [**viem과 함께 사용하기**](/ko/how-to/sdk-with-viem): 서버 측 계정, 브라우저 지갑 및 자체 `WalletClient` 가져오기. -- [**wagmi와 함께 사용하기**](/ko/how-to/sdk-with-wagmi): `useWalletClient` 및 훅을 사용하여 React 앱에 SDK를 연결합니다. +- [**wagmi와 함께 사용하기**](/ko/how-to/sdk-with-wagmi): `useWalletClient` 및 훅을 사용하여 SDK를 React 앱에 연결합니다. -## 다음 권장 사항 +## 다음에 권장하는 항목 - [**npm에서 설치**](https://www.npmjs.com/package/@stablechain/sdk): npmjs.com에서 패키지를 보고 최신 버전을 확인합니다. -- [**Stable에 연결**](/ko/reference/connect): 메인넷 및 테스트넷의 체인 ID, RPC 엔드포인트 및 탐색기. -- [**테스트넷 지갑에 자금 조달**](/ko/how-to/use-faucet): 빠른 시작을 실행하기 전에 수도꼭지에서 테스트넷 USDT0을 받으세요. +- [**Stable에 연결**](/ko/reference/connect): 메인넷 및 테스트넷의 체인 ID, RPC 엔드포인트 및 익스플로러. +- [**테스트넷 지갑에 자금 조달**](/ko/how-to/use-faucet): 빠른 시작을 실행하기 전에 수도꼭지에서 테스트넷 USDT0을 얻습니다. diff --git a/docs/pages/ko/how-to/earn-yield.mdx b/docs/pages/ko/how-to/earn-yield.mdx new file mode 100644 index 0000000..34d250e --- /dev/null +++ b/docs/pages/ko/how-to/earn-yield.mdx @@ -0,0 +1,178 @@ +--- +source_path: how-to/earn-yield.mdx +source_sha: 03f36a00122cecc73b90075f5f9276cfd84121ee +title: "SDK로 수익 창출하기" +description: "Stable SDK로 USDT0를 Morpho 볼트에 예치하고, APY와 포지션을 확인하고, 인출하고, Merkl 보상을 청구하세요." +diataxis: "how-to" +--- + +# SDK로 수익 창출하기 + +Stable 메인넷의 Morpho V2 볼트에 USDT0를 공급하여 수익을 얻고, 포지션을 읽고 인출하세요. `@stablechain/sdk`의 `earn` 메서드는 ERC-20 승인, 예치, 인출 시 어댑터 할당 해제를 처리하므로, 각 작업당 하나의 메서드를 호출합니다. + +두 클라이언트가 관련됩니다. `createStable`은 예치 및 인출을 위한 서명 클라이언트를 제공합니다. `createStableReader`는 APY, 포지션, 인출 가능성을 위한 읽기 전용 클라이언트를 제공하며, 서명자는 필요하지 않습니다. + +## 전제 조건 + +- Node.js 20 이상, 그리고 `@stablechain/sdk`와 `viem` 설치. [SDK Quickstart](/ko/tutorial/sdk-quickstart)를 참조하세요. +- Stable 메인넷에 USDT0를 가진 서명자. 자금을 이동하려면 [`stable.bridge`](/ko/reference/sdk#bridgeparams) 또는 [지원되는 브릿지](/ko/reference/bridges) 중 하나를 사용하세요. +- 볼트 주소. SDK는 기본 메인넷 볼트를 `STABLE_VAULT_ADDRESS`로 내보냅니다. + +:::note +수익 창출 예시는 메인넷에서 실행됩니다. SDK는 테스트넷 볼트 주소를 제공하지 않으므로, `Network.Testnet`은 직접 배포한 볼트에서만 작동합니다. +::: + +## 1. 볼트가 있는 서명 클라이언트 생성 + +`earn: { vault }`을 `createStable`에 전달하세요. 이것이 없으면, deposit 및 withdraw 메서드가 `StableValidationError`를 발생시킵니다. + +```ts +import { createStable, Network, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +import { privateKeyToAccount } from "viem/accounts"; + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); + +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); +``` + +```text +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } +``` + +## 2. 예치 전에 APY 확인하기 + +읽기 전용 리더를 생성하고 볼트의 현재 순 APY를 가져옵니다. 리더는 읽으려는 주소와 볼트가 필요하지만, 서명자는 필요하지 않습니다. + +```ts +import { createStableReader } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: account.address, + earn: { vault: STABLE_VAULT_ADDRESS }, +}); + +const { apy, native, rewards } = await reader.earn.getYield(); +console.log("Net APY:", (apy * 100).toFixed(2) + "%"); +``` + +```text +Net APY: 5.80% +``` + +`apy`는 보상 부스트를 포함한 수수료 후 총 순 APY입니다. `native`는 그 자체의 기본 시장 수익률이며, `rewards`는 각 보상 토큰의 APR 기여도를 분석합니다. 모든 값은 소수이므로, `0.058`은 5.8%입니다. + +시간이 지남에 따라 얼마를 얻을지 추정하려면 `preview`를 사용하세요: + +```ts +const { projectedYield } = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +console.log("30-day projection on 1000:", projectedYield.toFixed(2)); +``` + +```text +30-day projection on 1000: 4.64 +``` + +## 3. 예치 + +볼트에 자산을 공급하세요. SDK는 승인(또는 지갑이 지원하는 경우 허가 서명)을 보낸 다음, 예치를 보내고 영수증을 기다립니다. + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +console.log("Deposit:", txHash); +``` + +```text +Deposit: 0x8f3a...2d41 +``` + +금액은 사람이 읽을 수 있는 형식입니다. 기본 자산의 소수점 이하는 `tokenDecimals`를 전달하지 않으면 온체인에서 가져옵니다. 단일 예치금이 볼트의 유휴 유동성을 소비하는 양을 제한하려면 `depositBuffer` (예: 80%의 경우 `0.8`)를 전달하세요. 금액이 한도를 초과하면 SDK는 보내기 전에 오류를 발생시킵니다. + +:::tip +재시도를 안전하게 하기 위해 `idempotencyKey`를 전달하세요. 동일한 키를 가진 호출이 이미 진행 중인 경우, SDK는 두 번째 트랜잭션을 보내는 대신 동일한 Promise를 반환합니다. +::: + +## 4. 포지션 확인 + +공유 잔액과 현재 자산 가치를 가져옵니다. + +```ts +const position = await reader.earn.position(); +console.log("Shares:", position.sharesFormatted, "worth", position.assets); +``` + +```text +Shares: 100 worth 100.02 +``` + +`shares`는 원시 잔액이고 `sharesFormatted`는 사람이 읽을 수 있는 값입니다. `assets`는 현재 해당 공유가 기본 단위로 얼마의 가치가 있는지 나타냅니다. + +## 5. 인출 또는 상환 + +특정 금액의 기본 자산을 인출하거나 일정 수의 공유를 상환합니다. 볼트의 유휴 유동성이 부족할 때, SDK는 차이를 충당하기 위해 볼트의 어댑터에서 자동으로 할당 해제합니다. + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +console.log("Withdraw:", txHash); +``` + +```text +Withdraw: 0xabcd...7890 +``` + +대신 공유 수로 상환하려면 `stable.earn.redeem({ shares: 25 })`를 사용하세요. 금액이 즉시 인출될 수 있는지 먼저 확인하려면 리더를 호출하세요: + +```ts +const { isInstant, availableLiquidity } = await reader.earn.withdrawability({ amount: 50 }); +console.log("Instant:", isInstant, "available:", availableLiquidity); +``` + +```text +Instant: true available: 12500.5 +``` + +`isInstant`가 `false`인 경우에도 인출은 성공합니다. 단지 어댑터 할당 해제가 필요하며, 이는 `withdraw` 및 `redeem`이 자동으로 처리합니다. 어떤 어댑터에서 인출할지 수동으로 제어하려면 [`forceWithdraw` / `forceRedeem`](/ko/reference/sdk#forcewithdrawparams--forceredeemparams)을 사용하세요. + +## 6. 인센티브 보상 청구 + +볼트는 기본 수익 외에 Merkl 보상 토큰을 얻을 수 있습니다. 보류 중인 보상을 가져온 다음 청구합니다. 이 메서드에는 서명자가 필요하지만 `earn` 볼트 구성은 필요하지 않습니다. + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +if (rewards.length > 0) { + const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); + console.log("Claimed", tokenCount, "token(s):", txHash); +} +``` + +```text +Claimed 1 token(s): 0xabcd...7890 +``` + +:::note +Merkl은 약 두 시간마다 보상을 머클 트리로 커밋합니다. 업데이트가 보류 중인 보상은 아직 청구할 수 없으며, 청구할 항목이 없을 때 `claim()`을 호출하면 `StableValidationError`가 발생합니다. 브라우저에서 Merkl API의 CORS 제한을 피하려면 `merklApiBase`를 자체 서버의 프록시로 설정하세요. +::: + +## SDK에서 서명하지 않으셨나요? + +다른 곳에서 트랜잭션을 서명하는 경우(렐레이어, 가스 면제 흐름 또는 다중 서명), 보내지 않고 캘데이터를 빌드합니다: + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`steps`는 선택적 승인과 예치로 구성된 정렬된 트랜잭션 목록입니다. `prepareWithdrawCalldata` 및 `prepareRedeemCalldata`는 각각 단일 `{ to, data, value, chainId }` 트랜잭션을 반환합니다. + +## 다음으로 이동할 곳 + +- [**SDK 참조**](/ko/reference/sdk#stableearn): 모든 수익 매개변수, 반환 유형 및 리더 메서드의 전체 내용. +- [**wagmi와 함께 사용하기**](/ko/how-to/sdk-with-wagmi): 사용자가 브라우저 지갑에서 예치할 수 있도록 SDK를 React 앱에 연결합니다. +- [**SDK 개요**](/ko/explanation/sdk-overview): SDK를 언제 사용해야 하는지, 그리고 서명 모드가 작동하는 방식. diff --git a/docs/pages/ko/how-to/sdk-with-viem.mdx b/docs/pages/ko/how-to/sdk-with-viem.mdx index a5131fa..8b52bec 100644 --- a/docs/pages/ko/how-to/sdk-with-viem.mdx +++ b/docs/pages/ko/how-to/sdk-with-viem.mdx @@ -1,20 +1,20 @@ --- source_path: how-to/sdk-with-viem.mdx -source_sha: 48be50a291feef6843b9074ab105a81c375d5845 +source_sha: 7e53a41e2fc3a48b2ad38135b67d4d980e43e492 title: "viem과 함께 SDK 사용하기" -description: "@stablechain/sdk로 세 가지 방법으로 서명할 수 있습니다: 프라이빗 키 계정, 브라우저 트랜스포트, 또는 미리 빌드된 viem WalletClient." +description: "@stablechain/sdk로 세 가지 방법으로 서명하기: 개인 키 계정, 브라우저 트랜스포트, 또는 미리 빌드된 viem WalletClient." diataxis: "how-to" --- # viem과 함께 SDK 사용하기 -`@stablechain/sdk`는 viem 위에 구축됩니다. `createStable`은 세 가지 서명 모드를 허용하며, 코드가 실행되는 위치에 따라 하나를 선택합니다: 프라이빗 키를 이용한 서버 측, 사용자 지갑을 이용한 브라우저 측, 또는 이미 구성한 `WalletClient`(예: wagmi 앱)를 이용한 방식입니다. +`@stablechain/sdk`는 viem 위에 구축되었습니다. `createStable`은 세 가지 서명 모드를 허용하며, 코드가 실행되는 위치(서버 측에서 개인 키 사용, 브라우저 측에서 사용자 지갑 사용 또는 이미 구성한 `WalletClient` 사용)에 따라 하나를 선택합니다. -이 가이드는 각 모드를 처음부터 끝까지 보여줍니다. +이 가이드에서는 각 모드를 처음부터 끝까지 보여줍니다. -## 서버 측: 프라이빗 키 `Account` +## 서버 측: 개인 키 `Account` -viem의 `privateKeyToAccount`를 사용하여 백엔드에서 보유한 프라이빗 키로 서명합니다. +viem의 `privateKeyToAccount`를 사용하여 백엔드에 저장된 개인 키로 서명합니다. ```ts import "dotenv/config"; @@ -41,9 +41,9 @@ console.log(txHash); 0x8f3a...2d41 ``` -## 브라우저 측: 지갑에서 가져온 `Transport` +## 브라우저 측: 지갑의 `Transport` -`custom(window.ethereum)` (또는 모든 EIP-1193 프로바이더)를 `transport`로 전달합니다. SDK는 `WalletClient`를 구축하고 프로바이더에서 서명자 주소를 읽습니다. +`custom(window.ethereum)` (또는 EIP-1193 제공자)을 `transport`로 전달합니다. SDK는 트랜스포트에서 `WalletClient`를 구축하고 `transfer`에 전달한 `from` 주소로 서명합니다. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -68,12 +68,16 @@ const { txHash } = await stable.transfer({ ``` :::warning -`transfer`, `bridge`, 및 `swap`은 지갑을 올바른 네트워크에 연결하기 위해 `switchChain`을 호출합니다. 사용자가 거절하면 SDK는 `StableTransactionError`를 `phase: "switch_chain"`과 함께 발생시킵니다. 이를 포착하고 사용자에게 재시도를 표시해야 합니다. +트랜스포트 모드는 명시적인 `from`을 사용하는 `transfer`만 다룹니다. `swap`, `quoteSwap`, `bridge` 및 `earn` 메서드는 `account` 또는 `walletClient`에서 서명자 주소를 읽고 둘 다 설정되지 않은 경우 `StableValidationError`를 발생시킵니다. 이 경우, 동일한 제공자로부터 `WalletClient`를 대신 구축하십시오(다음 섹션). ::: -## 자체 `WalletClient` 가져오기 +:::warning +`transfer`, `bridge`, `swap`은 지갑을 올바른 네트워크로 전환하기 위해 `switchChain`을 호출합니다. 사용자가 거부하면 SDK는 `StableTransactionError`를 `phase: "switch_chain"`과 함께 발생시킵니다. 이를 포착하고 사용자에게 재시도를 표시하십시오. +::: + +## 자신만의 `WalletClient` 가져오기 -_이미_ `WalletClient`를 가지고 있는 경우(예: wagmi 또는 사용자 지정 서명자에서 가져온 경우) 직접 전달합니다. 이는 `account` 및 `transport`보다 우선합니다. +이미 `WalletClient`가 있는 경우(예: wagmi 또는 사용자 지정 서명자), 이를 직접 전달합니다. 이는 `account` 및 `transport`보다 우선합니다. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -103,12 +107,12 @@ const { txHash } = await stable.transfer({ from, to: "0xRecipient", amount: 5 }) | **모드** | **사용 시점** | | :--- | :--- | -| `account` | 백엔드 서비스, 스크립트, 에이전트, 키를 보유하고 있는 곳 어디든. | -| `transport` | MetaMask 또는 wagmi가 없는 사용자 지정 흐름으로 사용자가 서명하는 브라우저 앱. | -| `walletClient` | 이미 구성된 `WalletClient`가 있는 경우 (wagmi, RainbowKit, ConnectKit). | +| `account` | 백엔드 서비스, 스크립트, 에이전트, 키를 보유하고 있는 모든 곳. | +| `transport` | 명시적인 `from`을 사용하여 `transfer`만 호출하는 브라우저 앱. | +| `walletClient` | 이미 구성된 `WalletClient`가 있는 경우(wagmi, RainbowKit, ConnectKit). | ## 다음 권장 사항 -- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): wagmi Hooks를 통해 SDK를 React 앱에 연결합니다. -- [**SDK 참조**](/ko/reference/sdk): 모든 구성 필드, 메서드, ENUM, 오류 클래스. -- [**SDK 빠른 시작**](/ko/tutorial/sdk-quickstart): 테스트넷에서 첫 번째 전송, 브릿지, 스왑을 실행합니다. +- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): wagmi 훅을 통해 SDK를 React 앱에 연결합니다. +- [**SDK 참조**](/ko/reference/sdk): 모든 구성 필드, 메서드, 열거형 및 오류 클래스. +- [**SDK 빠른 시작**](/ko/tutorial/sdk-quickstart): 테스트넷에서 첫 번째 전송, 브릿지 및 스왑을 실행합니다. diff --git a/docs/pages/ko/how-to/sdk-with-wagmi.mdx b/docs/pages/ko/how-to/sdk-with-wagmi.mdx index 9036fad..baf440e 100644 --- a/docs/pages/ko/how-to/sdk-with-wagmi.mdx +++ b/docs/pages/ko/how-to/sdk-with-wagmi.mdx @@ -1,6 +1,6 @@ --- source_path: how-to/sdk-with-wagmi.mdx -source_sha: 396745c5590db9fb3f8b0677d67b69579aeeb55a +source_sha: 2c4c2bcb4189c3ae47884b82bf8433ee0a18ae5e title: "wagmi와 함께 SDK 사용하기" description: "wagmi의 useWalletClient, useAccount, useChainId 훅을 사용하여 @stablechain/sdk를 React 앱에 연결합니다." diataxis: "how-to" @@ -8,13 +8,13 @@ diataxis: "how-to" # wagmi와 함께 SDK 사용하기 -`createStable`은 wagmi의 `useWalletClient`가 반환하는 것과 정확히 같은 viem `WalletClient`를 받습니다. 평소처럼 wagmi를 통해 지갑을 연결한 다음, 지갑 클라이언트가 변경될 때마다 `StableClient`를 메모이제이션합니다. +`createStable`은 viem `WalletClient`를 허용하며, 이는 wagmi의 `useWalletClient`가 반환하는 것과 정확히 같습니다. 평소와 같이 wagmi를 통해 지갑을 연결한 다음, 지갑 클라이언트가 변경될 때마다 `StableClient`를 메모화합니다. -이 가이드는 wagmi v2와 `@tanstack/react-query`를 가정합니다. +이 가이드는 wagmi v2 및 `@tanstack/react-query`를 가정합니다. -## 1. wagmi 구성하기 +## 1. wagmi 구성 -Stable을 wagmi 구성에 추가합니다. viem은 두 네트워크에 대한 체인 정의를 제공합니다. +wagmi 구성에 Stable을 추가합니다. viem은 두 네트워크에 대한 체인 정의를 제공합니다. ```ts import { http, createConfig } from "wagmi"; @@ -35,9 +35,9 @@ export const wagmiConfig = createConfig({ WagmiConfig { chains: [988, 2201], connectors: [injected] } ``` -## 2. `StableClient`를 반환하는 훅 빌드하기 +## 2. `StableClient`를 반환하는 훅 빌드 -현재 `WalletClient`에 대해 `StableClient`를 메모이제이션합니다. 지갑 클라이언트 아이덴티티가 변경될 때마다 다시 생성합니다. +현재 `WalletClient`에 대해 `StableClient`를 메모화합니다. 지갑 클라이언트 ID가 변경될 때 재생성합니다. ```tsx import { useMemo } from "react"; @@ -55,7 +55,7 @@ export function useStable(network: Network = Network.Mainnet): StableClient | nu ``` :::warning -`useWalletClient()`는 사용자가 연결하기 전에는 `undefined`를 반환합니다. SDK 메서드를 호출하기 전에 항상 보호 장치를 두어야 합니다. 그렇지 않으면 구조 분해된 `walletClient`가 거짓이 되고 `createStable`에 서명자가 없게 됩니다. +사용자가 연결하기 전에 `useWalletClient()`는 `undefined`를 반환합니다. SDK 메서드를 호출하기 전에 항상 보호 장치를 설정해야 합니다. 그렇지 않으면 구조 분해된 `walletClient`는 falsy가 되고 `createStable`은 서명자를 가지지 못합니다. ::: ## 3. 컴포넌트에서 사용하기 @@ -94,7 +94,7 @@ Sent: 0x8f3a...2d41 ## 4. React에서 브릿지 및 스왑하기 -동일한 `stable` 인스턴스가 브릿지 및 스왑을 처리합니다. effect 또는 `useQuery`에서 견적을 가져온 다음 클릭 시 실행합니다. +동일한 `stable` 인스턴스가 브릿지 및 스왑을 처리합니다. 이펙트 또는 `useQuery`에서 견적을 가져온 다음 클릭 시 실행합니다. `StableQuoteError`를 catch합니다. Stable에 대한 LI.FI의 DEX 범위는 여전히 제한적이며, 경로가 없는 쌍은 "No available quotes for the requested transfer"를 발생시킵니다. ```tsx const stable = useStable(Network.Mainnet); @@ -123,11 +123,11 @@ const onSwap = async () => { ``` :::note -`useQuery`로 견적을 캐싱하는 것은 잘 작동합니다. `quoteSwap` / `quoteBridge`를 쿼리 함수로 전달하고 캐시된 `quote`를 `swap` / `bridge`에 전달합니다. SDK는 견적이 제공되면 내부 견적 호출을 건너뜁니다. +`useQuery`로 견적을 캐시하는 것이 잘 작동합니다. `quoteSwap` / `quoteBridge`를 쿼리 함수로 전달하고 캐시된 `quote`를 `swap` / `bridge`로 전달합니다. SDK는 제공된 견적이 있을 때 내부 견적 호출을 건너뜁니다. ::: ## 다음 권장 사항 - [**SDK 참조**](/ko/reference/sdk): 모든 메서드, 구성 필드 및 오류 클래스. - [**viem과 함께 사용하기**](/ko/how-to/sdk-with-viem): 세 가지 서명 모드를 나란히 비교합니다. -- [**SDK 퀵스타트**](/ko/tutorial/sdk-quickstart): 테스트넷에서 첫 번째 전송, 브릿지 및 스왑을 실행합니다. +- [**SDK 퀵스타트**](/ko/tutorial/sdk-quickstart): 테스트넷에서 첫 전송, 브릿지 및 스왑을 실행합니다. diff --git a/docs/pages/ko/reference/sdk.mdx b/docs/pages/ko/reference/sdk.mdx index 1b9bcd6..b1e74d6 100644 --- a/docs/pages/ko/reference/sdk.mdx +++ b/docs/pages/ko/reference/sdk.mdx @@ -1,14 +1,14 @@ --- source_path: reference/sdk.mdx -source_sha: 64d437cfd1ea8f5c39da592e6dfad6bb6ab8b3bf +source_sha: cb2522d5ba3ff23ce6506a1bdc182e119b247b59 title: "SDK 레퍼런스" -description: "@stablechain/sdk에 대한 완전한 레퍼런스: createStable, StableClient 메서드, 열거형, 체인 구성, 오류 클래스." +description: "@stablechain/sdk의 완전한 레퍼런스: createStable, createStableReader, transfer, bridge, swap, earn 및 yield 메서드, 열거형, 오류 클래스." diataxis: "reference" --- # SDK 레퍼런스 -`@stablechain/sdk`의 전체 표면. 자세한 내용은 [SDK 퀵스타트](/ko/tutorial/sdk-quickstart)를 참조하십시오. +`@stablechain/sdk`의 전체 표면입니다. 여기에는 [`createStable`](#createstableconfig)의 서명 클라이언트, [`createStableReader`](#createstablereaderconfig)의 읽기 전용 클라이언트, 공유되는 열거형, 상수, 오류 클래스가 포함됩니다. 자세한 내용은 [SDK 퀵스타트](/ko/tutorial/sdk-quickstart)를 참조하십시오. 작업 중심의 수익 가이드에 대해서는 [SDK로 수익 얻기](/ko/how-to/earn-yield)를 참조하십시오. ## 설치 @@ -24,7 +24,7 @@ added 2 packages, audited 3 packages in 2s ## `createStable(config)` -`StableClient`를 구성합니다. [`StableClient`](#stableclient) 아래에 나열된 메서드를 포함하는 객체를 반환합니다. +`StableClient`를 구성합니다. [`StableClient`](#stableclient)에 나열된 메서드를 포함하는 객체를 반환합니다. ```ts import { createStable, Network } from "@stablechain/sdk"; @@ -33,7 +33,7 @@ const stable = createStable({ network: Network.Mainnet, account }); ``` ```text -StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } +StableClient { transfer, quoteBridge, bridge, quoteSwap, swap, earn } ``` ### `StableConfig` @@ -42,17 +42,19 @@ StableClient { transfer, quoteBridge, bridge, quoteSwap, swap } | :--- | :--- | :--- | :--- | | `network` | `Network` | `Network.Mainnet` | 대상 네트워크. | | `rpc` | `string` | `network`의 공개 RPC | RPC 재정의. | -| `account` | `viem.Account` | | 서버 측 서명자(예: `privateKeyToAccount`). | -| `transport` | `viem.Transport` | | 브라우저-지갑 전송(예: `custom(window.ethereum)`). | -| `walletClient` | `viem.WalletClient` | | 미리 빌드된 지갑 클라이언트. `account` 및 `transport`보다 우선합니다. | +| `account` | `viem.Account` | | 서버 측 서명자(예: `privateKeyToAccount`). | +| `transport` | `viem.Transport` | | 브라우저-지갑 전송(예: `custom(window.ethereum)`). | +| `walletClient` | `viem.WalletClient` | | 미리 빌드된 지갑 클라이언트. `account` 및 `transport`보다 우선합니다. | +| `earn` | `{ vault: string }` | | Morpho V2 볼트 구성. `stable.earn` 입금, 출금, 상환 및 calldata 준비 메서드를 호출하는 데 필요합니다. | +| `merklApiBase` | `string` | `https://api.merkl.xyz` | Merkl 보상 API 기본 URL. 자체 서버를 통해 프록시하여 브라우저 CORS 제한을 피하려면 재정의하세요. | -`account`, `transport` 또는 `walletClient` 중 하나를 제공하십시오. +`account`, `transport`, `walletClient` 중 하나를 제공합니다. `earn` 필드는 선택 사항입니다. 이 필드 없이도 transfer, bridge, swap이 작동합니다. 볼트 입금 및 출금에는 이 필드가 필요하며, 구성된 볼트 없이 호출하면 SDK는 `StableValidationError`를 발생시킵니다. ## `StableClient` ### `transfer(params)` -기본 USDT0 또는 Stable의 ERC-20을 보냅니다. 지갑을 Stable 체인으로 전환하고, 누락된 경우 온체인에서 토큰 소수 자릿수를 가져오고, 영수증을 기다립니다. +네이티브 USDT0 또는 Stable의 모든 ERC-20을 보냅니다. 지갑을 Stable 체인으로 전환하고, 토큰 소수 자릿수가 누락된 경우 온체인에서 가져오며, 영수증을 기다립니다. ```ts const { txHash } = await stable.transfer({ @@ -70,35 +72,39 @@ const { txHash } = await stable.transfer({ | :--- | :--- | :--- | | `from` | `string` | 발신자 주소. | | `to` | `string` | 수신자 주소. | -| `amount` | `number` | 읽기 쉬운 양. | -| `token` | `string?` | ERC-20 컨트랙트 주소. 기본 USDT0의 경우 생략합니다. | +| `amount` | `number` | 읽기 쉬운 금액. | +| `token` | `string?` | ERC-20 컨트랙트 주소. 네이티브 USDT0의 경우 생략. | | `tokenDecimals` | `number?` | 소수 자릿수. 생략 시 온체인에서 가져옵니다. | `OperationResult` (`{ txHash, toAmount? }`)를 반환합니다. ### `quoteBridge(params)` -브릿지를 미리 봅니다. 읽기 전용. 서명, 가스 없음. +브리지를 미리 봅니다. 읽기 전용. 서명 없음, 가스 없음. ```ts const quote = await stable.quoteBridge({ fromChain: Chain.Ethereum, toChain: Chain.Stable, - fromToken: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee", - toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // 이더리움의 USDT + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // Stable의 USDT0 amount: 100, }); ``` ```text -{ toAmount: 99.94 } +{ toAmount: 99.73 } ``` `BridgeQuote`를 반환합니다. +:::note +LI.FI는 [`Chain`](#chain) 열거형의 메인넷 체인에 대해서만 견적을 제공합니다. `Chain.Sepolia` 및 `Chain.StableTestnet`은 `StableQuoteError`와 함께 거부되며, `CHAIN_CONFIGS`의 일부 토큰 주소(예: 이더리움의 USDT0)는 LI.FI의 견적 거부 목록에 있습니다. 확실하지 않을 경우 체인의 정식 토큰으로 견적을 요청하십시오. +::: + ### `bridge(params)` -토큰을 크로스체인으로 브릿지합니다. SDK는 경로를 선택합니다: USDT0 → USDT0의 경우 LayerZero, 나머지 모든 것의 경우 LI.FI. 내부 인용 호출을 건너뛰려면 미리 가져온 `quote`를 전달하십시오. +LI.FI를 통해 토큰을 크로스체인 브리징하며, LI.FI가 브리지 경로를 선택합니다. SDK는 ERC-20 승인을 처리하고 전송 전에 지갑을 소스 체인으로 전환합니다. 미리 가져온 `quote`를 전달하여 내부 견적 호출을 건너뛸 수 있습니다. ```ts const { txHash } = await stable.bridge({ ...bridgeParams, quote }); @@ -114,14 +120,14 @@ const { txHash } = await stable.bridge({ ...bridgeParams, quote }); | `toChain` | `Chain` | 대상 체인. | | `fromToken` | `string` | 소스 토큰 컨트랙트 주소. | | `toToken` | `string` | 대상 토큰 컨트랙트 주소. | -| `amount` | `number` | 읽기 쉬운 양. | +| `amount` | `number` | 읽기 쉬운 금액. | | `fromDecimals` | `number?` | 소스 토큰 소수 자릿수. 기본값은 `6`입니다. | -| `recipient` | `string?` | 대상 주소. 기본값은 서명자입니다. | -| `quote` | `BridgeQuote?` | 미리 가져온 인용. 내부 인용 호출을 건너뜁니다. | +| `recipient` | `string?` | 대상 주소. 서명자로 기본 설정됩니다. | +| `quote` | `BridgeQuote?` | 미리 가져온 견적. 내부 견적 호출을 건너뜁니다. | ### `quoteSwap(params)` -Stable에서 LI.FI 스왑 인용을 가져옵니다. 미리 빌드된 트랜잭션 요청 및 승인 주소를 반환합니다. +Stable에서 LI.FI 스왑 견적을 가져옵니다. 미리 빌드된 트랜잭션 요청과 승인 주소를 반환합니다. ```ts const quote = await stable.quoteSwap({ @@ -136,6 +142,10 @@ const quote = await stable.quoteSwap({ { toAmount: 99.81, fromAmount: 100000000n, fromToken: "0x8a2B...", approvalAddress: "0x...", transactionRequest: { ... } } ``` +:::note +스왑 경로는 Stable의 LI.FI DEX 적용 범위에 따라 달라지며, 이는 아직 제한적입니다. 특정 페어에 대한 경로가 존재하지 않는 경우, `quoteSwap` 및 `swap`은 "요청된 전송에 대한 사용 가능한 견적이 없습니다"라는 메시지와 함께 `StableQuoteError`를 발생시킵니다. +::: + ### `swap(params)` LI.FI를 통해 Stable에서 토큰을 스왑합니다. 필요한 경우 ERC-20 승인을 자동으로 처리하고 지갑의 체인을 전환합니다. @@ -150,12 +160,277 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); | **매개변수** | **유형** | **기본값** | **설명** | | :--- | :--- | :--- | :--- | -| `fromToken` | `string` | | 소스 토큰 주소. | -| `toToken` | `string` | | 대상 토큰 주소. | -| `amount` | `number` | | 읽기 쉬운 양. | +| `fromToken` | `string` | | 소스 토큰 주소. | +| `toToken` | `string` | | 대상 토큰 주소. | +| `amount` | `number` | | 읽기 쉬운 금액. | | `fromDecimals` | `number?` | `6` | 소스 토큰 소수 자릿수. | -| `toAddress` | `string?` | signer | 수신자 주소. | -| `quote` | `SwapQuote?` | | 미리 가져온 인용. LI.FI 호출을 건너뜁니다. | +| `toAddress` | `string?` | 서명자 | 수신자 주소. | +| `quote` | `SwapQuote?` | | 미리 가져온 견적. LI.FI 호출을 건너뜁니다. | + +## `stable.earn` + +수익을 얻기 위해 USDT0을 Morpho V2 볼트에 공급하고 Merkl 인센티브 보상을 청구합니다. 모든 메서드는 `earn` 네임스페이스 아래에 있으며 서명자가 필요합니다. 입금, 출금, 상환 및 calldata 준비 메서드에는 [`StableConfig`](#stableconfig)에 `earn: { vault }`도 필요합니다. + +```ts +const stable = createStable({ + network: Network.Mainnet, + account, + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +stable.earn { deposit, bulkDeposit, withdraw, bulkWithdraw, redeem, forceWithdraw, forceRedeem, prepareDepositCalldata, prepareWithdrawCalldata, prepareRedeemCalldata, incentiveRewards } +``` + +금액 및 주식 수는 사람이 읽을 수 있는 숫자입니다. 자산 및 주식 소수 자릿수가 생략되면 온체인에서 가져옵니다. 입금, 출금, 상환 및 강제 메서드는 `EarnResult` (`{ txHash, idempotencyKey? }`)를 반환하며, 해결되기 전에 영수증을 기다립니다. 일괄 메서드는 `BulkEarnResult`를 반환하고 `incentiveRewards.claim()`은 아래 섹션에서 설명된 `MerklClaimResult`를 반환합니다. + +### `deposit(params)` + +자산을 볼트에 공급합니다. ERC-20 승인(또는 지갑이 지원하는 경우 허가 서명) 및 입금을 한 번의 호출로 처리합니다. + +```ts +const { txHash } = await stable.earn.deposit({ amount: 100 }); +``` + +```text +{ txHash: "0x8f3a...2d41" } +``` + +| **매개변수** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | 예치할 기본 자산, 사람이 읽을 수 있는 형식. | +| `vault` | `string?` | config vault | 볼트 주소 재정의. 여러 볼트의 `bulkDeposit`에서 항목당 필요합니다. | +| `tokenDecimals` | `number?` | 온체인 | 기본 자산 소수점. | +| `slippageTolerance` | `number?` | `0.0003` | 슬리피지(소수점 형식, 예: `0.003` = 0.3%). Morpho의 0.03%로 기본 설정됩니다. | +| `depositBuffer` | `number?` | | 이 예금이 사용할 수 있는 볼트 유휴 유동성의 최대 비율(예: `0.8` = 80%). `> 0` 및 `≤ 1`이어야 합니다. 초과하면 전송 전에 오류가 발생합니다. | +| `idempotencyKey` | `string?` | | 중복 제거 키. 진행 중인 키로 두 번째 호출을 하면 동일한 Promise가 반환됩니다. | + +### `withdraw(params)` + +주어진 금액의 기본 자산을 인출합니다. 유휴 볼트 유동성이 부족할 경우 SDK는 차액을 충당하기 위해 볼트 어댑터에서 자동으로 할당을 해제합니다. + +```ts +const { txHash } = await stable.earn.withdraw({ amount: 50 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **매개변수** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | | 출금할 기본 자산, 사람이 읽을 수 있는 형식. | +| `tokenDecimals` | `number?` | 온체인 | 기본 자산 소수점. | +| `idempotencyKey` | `string?` | | 중복 제거 키. | + +### `redeem(params)` + +주어진 수의 볼트 주식을 기본 자산으로 상환합니다. `withdraw`와 마찬가지로 유휴 유동성이 부족할 경우 어댑터에서 자동으로 할당을 해제합니다. + +```ts +const { txHash } = await stable.earn.redeem({ shares: 25 }); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +| **매개변수** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `shares` | `number` | | 상환할 볼트 주식, 사람이 읽을 수 있는 형식. | +| `shareDecimals` | `number?` | 온체인 | 볼트 주식 소수점. | +| `idempotencyKey` | `string?` | | 중복 제거 키. | + +### `bulkDeposit(items, options?)` / `bulkWithdraw(items, options?)` + +여러 입금 또는 출금을 순차적으로 실행합니다. 기본적으로 배치는 모든 항목을 시도하고 부분 결과를 반환합니다. `stopOnError: true`를 설정하면 첫 번째 실패 시 중지하고 `StableBulkError`를 발생시킵니다. + +```ts +const result = await stable.earn.bulkDeposit([ + { amount: 100 }, + { amount: 250, vault: "0xAnotherVault" }, +]); +``` + +```text +{ results: [ { status: "fulfilled", txHash: "0x..." }, ... ], succeeded: 2, failed: 0 } +``` + +`items`는 `EarnDepositParams` (또는 `EarnWithdrawParams`)의 배열입니다. `options`에는 다음이 허용됩니다. + +| **옵션** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `idempotencyKey` | `string?` | | 배치 수준 중복 제거 키. | +| `stopOnError` | `boolean?` | `false` | 부분 결과를 반환하는 대신 첫 번째 실패 시 중지하고 `StableBulkError`를 발생시킵니다. | + +`BulkEarnResult` (`{ results, succeeded, failed, idempotencyKey? }`)를 반환합니다. `results`의 각 항목은 `{ status: "fulfilled", txHash, vault?, idempotencyKey? }` 또는 `{ status: "rejected", error, vault?, idempotencyKey? }`입니다. + +### `forceWithdraw(params)` / `forceRedeem(params)` + +어떤 어댑터 포지션부터 할당 해제할지 명시적으로 제어하면서 인출 또는 상환합니다. `withdraw` / `redeem`의 자동 선택 대신 직접 소스 시장을 선택해야 할 때 사용합니다. + +```ts +const { txHash } = await stable.earn.forceWithdraw({ + amount: 1000, + deallocations: [{ adapter: "0xAdapter", amount: 1000 }], +}); +``` + +```text +{ txHash: "0xabcd...7890" } +``` + +둘 다 `deallocations: ForceDeallocation[]` 배열과 선택적 `tokenDecimals`(할당 해제 금액에 사용되는 기본 자산 소수점; 생략 시 온체인에서 가져옴)를 받습니다. `forceWithdraw`는 `amount`를 받고, `forceRedeem`은 `shares`와 선택적인 `shareDecimals`를 받습니다. 각 `ForceDeallocation`은 다음과 같습니다. + +| **필드** | **유형** | **설명** | +| :--- | :--- | :--- | +| `adapter` | `string` | 할당 해제할 어댑터 컨트랙트. | +| `amount` | `number` | 기본 자산 단위로 할당 해제할 금액. | +| `marketParams` | `object?` | Morpho Blue 시장 어댑터에만 필요: `{ loanToken, collateralToken, oracle, irm, lltv }` (`lltv`는 1e18로 스케일링됨). 볼트 어댑터의 경우 생략합니다. | + +### `prepareDepositCalldata(params)` / `prepareWithdrawCalldata(params)` / `prepareRedeemCalldata(params)` + +서명하거나 전송하지 않고 트랜잭션 calldata를 빌드합니다. 이를 사용하여 릴레이어, 가스 면제 흐름 또는 멀티시그를 통해 트랜잭션을 라우팅하십시오. + +```ts +const { steps, chainId } = await stable.earn.prepareDepositCalldata({ amount: 100 }); +``` + +```text +{ steps: [ { to: "0x...", data: "0x...", value: 0n } ], chainId: 988 } +``` + +`prepareDepositCalldata`는 `EarnDepositCalldata` (`{ steps, chainId }`)를 반환하며, 여기서 `steps`는 `{ to, data, value }` 트랜잭션의 정렬된 목록입니다. 선택적 토큰 승인 후 입금이 이어집니다. `prepareWithdrawCalldata` 및 `prepareRedeemCalldata`는 `EarnWithdrawCalldata` (`{ to, data, value, chainId }`)라는 단일 트랜잭션을 반환합니다. 이들은 해당 서명 메서드와 동일한 매개변수를 허용합니다. + +### `incentiveRewards` + +서명자에 대한 Merkl 보상 토큰을 가져와 청구합니다. 이 메서드에는 서명자가 필요하지만 `earn` 볼트 구성은 필요하지 않습니다. + +```ts +const { rewards } = await stable.earn.incentiveRewards.fetch(); +const { txHash, tokenCount } = await stable.earn.incentiveRewards.claim(); +``` + +```text +{ txHash: "0xabcd...7890", tokenCount: 1 } +``` + +`fetch()`는 `MerklRewardsResult` (`{ chainId, rewards: MerklReward[] }`)를 반환하며, 토큰당 순 청구 가능 금액을 포함합니다. `claim()`은 현재 Merkl Merkle 트리에 커밋된 모든 보상을 청구하고 `MerklClaimResult` (`{ txHash, tokenCount }`)를 반환합니다. + +:::note +Merkl은 약 2시간마다 Merkle 트리에 보상을 커밋합니다. 해당 업데이트가 아직 보류 중인 보상은 아직 청구할 수 없습니다. 청구할 수 없는 상태에서 `claim()`을 호출하면 `StableValidationError`가 발생합니다. +::: + +## `createStableReader(config)` + +볼트 수익률 및 포지션 데이터를 위한 읽기 전용 `StableReader`를 구성합니다. 서명자는 필요 없고, 읽으려는 주소만 필요합니다. + +```ts +import { createStableReader, Network } from "@stablechain/sdk"; + +const reader = createStableReader({ + network: Network.Mainnet, + address: "0xUserAddress", + earn: { vault: "0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08" }, +}); +``` + +```text +StableReader { earn: { getYield, position, preview, withdrawability, incentiveRewards } } +``` + +### `StableReaderConfig` + +| **필드** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `address` | `string` | | 포지션과 보상을 읽을 사용자 주소. 필수. | +| `earn` | `{ vault: string }` | | 읽을 볼트. 필수: 누락 시 `createStableReader`는 `StableValidationError`를 발생시킵니다. | +| `network` | `Network?` | `Network.Mainnet` | 대상 네트워크. | +| `rpc` | `string?` | 공개 RPC | RPC 재정의. | +| `merklApiBase` | `string?` | `https://api.merkl.xyz` | Merkl 보상 API 기본 URL. | + +## `StableReader` + +### `getYield()` + +볼트의 현재 순 APY 및 수수료 분석을 반환합니다. + +```ts +const vaultYield = await reader.earn.getYield(); +``` + +```text +{ apy: 0.058, native: 0.041, rewards: [ { symbol: "MORPHO", address: "0x...", apr: 0.017 } ], performanceFee: 0.1, managementFee: 0 } +``` + +`VaultYield`를 반환합니다. `apy`는 보상 부스트를 포함한 수수료 후 총 순 APY이며, `native`는 보상을 제외한 기본 시장 수익률입니다. 모든 값은 소수점(예: `0.05` = 5%)입니다. + +### `position()` + +설정된 주소의 현재 볼트 포지션을 반환합니다. + +```ts +const position = await reader.earn.position(); +``` + +```text +{ shares: 100000000n, sharesFormatted: 100, shareDecimals: 6, assets: 101.2, tokenDecimals: 6, assetAddress: "0x..." } +``` + +원시 `shares` 잔액, 포맷된 `sharesFormatted` 사람의 값, 기본 단위로 표현된 `assets` 값을 포함하는 `VaultPosition`을 반환합니다. + +### `preview(params)` + +볼트의 실시간 순 APY를 사용하여 특정 금액에 대한 수익률을 일정 기간 동안 예측합니다. + +```ts +const preview = await reader.earn.preview({ amount: 1000, horizonDays: 30 }); +``` + +```text +{ projectedYield: 4.64, apy: 0.058, horizonDays: 30 } +``` + +| **매개변수** | **유형** | **설명** | +| :--- | :--- | :--- | +| `amount` | `number` | 예측할 원금, 사람이 읽을 수 있는 형식. | +| `horizonDays` | `number` | 예측 기간(일). | + +`YieldPreview` (`{ projectedYield, apy, horizonDays }`)를 반환합니다. + +### `withdrawability(params)` + +볼트의 유휴 유동성에서 특정 금액을 즉시 인출할 수 있는지 확인합니다. + +```ts +const status = await reader.earn.withdrawability({ amount: 5000 }); +``` + +```text +{ isInstant: true, availableLiquidity: 12500.5, tokenDecimals: 6 } +``` + +| **매개변수** | **유형** | **기본값** | **설명** | +| :--- | :--- | :--- | :--- | +| `amount` | `number` | 테스트할 금액, 사람이 읽을 수 있는 형식. | +| `tokenDecimals` | `number?` | 온체인 | 기본 자산 소수점. | + +`WithdrawStatus` (`{ isInstant, availableLiquidity, tokenDecimals }`)를 반환합니다. `isInstant`가 `false`인 경우, 인출에는 어댑터 할당 해제가 필요하며, 이는 `stable.earn.withdraw`가 자동으로 처리합니다. + +### `incentiveRewards.fetch()` + +설정된 주소에 대한 보류 중인 Merkl 보상을 읽습니다. `stable.earn.incentiveRewards.fetch`의 읽기 전용 대응책입니다. + +```ts +const { rewards } = await reader.earn.incentiveRewards.fetch(); +``` + +```text +{ chainId: 988, rewards: [ { token: { symbol: "MORPHO", ... }, amount: 1.25, rawAmount: "1250000...", proofs: [...] } ] } +``` + +`MerklRewardsResult`를 반환합니다. ## 열거형 @@ -168,25 +443,25 @@ const { txHash, toAmount } = await stable.swap({ ...swapParams, quote }); ### `Chain` -`quoteBridge` 및 `bridge`에서 사용됩니다. 각 항목에는 해당 `CHAIN_CONFIGS` 항목이 있습니다. +`quoteBridge` 및 `bridge`에서 사용됩니다. 각 항목에는 해당 `CHAIN_CONFIGS` 항목이 있습니다. 두 개의 테스트넷 항목은 완전성을 위해 정의되었지만 LI.FI는 이를 거부합니다. 브리징은 메인넷 체인 간에만 작동합니다. | **열거형** | **네트워크** | **체인 ID** | | :--- | :--- | :--- | -| `Chain.Sepolia` | Ethereum Sepolia | 11155111 | -| `Chain.StableTestnet` | Stable Testnet | 2201 | -| `Chain.Stable` | Stable Mainnet | 988 | -| `Chain.Ethereum` | Ethereum | 1 | -| `Chain.Arbitrum` | Arbitrum One | 42161 | -| `Chain.Ink` | Ink | 57073 | -| `Chain.Bera` | Berachain | 80094 | -| `Chain.MegaETH` | MegaETH | 4326 | -| `Chain.Base` | Base | 8453 | +| `Chain.Sepolia` | 이더리움 세폴리아 | 11155111 | +| `Chain.StableTestnet` | Stable 테스트넷 | 2201 | +| `Chain.Stable` | Stable 메인넷 | 988 | +| `Chain.Ethereum` | 이더리움 | 1 | +| `Chain.Arbitrum` | 아비트럼 원 | 42161 | +| `Chain.Ink` | 잉크 | 57073 | +| `Chain.Bera` | 베라체인 | 80094 | +| `Chain.MegaETH` | 메가EVM | 4326 | +| `Chain.Base` | 베이스 | 8453 | | `Chain.BSC` | BNB Smart Chain | 56 | -| `Chain.HyperEVM` | HyperEVM | 999 | +| `Chain.HyperEVM` | 하이퍼EVM | 999 | ## `CHAIN_CONFIGS` -`Chain` 열거형으로 키가 지정된 `Partial>`. 각 항목은 `id`, `rpc`, `usdt` 및 `decimals`를 노출합니다. 하드코딩하지 않고 지원되는 체인에서 표준 USDT 주소가 필요할 때 사용하십시오. +`Chain` 열거형으로 키가 지정된 `Partial>`입니다. 각 항목은 `id`, `rpc`, `usdt`, `decimals`를 노출합니다. 지원되는 체인의 정식 USDT 주소가 필요하지만 하드코딩하고 싶지 않을 때 사용하십시오. ```ts import { CHAIN_CONFIGS, Chain } from "@stablechain/sdk"; @@ -198,16 +473,35 @@ console.log(CHAIN_CONFIGS[Chain.Stable]); { id: 988, rpc: "https://rpc.stable.xyz", usdt: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", decimals: 6 } ``` +## 상수 + +하드코딩을 피하기 위해 내보낸 정식 메인넷 주소. + +```ts +import { STABLE_USDT_ADDRESS, STABLE_VAULT_ADDRESS } from "@stablechain/sdk"; +``` + +```text +STABLE_USDT_ADDRESS 0x779ded0c9e1022225f8e0630b35a9b54be713736 +STABLE_VAULT_ADDRESS 0xb7Df8db22A5DBBFA9ebeb94b3910aec6a4f05c08 +``` + +| **상수** | **설명** | +| :--- | :--- | +| `STABLE_USDT_ADDRESS` | Stable 메인넷의 USDT 토큰 주소입니다. | +| `STABLE_VAULT_ADDRESS` | Stable 메인넷의 기본 Morpho V2 수익 볼트입니다. `earn: { vault }`에 전달합니다. | + ## 오류 -모든 SDK 오류는 viem의 `BaseError`를 확장하는 `StableError`를 확장합니다. 오류는 구조화된 메타데이터를 전달하므로 `error.name` 또는 `instanceof`를 기준으로 분기할 수 있습니다. +모든 SDK 오류는 viem의 `BaseError`를 확장하는 `StableError`를 확장합니다. 오류는 구조화된 메타데이터를 포함하므로 `error.name` 또는 `instanceof`에 따라 분기할 수 있습니다. | **클래스** | **발생 시점** | **유용한 필드** | | :--- | :--- | :--- | -| `StableValidationError` | 매개변수가 유효성 검사에 실패함(잘못된 주소, 유한하지 않은 금액, 지원되지 않는 체인). | `field`, `value` | -| `StableQuoteError` | LI.FI에 대한 견적 요청이 실패함. | `provider`, `httpStatus`, `providerCode`, `body` | -| `StableTransactionError` | 온체인 단계가 실패함: 체인 전환, 승인, 전송 또는 되돌리기. | `phase`, `txHash`, `chainId`, `revertReason` | -| `StableNetworkError` | 기본 HTTP/RPC 호출이 실패함. | `url` | +| `StableValidationError` | 매개변수 유효성 검사 실패(잘못된 주소, 비유한 금액, 지원되지 않는 체인). | `field`, `value` | +| `StableQuoteError` | LI.FI에 대한 견적 요청 실패. | `provider`, `httpStatus`, `providerCode`, `body` | +| `StableTransactionError` | 온체인 단계 실패: 체인 전환, 승인, 전송 또는 되돌리기. | `phase`, `txHash`, `chainId`, `revertReason` | +| `StableNetworkError` | 기본 HTTP/RPC 호출 실패(예: Morpho 또는 Merkl API). | `url` | +| `StableBulkError` | `stopOnError: true`로 실행된 `bulkDeposit` 또는 `bulkWithdraw`가 첫 번째 실패에 도달. | `results`, `succeeded`, `failed` | ```ts import { StableTransactionError } from "@stablechain/sdk"; @@ -216,7 +510,7 @@ try { await stable.transfer({ from, to, amount: 1 }); } catch (err) { if (err instanceof StableTransactionError && err.phase === "switch_chain") { - // 사용자가 체인 전환을 거부했습니다. + // 사용자가 체인 전환을 거부함 } throw err; } @@ -231,5 +525,5 @@ StableTransactionError: transfer: wallet rejected or failed to switch to chain 9 ## 다음 권장 사항 - [**SDK 퀵스타트**](/ko/tutorial/sdk-quickstart): SDK를 설치하고 테스트넷에서 첫 번째 전송을 실행합니다. -- [**viem과 함께 사용**](/ko/how-to/sdk-with-viem): 개인 키, 브라우저 지갑 및 미리 빌드된 서명자 간에 전환합니다. -- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): 훅을 사용하여 SDK를 React 앱에 연결합니다. +- [**viem과 함께 사용**](/ko/how-to/sdk-with-viem): 비공개 키, 브라우저 지갑 및 미리 빌드된 서명자 간 전환. +- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): 훅스를 사용하여 React 앱에 SDK를 연결합니다. diff --git a/docs/pages/ko/tutorial/sdk-quickstart.mdx b/docs/pages/ko/tutorial/sdk-quickstart.mdx index a2927e5..5463d04 100644 --- a/docs/pages/ko/tutorial/sdk-quickstart.mdx +++ b/docs/pages/ko/tutorial/sdk-quickstart.mdx @@ -1,36 +1,39 @@ --- source_path: tutorial/sdk-quickstart.mdx -source_sha: 24bdfc4340389e06e1cb06ee6a7f3e1939940c97 +source_sha: c918339737f4e49e3639b4e61c16abf0e1a9376e title: "SDK 퀵스타트" -description: "Stable SDK를 설치하고, 테스트넷에서 첫 USDT0 전송을 보내고, 브릿지 및 스왑 견적을 미리 봅니다." +description: "Stable SDK를 설치하고, 테스트넷에서 첫 USDT0 트랜스퍼를 전송하고, 메인넷 브릿지 견적을 미리 봅니다." diataxis: "tutorial" --- # SDK 퀵스타트 -`@stablechain/sdk`를 설치하고, 프라이빗 키로 서명된 클라이언트를 생성하고, Stable 테스트넷에서 USDT0 전송을 보내고, 브릿지 및 스왑 견적을 가져옵니다. 총 소요 시간: 약 5분. +`@stablechain/sdk`를 설치하고, 개인 키로 서명된 클라이언트를 생성하고, Stable 테스트넷에서 USDT0 트랜스퍼를 전송하고, 메인넷 브릿지 견적을 가져옵니다. 총 소요 시간: 약 5분. :::note -Stable은 가스 토큰으로 USDT0을 사용합니다. 거래를 위해서는 테스트넷 USDT0만 있으면 됩니다. 자금을 조달할 별도의 기본 자산은 없습니다. +Stable은 USDT0를 가스 토큰으로 사용합니다. 거래를 위해서는 테스트넷 USDT0만 있으면 됩니다. 자금을 조달할 별도의 네이티브 자산은 없습니다. ::: ## 전제 조건 - Node.js 20 이상 -- 테스트넷 USDT0이 있는 테스트 프라이빗 키. [테스트넷 지갑에 자금 조달](/ko/how-to/use-faucet) 참조. +- 테스트넷 USDT0가 있는 테스트 개인 키. [테스트넷 지갑에 자금 조달](/ko/how-to/use-faucet)을 참조하십시오. ## 1. 설치 ```bash mkdir stable-sdk-quickstart && cd stable-sdk-quickstart -npm init -y && npm install @stablechain/sdk viem +npm init -y && npm pkg set type=module +npm install @stablechain/sdk viem dotenv ``` ```text -added 2 packages, audited 3 packages in 2s +added 3 packages, audited 4 packages in 2s ``` -테스트 키를 저장합니다. +`type=module`을 사용하면 아래 단계에서 최상위 `await`를 사용할 수 있습니다. `type=module`이 없으면 `tsx`가 스크립트를 거부합니다. + +테스트 키를 저장합니다: ```bash echo "PRIVATE_KEY=0xYOUR_TEST_KEY" > .env @@ -38,7 +41,7 @@ echo "PRIVATE_KEY=0xYOUR_TEST_KEY" > .env ## 2. 클라이언트 생성 -`index.ts` 생성: +`index.ts`를 생성합니다: ```ts import "dotenv/config"; @@ -59,11 +62,11 @@ console.log("Signer:", account.address); Signer: 0xYourAddress ``` -`createStable`은 세 가지 서명 모드를 허용합니다: `account` (서버 측, 위 참조), `transport` (`custom(window.ethereum)`을 통한 브라우저 지갑), 또는 `walletClient` (사전 구축된 viem `WalletClient`). 세 가지 모두에 대해서는 [viem과 함께 SDK 사용](/ko/how-to/sdk-with-viem)을 참조하세요. +`createStable`은 세 가지 서명 모드를 허용합니다: `account`(서버 측, 위에 표시됨), `transport`(`custom(window.ethereum)`을 통한 브라우저 지갑), 또는 `walletClient`(미리 구축된 viem `WalletClient`). 세 가지 모두에 대한 자세한 내용은 [viem과 함께 SDK 사용](/ko/how-to/sdk-with-viem)을 참조하십시오. ## 3. USDT0 전송 보내기 -`index.ts`에 추가: +`index.ts`에 추가합니다: ```ts const { txHash } = await stable.transfer({ @@ -75,7 +78,7 @@ const { txHash } = await stable.transfer({ console.log("Transfer:", txHash); ``` -실행: +실행합니다: ```bash npx tsx index.ts @@ -86,55 +89,48 @@ Signer: 0xYourAddress Transfer: 0x8f3a...2d41 ``` -[테스트넷 탐색기](https://testnet.stablescan.xyz)에서 해시를 열어 확인합니다. +[테스트넷 익스플로러](https://testnet.stablescan.xyz)에서 해시를 열어 확인합니다. ## 4. 브릿지 견적 -이더리움 Sepolia에서 Stable 테스트넷으로 USDT0을 브릿지합니다. `quoteBridge`는 서명도 가스도 없는 읽기 전용 호출입니다. +브릿지 및 스왑 라우팅은 Stable 메인넷을 지원하지만 테스트넷은 지원하지 않는 LI.FI를 통해 이루어집니다. `quoteBridge`는 읽기 전용 호출이므로 (서명, 가스, 자금 필요 없음) 동일한 키로 메인넷 브릿지 견적을 미리 볼 수 있습니다. 추가합니다: ```ts import { Chain } from "@stablechain/sdk"; -const bridgeQuote = await stable.quoteBridge({ - fromChain: Chain.Sepolia, - toChain: Chain.StableTestnet, - fromToken: "0xc4DCC311c028e341fd8602D8eB89c5de94625927", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, +const stableMainnet = createStable({ + network: Network.Mainnet, + account, +}); + +const bridgeQuote = await stableMainnet.quoteBridge({ + fromChain: Chain.Ethereum, + toChain: Chain.Stable, + fromToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", // Ethereum의 USDT + toToken: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736", // Stable의 USDT0 + amount: 100, }); console.log("Bridge quote:", bridgeQuote); ``` ```text -Bridge quote: { toAmount: 0.999812 } +Bridge quote: { toAmount: 99.73 } ``` -견적을 `stable.bridge({ ...params, quote })`에 전달하여 실행합니다. SDK는 USDT0 → USDT0 경로에 LayerZero를, 그 외 모든 것에 LI.FI를 사용합니다. - -## 5. 스왑 견적 - -스왑은 LI.FI를 통해 Stable에서 실행됩니다. 견적은 예상 출력과 미리 구축된 트랜잭션을 반환합니다. - -```ts -const swapQuote = await stable.quoteSwap({ - fromToken: "0x8a2B28364102Bea189D99A475C494330Ef2bDD0B", - toToken: "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9", - amount: 1, - fromDecimals: 6, -}); +견적을 `stableMainnet.bridge({ ...params, quote })`에 전달하여 실행합니다. 라우팅은 LI.FI를 통해 이루어지며, LI.FI는 브릿지 경로를 선택합니다. SDK는 승인 및 체인 전환을 처리합니다. -console.log("You'll receive:", swapQuote.toAmount, "USDT0"); -``` +## 5. 스왑 -```text -You'll receive: 0.998 USDT0 -``` +동일 체인 스왑은 동일한 패턴을 따릅니다: `stable.quoteSwap`은 예상 출력 및 미리 구축된 트랜잭션을 반환하고, `stable.swap({ ...params, quote })`는 ERC-20 승인을 내부적으로 처리하여 실행합니다. -`stable.swap({ ...params, quote: swapQuote })`를 호출하여 실행합니다. ERC-20 소스에 대한 승인은 내부적으로 처리됩니다. +:::note +스왑 경로는 Stable에서 LI.FI의 DEX 커버리지에 따라 달라지며, 이는 아직 제한적입니다. 두 쌍에 대해 경로가 없는 경우 `quoteSwap` 및 `swap`은 "요청된 전송에 대한 사용 가능한 견적이 없습니다."라는 `StableQuoteError`를 발생시킵니다. 전체 API는 [SDK 참조의 `quoteSwap`](/ko/reference/sdk#quoteswapparams)을 참조하십시오. +::: ## 다음 권장 사항 - [**SDK 참조**](/ko/reference/sdk): 모든 매개변수, 반환 유형 및 오류 클래스. -- [**viem과 함께 사용**](/ko/how-to/sdk-with-viem): 프라이빗 키, 브라우저 지갑 및 미리 구축된 `WalletClient` 서명 간에 전환. -- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): wagmi 훅을 사용하여 SDK를 React 앱에 연결. +- [**SDK로 수익 창출**](/ko/how-to/earn-yield): 메인넷 볼트에 USDT0를 예치하고, APY 및 포지션을 확인하고, 인출합니다. +- [**viem과 함께 사용**](/ko/how-to/sdk-with-viem): 개인 키, 브라우저 지갑 및 미리 구축된 `WalletClient` 서명 간에 전환합니다. +- [**wagmi와 함께 사용**](/ko/how-to/sdk-with-wagmi): wagmi 훅을 사용하여 React 앱에 SDK를 연결합니다. diff --git a/docs/sidebar.json b/docs/sidebar.json index 99de7e2..f5a7a18 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -194,6 +194,10 @@ "text": "Sdk", "link": "/en/reference/sdk" }, + { + "text": "Earn Yield", + "link": "/en/how-to/earn-yield" + }, { "text": "Sdk With Viem", "link": "/en/how-to/sdk-with-viem" @@ -687,11 +691,11 @@ "link": "/ko/explanation/key-features" }, { - "text": "Ethereum 비교", + "text": "이더리움 비교", "link": "/ko/explanation/ethereum-comparison" }, { - "text": "Ethereum 호환성", + "text": "이더리움 호환성", "link": "/ko/explanation/ethereum-compatibility" }, { @@ -729,7 +733,7 @@ "link": "/ko/explanation/usdt0-bridging" }, { - "text": "브리지 보안", + "text": "브릿지 보안", "link": "/ko/explanation/bridge-security" }, { @@ -763,7 +767,7 @@ "collapsed": true, "items": [ { - "text": "핵심 최적화 개요", + "text": "코어 최적화 개요", "link": "/ko/explanation/core-optimization-overview" }, { @@ -783,7 +787,7 @@ "link": "/ko/explanation/high-performance-rpc" }, { - "text": "Autobahn", + "text": "아우토반", "link": "/ko/explanation/autobahn" } ] @@ -793,19 +797,19 @@ "collapsed": true, "items": [ { - "text": "사용 사례: 결제", + "text": "결제 사용 사례", "link": "/ko/explanation/use-case-payments" }, { - "text": "사용 사례: 급여", + "text": "급여 사용 사례", "link": "/ko/explanation/use-case-payroll" }, { - "text": "사용 사례: 스폰서", + "text": "스폰서 사용 사례", "link": "/ko/explanation/use-case-sponsored" }, { - "text": "사용 사례: 프라이빗", + "text": "개인 사용 사례", "link": "/ko/explanation/use-case-private" } ] @@ -853,11 +857,15 @@ "link": "/ko/reference/sdk" }, { - "text": "viem과 함께 SDK", + "text": "수익 창출", + "link": "/ko/how-to/earn-yield" + }, + { + "text": "viem과 함께하는 SDK", "link": "/ko/how-to/sdk-with-viem" }, { - "text": "wagmi와 함께 SDK", + "text": "wagmi와 함께하는 SDK", "link": "/ko/how-to/sdk-with-wagmi" } ] @@ -913,17 +921,17 @@ "link": "/ko/how-to/zero-gas-transactions" }, { - "text": "USDT 가스 활용", + "text": "USDT 가스 작업", "link": "/ko/how-to/work-with-usdt-gas" }, { - "text": "USDT0 브리지", + "text": "USDT0 브릿지", "link": "/ko/tutorial/bridge-usdt0" } ] }, { - "text": "사용자 청구", + "text": "사용자에게 청구", "collapsed": true, "items": [ { @@ -931,11 +939,11 @@ "link": "/ko/how-to/build-p2p-payments" }, { - "text": "구독 및 수금", + "text": "구독 및 수집", "link": "/ko/how-to/subscribe-and-collect" }, { - "text": "인보이스로 결제", + "text": "송장으로 결제", "link": "/ko/how-to/pay-with-invoice" } ] @@ -961,11 +969,11 @@ "link": "/ko/reference/subscriptions" }, { - "text": "인보이스", + "text": "송장", "link": "/ko/reference/invoices" }, { - "text": "호출당 결제", + "text": "호출당 지불", "link": "/ko/reference/pay-per-call" }, { @@ -977,31 +985,31 @@ ] }, { - "text": "컨트랙트 배포", + "text": "계약 배포", "collapsed": true, "items": [ { - "text": "컨트랙트 개요", + "text": "계약 개요", "link": "/ko/explanation/contracts-overview" }, { - "text": "컨트랙트 색인", + "text": "계약 색인", "link": "/ko/explanation/contracts-guides" }, { - "text": "첫 컨트랙트 배포", + "text": "첫 번째 계약 배포", "collapsed": true, "items": [ { - "text": "스마트 컨트랙트", + "text": "스마트 계약", "link": "/ko/tutorial/smart-contract" }, { - "text": "컨트랙트 검증", + "text": "계약 확인", "link": "/ko/how-to/verify-contract" }, { - "text": "컨트랙트 색인화", + "text": "색인 계약", "link": "/ko/how-to/index-contract" } ] @@ -1023,27 +1031,27 @@ "link": "/ko/reference/system-modules-api-overview" }, { - "text": "Bank 모듈", + "text": "뱅크 모듈", "link": "/ko/explanation/bank-module" }, { - "text": "Bank 모듈 API", + "text": "뱅크 모듈 API", "link": "/ko/reference/bank-module-api" }, { - "text": "Distribution 모듈", + "text": "배포 모듈", "link": "/ko/explanation/distribution-module" }, { - "text": "Distribution 모듈 API", + "text": "배포 모듈 API", "link": "/ko/reference/distribution-module-api" }, { - "text": "Staking 모듈", + "text": "스테이킹 모듈", "link": "/ko/explanation/staking-module" }, { - "text": "Staking 모듈 API", + "text": "스테이킹 모듈 API", "link": "/ko/reference/staking-module-api" }, { @@ -1059,7 +1067,7 @@ "link": "/ko/how-to/track-unbonding" }, { - "text": "검증자 데이터 색인화", + "text": "유효성 검사기 데이터 색인", "link": "/ko/how-to/index-validator-data" } ] @@ -1093,33 +1101,33 @@ "collapsed": true, "items": [ { - "text": "Mainnet", + "text": "메인넷", "collapsed": true, "items": [ { - "text": "Mainnet 정보", + "text": "메인넷 정보", "link": "/ko/reference/mainnet-information" }, { - "text": "Mainnet 버전 기록", + "text": "메인넷 버전 기록", "link": "/ko/reference/mainnet-version-history" } ] }, { - "text": "Testnet", + "text": "테스트넷", "collapsed": true, "items": [ { - "text": "Testnet 정보", + "text": "테스트넷 정보", "link": "/ko/reference/testnet-information" }, { - "text": "Testnet 버전 기록", + "text": "테스트넷 버전 기록", "link": "/ko/reference/testnet-version-history" }, { - "text": "Testnet 생태계", + "text": "테스트넷 생태계", "link": "/ko/reference/testnet-ecosystem" } ] @@ -1153,11 +1161,11 @@ "collapsed": true, "items": [ { - "text": "브리지", + "text": "브릿지", "link": "/ko/reference/bridges" }, { - "text": "DEX", + "text": "덱스", "link": "/ko/reference/dexes" }, { @@ -1165,7 +1173,7 @@ "link": "/ko/reference/oracles" }, { - "text": "RPC 제공자", + "text": "RPC 제공업체", "link": "/ko/reference/rpc-providers" }, { @@ -1181,7 +1189,7 @@ "link": "/ko/reference/wallets" }, { - "text": "커스터디", + "text": "수호", "link": "/ko/reference/custody" }, { @@ -1191,7 +1199,7 @@ ] }, { - "text": "프로덕션 준비", + "text": "생산 준비", "link": "/ko/how-to/production-readiness" }, { @@ -1199,7 +1207,7 @@ "link": "/ko/reference/developer-assistance" }, { - "text": "리소스", + "text": "자료", "collapsed": true, "items": [ { @@ -1219,7 +1227,7 @@ "link": "/ko/reference/node-operations-overview" }, { - "text": "노드 시스템 요구사항", + "text": "노드 시스템 요구 사항", "link": "/ko/reference/node-system-requirements" }, { @@ -1235,7 +1243,7 @@ "link": "/ko/how-to/use-node-snapshots" }, { - "text": "검증자 실행", + "text": "유효성 검사기 실행", "link": "/ko/how-to/run-validator" }, { @@ -1253,7 +1261,7 @@ ] }, { - "text": "[Agents]", + "text": "[에이전트]", "collapsed": true, "items": [ { @@ -1265,11 +1273,11 @@ "link": "/ko/explanation/ai-agents-guides" }, { - "text": "x402 및 MPP로 결제", + "text": "x402 및 MPP를 통한 지불", "collapsed": true, "items": [ { - "text": "x402", + "text": "X402", "link": "/ko/explanation/x402" }, { @@ -1281,7 +1289,7 @@ "link": "/ko/explanation/mpp-sessions" }, { - "text": "호출당 결제 구축", + "text": "호출당 지불 구축", "link": "/ko/how-to/build-pay-per-call" }, { @@ -1303,11 +1311,11 @@ "link": "/ko/how-to/develop-with-ai" }, { - "text": "에이전트 퍼실리테이터", + "text": "에이전틱 촉진자", "link": "/ko/reference/agentic-facilitators" }, { - "text": "에이전트 지갑", + "text": "에이전틱 지갑", "link": "/ko/reference/agentic-wallets" } ] @@ -1341,15 +1349,15 @@ "link": "/cn/explanation/core-concepts" }, { - "text": "主要特性", + "text": "主要特点", "link": "/cn/explanation/key-features" }, { - "text": "Ethereum 对比", + "text": "以太坊对比", "link": "/cn/explanation/ethereum-comparison" }, { - "text": "Ethereum 兼容性", + "text": "以太坊兼容性", "link": "/cn/explanation/ethereum-compatibility" }, { @@ -1371,11 +1379,11 @@ ] }, { - "text": "USDT0 特性", + "text": "USDT0 功能", "collapsed": true, "items": [ { - "text": "USDT 特性概览", + "text": "USDT0 功能概览", "link": "/cn/explanation/usdt-features-overview" }, { @@ -1383,15 +1391,15 @@ "link": "/cn/explanation/flow-of-funds" }, { - "text": "USDT0 跨链", + "text": "USDT0 跨链桥接", "link": "/cn/explanation/usdt0-bridging" }, { - "text": "桥接安全", + "text": "桥接安全性", "link": "/cn/explanation/bridge-security" }, { - "text": "USDT 作为 Gas 代币", + "text": "将 USDT0 作为 Gas 代币", "link": "/cn/explanation/usdt-as-gas-token" }, { @@ -1403,11 +1411,11 @@ "link": "/cn/explanation/gas-waiver" }, { - "text": "保障区块空间", + "text": "保证区块空间", "link": "/cn/explanation/guaranteed-blockspace" }, { - "text": "USDT 转账聚合器", + "text": "USDT0 转账聚合器", "link": "/cn/explanation/usdt-transfer-aggregator" }, { @@ -1451,19 +1459,19 @@ "collapsed": true, "items": [ { - "text": "用例:支付", + "text": "支付用例", "link": "/cn/explanation/use-case-payments" }, { - "text": "用例:薪资", + "text": "薪资用例", "link": "/cn/explanation/use-case-payroll" }, { - "text": "用例:代付", + "text": "赞助用例", "link": "/cn/explanation/use-case-sponsored" }, { - "text": "用例:私密", + "text": "隐私用例", "link": "/cn/explanation/use-case-private" } ] @@ -1503,7 +1511,7 @@ "link": "/cn/explanation/sdk-overview" }, { - "text": "SDK 快速开始", + "text": "SDK 快速入门", "link": "/cn/tutorial/sdk-quickstart" }, { @@ -1511,17 +1519,21 @@ "link": "/cn/reference/sdk" }, { - "text": "搭配 viem 使用 SDK", + "text": "赚取收益", + "link": "/cn/how-to/earn-yield" + }, + { + "text": "带 viem 的 SDK", "link": "/cn/how-to/sdk-with-viem" }, { - "text": "搭配 wagmi 使用 SDK", + "text": "带 wagmi 的 SDK", "link": "/cn/how-to/sdk-with-wagmi" } ] }, { - "text": "用户接入", + "text": "引导用户", "collapsed": true, "items": [ { @@ -1547,7 +1559,7 @@ ] }, { - "text": "接受支付", + "text": "接受付款", "collapsed": true, "items": [ { @@ -1589,11 +1601,11 @@ "link": "/cn/how-to/build-p2p-payments" }, { - "text": "订阅与收款", + "text": "订阅和收款", "link": "/cn/how-to/subscribe-and-collect" }, { - "text": "发票支付", + "text": "凭发票支付", "link": "/cn/how-to/pay-with-invoice" } ] @@ -1647,7 +1659,7 @@ "link": "/cn/explanation/contracts-guides" }, { - "text": "部署你的首个合约", + "text": "部署第一个合约", "collapsed": true, "items": [ { @@ -1665,7 +1677,7 @@ ] }, { - "text": "接入系统模块", + "text": "利用系统模块", "collapsed": true, "items": [ { @@ -1681,27 +1693,27 @@ "link": "/cn/reference/system-modules-api-overview" }, { - "text": "Bank 模块", + "text": "银行模块", "link": "/cn/explanation/bank-module" }, { - "text": "Bank 模块 API", + "text": "银行模块 API", "link": "/cn/reference/bank-module-api" }, { - "text": "Distribution 模块", + "text": "分发模块", "link": "/cn/explanation/distribution-module" }, { - "text": "Distribution 模块 API", + "text": "分发模块 API", "link": "/cn/reference/distribution-module-api" }, { - "text": "Staking 模块", + "text": "质押模块", "link": "/cn/explanation/staking-module" }, { - "text": "Staking 模块 API", + "text": "质押模块 API", "link": "/cn/reference/staking-module-api" }, { @@ -1713,7 +1725,7 @@ "link": "/cn/reference/system-transactions-api" }, { - "text": "跟踪解绑", + "text": "追踪解除绑定", "link": "/cn/how-to/track-unbonding" }, { @@ -1777,7 +1789,7 @@ "link": "/cn/reference/testnet-version-history" }, { - "text": "测试网生态", + "text": "测试网生态系统", "link": "/cn/reference/testnet-ecosystem" } ] @@ -1811,7 +1823,7 @@ "collapsed": true, "items": [ { - "text": "跨链桥", + "text": "桥", "link": "/cn/reference/bridges" }, { @@ -1853,7 +1865,7 @@ "link": "/cn/how-to/production-readiness" }, { - "text": "开发者支持", + "text": "开发者协助", "link": "/cn/reference/developer-assistance" }, { @@ -1861,7 +1873,7 @@ "collapsed": true, "items": [ { - "text": "品牌资料包", + "text": "品牌工具包", "link": "/cn/resources/brand-kit" } ] @@ -1869,11 +1881,11 @@ ] }, { - "text": "运维", + "text": "操作", "collapsed": true, "items": [ { - "text": "节点运维概览", + "text": "节点操作概览", "link": "/cn/reference/node-operations-overview" }, { @@ -1905,21 +1917,21 @@ "link": "/cn/how-to/monitor-node" }, { - "text": "节点故障排查", + "text": "节点故障排除", "link": "/cn/how-to/troubleshoot-node" } ] }, { - "text": "[Agents]", + "text": "[代理]", "collapsed": true, "items": [ { - "text": "Agent 结算", + "text": "代理结算", "link": "/cn/explanation/agent-settlement" }, { - "text": "AI Agents 索引", + "text": "AI 代理索引", "link": "/cn/explanation/ai-agents-guides" }, { @@ -1927,7 +1939,7 @@ "collapsed": true, "items": [ { - "text": "x402", + "text": "X402", "link": "/cn/explanation/x402" }, { @@ -1947,25 +1959,25 @@ "link": "/cn/how-to/build-mpp-endpoint" }, { - "text": "通过 MCP 支付", + "text": "使用 MCP 支付", "link": "/cn/how-to/pay-with-mcp" } ] }, { - "text": "Agent 基础设施", + "text": "代理基础设施", "collapsed": true, "items": [ { - "text": "用 AI 开发", + "text": "使用 AI 开发", "link": "/cn/how-to/develop-with-ai" }, { - "text": "Agentic Facilitators", + "text": "代理协调器", "link": "/cn/reference/agentic-facilitators" }, { - "text": "Agentic 钱包", + "text": "代理钱包", "link": "/cn/reference/agentic-wallets" } ]