APlane TypeScript SDK
通过apsigner签署Algorand事务的TypeScript SDK。
版本控制
只有当SDK发生变化时才会发布SDK包。SDK版本跟踪 兼容APlane发布标签,并可能跳过产品发布号。
原生猎鹰SDK发布线之后需要一个APlane签名者发布
v0.35.0;看到 docs/COMPATIBILITY.md.
安装
npm install aplanesdk algosdk
对于可选的AlgoKit适配器示例/客户端助手,安装当前的 AlgoKit Utils TypeScript v10测试版。适配器在10.0.0-beta.4上进行了测试:
npm install "@algorandfoundation/algokit-utils@^10.0.0-beta.4"
或者使用yarn/pnpm:
yarn add aplanesdk algosdk
pnpm add aplanesdk algosdk
从本地Tarball安装
从本地安装时 .tgz 首先构建并打包SDK,然后将生成的tarball安装到您的项目中:
# From typescript/
npm install
npm pack
# In your consuming project
npm init -y # if needed
npm install ../path/to/aplanesdk-<version>.tgz algosdk
故障排除
同伴依赖冲突如果看到对等依赖错误,请尝试:
npm install aplanesdk algosdk --legacy-peer-deps
快速入门
import { SignerClient, sendRawTransaction } from "aplanesdk";
import algosdk from "algosdk";
// Connect to signer
const client = await SignerClient.fromEnv();
// Build transaction with algosdk
const algodClient = new algosdk.Algodv2("", "https://testnet-api.4160.nodely.dev", "");
const params = await algodClient.getTransactionParams().do();
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
sender: "SENDER_ADDRESS",
receiver: "RECEIVER_ADDRESS",
amount: 1000000, // 1 ALGO
suggestedParams: params,
});
// Sign via apsigner (waits for operator approval)
const signed = await client.signTransaction(txn);
// Submit to network (signed is ready to use, no processing needed)
const txid = await sendRawTransaction(algodClient, signed);
console.log(`Submitted: ${txid}`);
的连接方法
所有SDK连接都使用配置的支持ssh的签名者路径。SDK模式不支持本地HTTP直接连接。
通过SSH远程连接
通过SSH隧道连接到远程机器上的指定程序,并进行身份验证 通过注册的客户端密钥:
const client = await SignerClient.connectSsh(
"signer.example.com",
"~/aplane/apclient/.ssh/id_ed25519",
{
sshPort: 1127, // default: 1127
timeout: 30000, // optional explicit shorter request timeout
sshSetupTimeout: 60000, // TCP dial + SSH authentication only
knownHostsPath: "~/aplane/apclient/.ssh/known_hosts",
}
);
SSH设置使用自己的60秒截止日期,独立于HTTP请求和
操作员批准超时。通过 signal 取消安装。注册了
同样的控制 setupTimeout 和 signal.
注意: SSH使用固定的非保密用户名 aplane 和公共密钥
身份验证;注册的密钥是客户端的唯一凭证。一个关键
未注册的签名者握手失败。完成后记得关闭:
await client.close();
环境相关连接
从数据目录加载配置。该目录是必需的- pass
dataDir 或者设置 APCLIENT_DATA 环境变量:
// Set environment variable
// export APCLIENT_DATA=~/aplane/apclient
const client = await SignerClient.fromEnv();
// Or pass directly
const client = await SignerClient.fromEnv({ dataDir: "~/aplane/apclient" });
数据目录结构(安装程序默认: ~/aplane/apclient):
<data_dir>/
config.yaml # Non-routing client settings
endpoints.yaml # Signer and cosigner routing
.ssh/
id_ed25519 # SSH key: the client's credential
known_hosts # Trusted signer host keys
例子 endpoints.yaml (通过SSH远程):
schema_version: 2
default: primary
endpoints:
primary:
role: signer
url: ssh://signer.example.com:1127
identity_file: .ssh/id_ed25519
known_hosts_path: .ssh/known_hosts
通过 trustOnFirstUse: true 来 SignerClient.fromEnv 只有当呼叫可能
信任并保存未知主机密钥。使用 endpoint: "cosigner.qa" 选择
命名的端点。
身份验证
客户端的SSH密钥是它的凭据。新密钥只注册一次
请求排队等待操作人员稍后批准 apadmin (pending: true(正常结果)或者钥匙已经注册了,但什么都没有
存储在客户端。操作员批准后连接:
import { requestEnrollmentFromEnv } from "aplanesdk";
const { fingerprint, pending } = await requestEnrollmentFromEnv({ label: "ci-runner" });
if (pending) console.log(`key ${fingerprint} queued; have the operator approve it in apadmin`);
requestEnrollment(host, sshKeyPath, { knownHostsPath, label }) 是
显式的形式。同样的注册可以从 apshell 与
enroll.
API参考
SignerClient
health(): Promise<boolean>
检查签名者是否可以联系到。
if (await client.health()) {
console.log("Signer is online");
}
getStatus(): Promise<StatusResponse>
获取已验证的签名者状态。这在签名者被锁定时有效。
const status = await client.getStatus();
console.log(status.state, status.keysetRevision, status.warnings);
keysetRevision 流程是本地的,对于决定何时刷新有用吗
listKeys(true);它在重新启动指定程序时不是持久的。
approvalWaitSeconds 是由SDK使用的大小 /sign 最后期限。显示
非空 warnings 运营商;他们报告了持续的健康状况
这需要关注。
listKeys(refresh?: boolean): Promise<KeyInfo[]>
列出可用的签名密钥。
const keys = await client.listKeys();
for (const key of keys) {
console.log(`${key.address} [${key.keyType}]`);
}
返回的列表 KeyInfo:
address: Algorand地址keyType: “ed25519”, “aplane.falcon1024. ”v1”、“aplane.htlc。v1”等。logicSigResources:独立的程序字节,参数字节,和 按授权路径划分的最大操作码成本需求。isGenericLsig:如果不需要加密签名,则为TruesigningArgs:名单SigningArg对于LogicSigs
SDK只公开有限的库存和普通支出签名。事实并非如此
构建、部分签署或完成合同管理密钥交易;使用
APlane aprekey 这些操作的工作流。
发现通用logicsig所需的参数:
const keyInfo = await client.getKeyInfo(hashlockAddress);
if (keyInfo?.signingArgs) {
for (const arg of keyInfo.signingArgs) {
console.log(`${arg.name}: ${arg.type} - ${arg.description}`);
}
}
signTransaction(txn, authAddress?, lsigArgs?): Promise<string>
签署单个事务。返回准备提交的base64编码字符串。
服务器通过根据需要添加虚拟事务来自动处理大型logicsig(例如Falcon-1024)的费用池。
// Basic signing (uses txn.sender as authAddress)
const signed = await client.signTransaction(txn);
// Rekeyed account (different auth key)
const signed = await client.signTransaction(txn, "SIGNER_KEY_ADDRESS");
// Generic LogicSig with runtime args (e.g., HTLC)
const signed = await client.signTransaction(
txn,
"HASHLOCK_ADDRESS",
{ preimage: new Uint8Array([/* secret value */]) }
);
// Submit directly (no processing needed)
const txid = await sendRawTransaction(algodClient, signed);
signTransactions(txns, authAddresses?, lsigArgsMap?): Promise<string>
将多个事务作为一个组进行签名。返回一个base64编码的串接签名事务字符串,准备提交。
重要的:不预先分配组id。服务器在为大型logicsig添加所需的虚拟事务后计算组ID。
// Build transactions (do NOT call assignGroupId)
const txn1 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({...});
const txn2 = algosdk.makePaymentTxnWithSuggestedParamsFromObject({...});
// Sign group (server handles grouping and dummies)
const signed = await client.signTransactions([txn1, txn2]);
// Submit directly (no processing needed)
const response = await algodClient.sendRawTransaction(Buffer.from(signed, "base64")).do();
signTransactionsList(txns, authAddresses?, lsigArgsMap?): Promise<string[]>
就像 signTransactions() 但是返回单个base64编码的事务,而不是串联的事务。当您需要单独检查事务时非常有用。
const signedList = await client.signTransactionsList([txn1, txn2]);
// signedList is string[], each element is a base64-encoded signed transaction
signRequests(requests, options?): Promise<GroupSignResponse>
发送一个或多个raw /sign 请求条目。在集成时使用这个
已经拥有事务编码,并希望APlane的本地响应形状。
const response = await client.signRequests(
[{
txn_bytes_hex: "5458...",
auth_address: "SIGNER_KEY_ADDRESS",
txn_sender: "SENDER_ADDRESS", // advisory display hint only
}],
{ requestId: "app-owned-request-id" },
);
AlgoKit Utils适配器
对于AlgoKit Utils TypeScript v10 beta版的事务签名,请使用适配器
帐户。它是针对10.0.0-beta测试的。4、连接AlgoKit客户端到
APlane的事务签名功能,并给出了 addr +
signer(txnGroup, indexesToSign) 形状。
最小的存储库示例是 examples/algokit_self_send.ts. 从一个
签出时安装了依赖项并构建了SDK:
cd ~/aplanesdk/typescript
npm install
npm install --no-save "@algorandfoundation/algokit-utils@^10.0.0-beta.4"
npm run build
export APCLIENT_DATA=~/aplane/apclient
export APLANE_ADDRESS=SENDER_ADDRESS
node --import tsx examples/algokit_self_send.ts
该示例使用AlgoKit构建一个事务,并通过APlane对其进行签名 适配器,然后提交签名blob与AlgoKit的algod客户端:
import { AlgorandClient, microAlgo } from "@algorandfoundation/algokit-utils";
import { SignerClient, createApsignerAccount } from "aplanesdk";
const sender = "SENDER_ADDRESS";
const algorand = AlgorandClient.testNet();
const signer = await SignerClient.fromEnv();
const info = await algorand.account.getInformation(sender);
const account = createApsignerAccount({
client: signer,
address: sender,
authAddress: info.authAddr?.toString() ?? sender,
});
const txn = await algorand.createTransaction.payment({
sender,
signer: account,
receiver: sender,
amount: microAlgo(0),
validityWindow: 1000,
});
const signed = await account.signer([txn], [0]);
const txId = (await algorand.client.algod.sendRawTransaction(signed)).txId;
使用 createTransaction.* 当APlane必须拥有最终签名和任何
aplane管理的集团扩张。 algorand.send.* 拥有编写器发送路径
在那条路上有标志。
签名呼叫发现 /status.approval_wait_seconds 然后使用这个值
加上30秒的请求超时。如果发现失败了,还是老的
签名者省略该字段,签名者将返回到6分钟。明确的更短的
超时仍然是赢家;SDK /sign 呼叫包括 request_id 然后发送一个
的最优 /sign/cancel HTTP请求超时或断开连接。
高级签名方法接受 requestId 和 signal 选项
需要用户发起取消的应用程序。
cancelSignRequest(requestId): Promise<CancelSignResponse>
要求指派者取消正在进行的同步 /sign 按请求ID请求。
成功的响应对于客户端行为和返回状态是幂等的
"canceled" 或者 "not_found".
TypeScript高级签名默认会生成一个请求ID。互动
应用程序可以传递应用程序拥有的ID和 AbortSignal;流产的
signal终止HTTP请求并发送best-effort /sign/cancel 与
相同的ID:
const controller = new AbortController();
const requestId = "wallet-ui-approval-123";
const signed = await client.signTransaction(txn, undefined, undefined, {
requestId,
signal: controller.signal,
});
// elsewhere, if the user aborts while approval is pending:
controller.abort();
支持的密钥类型
| 密钥类型 | 描述 | 笔记 |
|---|---|---|
ed25519 | 本地Algorand键 | 标准签署 |
aplane.falcon1024.v1 | Post-quantum LogicSig | LogicSig中的签名。Args [0] |
aplane.ed25519.v1 | Ed25519 DSA LogicSig | 库可见的普通DSA帐户 |
aplane.witness-falcon1024.v1 | 见证关键 | 共同签名者保管的策略签名密钥;不是支出账户 |
aplane.falcon1024-cosigner1024.v1 | 保护账户 | 需要用户和共同签名者组件签名 |
aplane.corridor.v1 | 有界走廊帐户 | bounded1 合同; bounded-cosigner1 花流 |
aplane.falcon1024-allowlist.v1 | 有界allowlist | 内联allowlist; bounded1 签字流程 |
aplane.falcon1024-allowlist.v2 | 有界allowlist | Merkle allowlist; bounded1 签字流程 |
aplane.falcon1024-timelock.v1 | 有界timelock | Round-gated bounded1 签字流程 |
aplane.falcon1024-allowlist-alock.v1 | Rekey-locked bounded allowlist | 一般开支用途 bounded1;admin rekey不在SDK范围内 |
aplane.htlc.v1 | Hash-locked基金 | 需要 preimage arg(检查 signingArgs) |
服务器组装完整的签名事务—SDK返回一个准备提交的base64字符串。
共同签署人和保密账户
注册为共同签名者的见证密钥是公共策略签名选择器,而不是Algorand 支出账户。不要将它们用作发送者、接收者、验证地址或 再续键的目标。保护帐户密钥必须通过保护流程进行签名。
低级端点包装器可用:
const userPart = await userClient.requestComponents({
group_bytes_hex: ["5458..."],
targets: [{
target_index: 0,
kind: "user",
auth_address: "GUARDED_ACCOUNT_ADDRESS",
}],
});
const cosignerPart = await cosignerClient.requestComponents({
group_bytes_hex: ["5458..."],
targets: [{
target_index: 0,
kind: "cosigner",
component_key: "COSIGNER_COMPONENT_SELECTOR",
}],
});
const assembled = await userClient.requestAssemble({
group_bytes_hex: ["5458..."],
targets: [{
target_index: 0,
kind: "guarded",
auth_address: "GUARDED_ACCOUNT_ADDRESS",
user_signature: userPart.components[0].signature!,
cosigner_signature: cosignerPart.components[0].signature!,
}],
});
对于常见的显式双客户机流,使用 signGuardedGroup. 直接
Helper不执行库存发现,因此通过审查的支出路径
返回的资源配置文件 listKeys():
const result = await signGuardedGroup({
userClient,
cosignerClient,
cosignerComponentKey: "COSIGNER_COMPONENT_SELECTOR",
groupBytesHex: ["5458..."],
guardedTargets: [
{
targetIndex: 0,
guardedAccount: "GUARDED_ACCOUNT_ADDRESS",
logicSigResources: reviewedSpendResources,
},
],
});
const signedGroup = result.signedGroup;
assembleGroup() 保留本地多方连接助手;事实并非如此
与服务器端保护程序集相同的操作。
有限的联名人账户
走廊使用有界契约 bounded1 与不同的
bounded-cosigner1 在线签名流程。合同标识LogicSig
规则;该流标识用户优先的多端点编排。这个
Prepared helper从签名者目录中检测该流并对其进行路由
自动:
const result = await signPreparedGuardedGroup({
userClient,
cosignerResolver,
preparedGroup,
});
const signedGroup = result.signedGroup;
SDK首先冻结整个规范组 /plan;用户
签名者然后批准这些字节通过 requestComponents() 与
kind: "bounded-base". 然后,它在普通的位置上签字,而且只有在签字之后
用户端是完整的,它请求寄售人签名相同
字节和调用 requestAssemble(). 之前
签署任何东西,SDK比较
签名者制作的计划与呼叫者的准备组:只报告费用
接受池和组id分配,并且附加的位置必须是
规范预算假人。返回的组必须使用规范事务
编码和从呈现的成员关系重新计算的组ID。SDK也是
控件验证普通签名位置和每个组装的事务
冻结的事务字节。
requestComponents() 发送感伤。 /sign/cancel 当它
承载审批的请求被中止、超时或断开连接。
requestAssemble() 不开审批请求,是不是一个
取消处理。
签名者规划器拥有费用选择、授权资源大小等 报告了两种保护流的群体突变。
拥有编排的应用程序可以调用 requestComponents() 和
requestAssemble() 直接。签名者授权仅在此使用
合同;有限的合约管理密钥仍然是外部的 aprekey 仪式
并且不是由SDK完成的。
错误处理
签署例外
import {
SignerError,
## Project
This SDK is part of the APlane project:
- Repository: https://github.com/aplane-algo/aplanesdk
- SDK path: `typescript`
APlane is an open-source project stewarded by the APlane Project.
See the repository [README](https://github.com/aplane-algo/aplanesdk/blob/main/README.md) for project overview and alpha-status guidance, and [DISCLAIMER.md](https://github.com/aplane-algo/aplanesdk/blob/main/DISCLAIMER.md) for risk, liability, and usage information.
AuthenticationError,
SigningRejectedError,
SignerUnavailableError,
KeyNotFoundError,
} from "aplanesdk";
try {
const signed = await client.signTransaction(txn);
} catch (error) {
if (error instanceof AuthenticationError) {
console.log("Connection is not authenticated by an enrolled SSH key");
} else if (error instanceof SigningRejectedError) {
console.log("Operator rejected the request");
} else if (error instanceof SignerUnavailableError) {
console.log("Signer not reachable or locked");
} else if (error instanceof KeyNotFoundError) {
console.log("Key not found in signer");
} else if (error instanceof SignerError) {
console.log(`Signing failed: ${error.message}`);
}
}
提交异常
sendRawTransaction() 将冗长的algod错误打包为干净的异常:
import {
sendRawTransaction,
TransactionRejectedError,
LogicSigRejectedError,
InsufficientFundsError,
InvalidTransactionError,
} from "aplanesdk";
try {
const txid = await sendRawTransaction(algodClient, signed);
} catch (error) {
if (error instanceof LogicSigRejectedError) {
console.log(`LogicSig failed: ${error.reason}`); // error.txid also available
} else if (error instanceof InsufficientFundsError) {
console.log(`Not enough funds: ${error.reason}`);
} else if (error instanceof InvalidTransactionError) {
console.log(`Invalid transaction: ${error.reason}`);
} else if (error instanceof TransactionRejectedError) {
console.log(`Rejected: ${error.reason}`);
}
}
示例:完整的工作流
import { SignerClient, SignerError, sendRawTransaction } from "aplanesdk";
import algosdk from "algosdk";
async function main() {
// Connect to the signer with the enrolled client key
const client = await SignerClient.fromEnv();
// List keys
const keys = await client.listKeys();
const sender = keys[0].address;
console.log(`Using: ${sender}`);
// Build transaction
const algodClient = new algosdk.Algodv2("", "https://testnet-api.4160.nodely.dev", "");
const params = await algodClient.getTransactionParams().do();
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
sender: sender,
receiver: sender,
amount: 0,
suggestedParams: params,
});
// Sign (will wait for operator approval)
try {
const signed = await client.signTransaction(txn);
console.log("Signed!");
// Submit directly (no processing needed)
const txid = await sendRawTransaction(algodClient, signed);
console.log(`TxID: ${txid}`);
// Wait for confirmation
const result = await algosdk.waitForConfirmation(algodClient, txid, 4);
console.log(`Confirmed in round ${result.confirmedRound}`);
} catch (error) {
if (error instanceof SignerError) {
console.log(`Failed: ${error.message}`);
} else {
throw error;
}
}
}
main().catch(console.error);
LogicSig资源规划
签名者将LogicSig程序字节、参数字节和操作码开销规划为 独立的共识资源。在v42下,多余的程序字节由 团费;只有当参数或操作码容量大时才添加假人 需要他们。
如何工作(服务器端)
- 服务器选择授权路径资源配置文件。
- 服务器解决参数和操作码容量,只添加规范假人 在需要时。
- 服务器增加了v42程序字节费用贡献。
- 服务器计算最终的组ID并签署所有事务。
- SDK返回准备提交的完整签名组。
示例:Falcon-1024 Key
const params = await algodClient.getTransactionParams().do();
const txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
sender: falconAddr,
receiver: receiverAddr,
amount: 1000000,
suggestedParams: params,
});
// Server automatically adds dummies - just sign and submit
const signed = await client.signTransaction(txn);
const txid = await sendRawTransaction(algodClient, signed);
费用的影响
| 密钥类型 | LogicSig大小 | 假人需要 | 额外的费用 |
|---|---|---|---|
| Ed25519 | 0 | 0 | 0 |
| 猎鹰- 1024 | ~ 3035 | 3 | ~ 3000 uA |
额外费用包括后量子安全所需的虚拟交易。
许可证
麻省理工学院
项目
这个SDK是APlane项目的一部分:
- 存储库: https://github.com/aplane-algo/aplanesdk
- SDK路径:
typescript
APlane是一个由APlane项目管理的开源项目。
请参阅存储库 自述 对于项目概述和alpha状态指导,以及 DISCLAIMER.md 风险、责任和使用信息。