混合 SDK
混合协议的SDK类型。 前端,通过SIWE验证钱包.
安装
pnpm add @blend-money/fe
同伴依赖性 : viem
快速启动
import { BlendSdk, getPimlicoPaymasterEndpoints } from "@blend-money/fe";
const sdk = new BlendSdk({
publishableKey: "pk_live_...",
signMessage: walletClient.signMessage,
paymaster: getPimlicoPaymasterEndpoints("pim_..."),
});
// 1. Discover what's available
const chains = await sdk.discover.depositChains();
const tokens = await sdk.discover.depositTokens(8453);
// 2. Sign in with SIWE
const session = await sdk.signIn({ address: "0x...", chainId: 8453 });
// 3. Get a quote
const quote = await sdk.quoteDeposit({
chainId: 8453,
tokenAddress: "0xA0b86991...",
amount: "1000000",
});
// 4. Show confirmation UI
console.log(quote.input); // { symbol: "USDC", amount: "1000000", amountUsd: "1" }
console.log(quote.output); // { symbol: "USDC", amount: "1000000", amountUsd: "1" }
console.log(quote.fees); // { totalUsd: "0.01" }
// 5. Execute
const result = await sdk.execute(quote, {
signerAddress: address,
deriveSigner: async (chainId) => ({
signer: getWalletClient({ chainId }),
publicClient: getPublicClient({ chainId }),
}),
onStatusChange: (status) => console.log(status),
});
if (result.status === "settled") {
console.log("Done!", result.txHashes);
}
配置
const sdk = new BlendSdk({
// Required
publishableKey: "pk_live_...", // Safe for client-side code
signMessage: (msg) => wallet.sign(msg), // SIWE signature function
// Required — validated at construction; sponsors Safe UserOps in execute()
paymaster: {
/* per-chain ERC-4337 bundler/paymaster endpoints — see below */
},
// Optional
baseUrl: "https://...", // API base URL override
fiatCurrency: "EUR", // ISO 4217 code for multi-currency
timeoutMs: 15000, // Request timeout (default: 15000)
retries: 3, // Max retries for 429/5xx (default: 3)
});
付费服务商
paymaster eRC-4337捆绑商/付费主机为每个链条和供应商所需的元数据服务的同步真理源.
单一提供者的设置是一个呼叫—— chainIds 默认为提供者服务的每个链条:
import {
getAlchemyPaymasterEndpoints,
getPimlicoPaymasterEndpoints,
} from "@blend-money/fe";
// Pimlico everywhere
paymaster: getPimlicoPaymasterEndpoints("pim_...");
// Alchemy everywhere
paymaster: getAlchemyPaymasterEndpoints("alch_...", {
policyId: "your-gas-manager-policy-id",
});
混合布局相接而起 后来为同一连锁ID扩展了先前的连锁ID,因此给少数群体提供者一个明确的连锁列表:
paymaster: {
...getPimlicoPaymasterEndpoints("pim_..."),
...getAlchemyPaymasterEndpoints(
"alch_...",
{ policyId: "your-gas-manager-policy-id" },
[9745], // Plasma — not served by Pimlico
),
},
Pimlico端点只需要API密钥(URL既能提供捆绑运输,又能提供支付). 炼金术终点还带有作为ERC-7677付款人背景传送的气体管理器政策ID(pm_getPaymasterStubData 编号 : pm_getPaymasterData) (中文(简体) ). 在登记册缺失的链上执行会丢出一个验证错误 .
供应商标签还选择了天然气价格来源: 用户操作费从执行时配置的捆绑器中引用,每个供应商遵循其供应商的有记录的食谱——Pimlico端点通过 pimlico_getUserOperationGasPrice (快速分级),通过 rundler_maxPriorityFeePerGas 引出缓冲+25%加缓冲最新基费. 不需要额外的配置,而Blend API在气体定价中也不起任何作用.
API 参考
认证
// Sign in — wallet prompted to sign a SIWE message
const session = await sdk.signIn({ address: "0x...", chainId: 8453 });
// session: { address, accountId, safeAddress, chainsDeployed, expiresAt }
// Sign out
await sdk.signOut();
// Check state
sdk.isSignedIn; // boolean
sdk.session; // AuthSession (throws if not signed in)
会话持久性
会话默认为内存并丢失在页面重载中 。 使用 exportSession() 财务报告和审定财务报表 restoreSession() 可以在重新装入时持续。 过期的令牌在下一次API呼叫时自动刷新. 使用 sessionStorage (当标签关闭时被清除) 除非你需要跨塔布的坚持- localStorage 将 XSS 的曝光范围延伸至符的一生.
// After sign-in, save the session
await sdk.signIn({ address: "0x...", chainId: 8453 });
sessionStorage.setItem("blend-session", JSON.stringify(sdk.exportSession()));
// On page load, restore the session
const stored = sessionStorage.getItem("blend-session");
if (stored) {
try {
sdk.restoreSession(JSON.parse(stored));
// sdk.isSignedIn === true — no wallet prompt needed
} catch {
// Corrupt data — fall back to signIn
sessionStorage.removeItem("blend-session");
}
}
发现
不需要认证 。 签到后, depositTokens 自动过滤以表示钱包持有的符号.
const chains = await sdk.discover.depositChains();
// [{ chainId, name, displayName, iconUrl }]
const tokens = await sdk.discover.depositTokens(8453);
// [{ chainId, address, symbol, name, decimals, logoURI, price, balance?, amount? }]
const destinations = await sdk.discover.withdrawDestinations();
// [{ chainId, name, loanTokenAddress }]
const yieldData = await sdk.discover.yield();
// { accountTypeId, yieldBreakdown: [{ chainId, vaultAddress, breakdown, summary }] }
数额
SDK要求在符的最小单位(例如. "1000000" (以美元计) 使用 parseAmount 财务报告和审定财务报表 formatAmount 可以在人可读和最小单位字符串之间转换,而不发生浮点精度损失.
import { parseAmount, formatAmount } from "@blend-money/fe";
// Human-readable → smallest unit
const smallest = parseAmount("100.50", 6); // "100500000" (USDC has 6 decimals)
// Smallest unit → human-readable
const human = formatAmount("100500000", 6); // "100.5"
// Use with discovered tokens
const tokens = await sdk.discover.depositTokens(8453);
const usdc = tokens.find((t) => t.symbol === "USDC")!;
const amount = parseAmount(userInput, usdc.decimals);
const quote = await sdk.quoteDeposit({
chainId: 8453,
tokenAddress: usdc.address,
amount,
});
引用
需要认证 。 创建或再使用会话,然后引用它。 返回您的确认对象 UI .
// Deposit quote
const quote = await sdk.quoteDeposit({
chainId: 8453,
tokenAddress: "0xA0b86991...",
amount: "1000000",
externalRef: "order-123", // optional — for reconciliation
forceReset: false, // optional — cancel existing session first (default: false)
});
// DepositQuote: { type, intentId, originChainId, destinationChainId,
// input, output, fees, estimatedSeconds, expiresAt }
// Withdrawal quote
const quote = await sdk.quoteWithdraw({
destinationChainId: 8453,
amount: "1000000",
isMaxWithdraw: false, // optional — redeem all shares (default: false)
forceReset: false, // optional
});
// WithdrawQuote: { type, intentId, destinationChainId, totalAmount,
// totalFeesUsd, estimatedSeconds, sourceChainCount, expiresAt }
现场引用
通话 quoteDeposit 或者说 quoteWithdraw 多次重引用相同的开放式会话与新参数。 没必要 forceReset 只是为了改变金额或象征.
// User types $1
const quote1 = await sdk.quoteDeposit({
chainId: 8453,
tokenAddress: "0xA0b86991...",
amount: parseAmount("1", 6),
});
// User changes to $5 — same session, fresh quote
const quote2 = await sdk.quoteDeposit({
chainId: 8453,
tokenAddress: "0xA0b86991...",
amount: parseAmount("5", 6),
});
// quote2 reflects the updated amount — same intentId, fresh prices
// Switching from withdraw to deposit also works on open sessions
const depositQuote = await sdk.quoteDeposit({ ... });
执行
通过一个引文、一个签名地址和一个 deriveSigner 回调返回给定链条的viem客户端。 SDK处理会话锁定,链上提交(通过付费人安全用户Ops),散列提交,以及内部和解投票. 对于多链取出, deriveSigner 每个源链称为一次.
const result = await sdk.execute(quote, {
signerAddress: address, // EOA address (Hex)
deriveSigner: async (chainId) => ({
// returns viem clients for the chain
signer: getWalletClient({ chainId }),
publicClient: getPublicClient({ chainId }),
}),
isContractSigner: false, // optional — for smart contract wallets (e.g. Coinbase)
onStatusChange: (status) => {
// optional — "executing" | "confirming" | "settled" | "failed"
updateUI(status);
},
pollIntervalMs: 3000, // optional (default: 3000)
pollTimeoutMs: 300000, // optional (default: 300000)
});
// ExecuteResult: { status, txHashes, settledAt, error }
账户数据
需要认证 .
const balance = await sdk.account.balance();
// { accountId, safeAddress, perChain, total }
const history = await sdk.account.balanceHistory({ startDate: "2025-01-01" });
// [{ id, createdAt, totalValue, totalYield, breakdown }]
const positions = await sdk.account.positions();
// { accountId, safeAddress, events }
const returns = await sdk.account.returns();
// { current, totalDeposited, totalWithdrawn, netDeposited, returns, returnsPct }
安全管理
// Resolve Safe deployment on a chain (requires auth)
const resolution = await sdk.account.safe.resolve(8453);
// { status: "validated", accountId, userAddress, safeAddress, chainId }
// Request Safe deployment (requires auth)
await sdk.account.safe.request(8453);
// Look up account by EOA (no auth required)
const account = await sdk.account.safe.lookup("0x...");
// { accountId, safeAddress, chainsDeployed }
电源用户:会话周期
对于先进用途案例,可通过下列途径获得整个意图会话生命周期: sdk.sessions数字 :
const session = await sdk.sessions.createSession({ forceReset: true });
const quoted = await sdk.sessions.quoteDeposit(session.intentId, { ... });
const locked = await sdk.sessions.lock(session.intentId, { signerAddress: "0x..." });
const submitted = await sdk.sessions.submit(session.intentId, { txHashes: [...] });
const result = await sdk.sessions.execute(session.intentId, { ... });
const sessions = await sdk.sessions.list();
await sdk.sessions.cancel(session.intentId);
处理错误
import { SdkError, SESSION_ERROR_CODES } from "@blend-money/fe";
try {
await sdk.quoteDeposit({ ... });
} catch (err) {
if (err instanceof SdkError) {
console.log(err.code); // e.g. "AUTH_NOT_SIGNED_IN"
console.log(err.status); // HTTP status
console.log(err.getUserMessage()); // human-readable message
console.log(err.isRetryable()); // true for 429, 5xx, network errors
}
}
错误代码 : AUTH_NOT_SIGNED_IN, (中文(简体) ), AUTH_CHALLENGE_FAILED, (中文(简体) ), AUTH_VERIFICATION_FAILED, (中文(简体) ), AUTH_TOKEN_EXPIRED, (中文(简体) ), AUTH_INVALID_SESSION, (中文(简体) ), INTENT_NOT_FOUND, (中文(简体) ), INTENT_EXPIRED, (中文(简体) ), INTENT_WRONG_STATUS, (中文(简体) ), INTENT_CONCURRENT_TRANSITION, (中文(简体) ), SESSION_NOT_QUOTED, (中文(简体) ), SESSION_LOCKED_BY_OTHER, (中文(简体) ), SETTLEMENT_TIMEOUT, (中文(简体) ), FLOWPLAN_CONFLICT。 。 。 .
发展
从单列波根:
pnpm install # Install all dependencies
pnpm --filter @blend-money/fe build # Build this package
pnpm --filter @blend-money/fe dev # Watch mode
pnpm --filter @blend-money/fe test # Run tests (interactive)
pnpm --filter @blend-money/fe test:run # Run tests (CI)
pnpm --filter @blend-money/fe check-types # Type check
或运行 pnpm build, (中文(简体) ), pnpm test:run等从单列波根运行到所有软件包.
许可证
麻省理工学院