@portkey/aelf-signer
v1.0.2
Published
Unified signer interface for aelf blockchain — supports both EOA (direct signing) and CA (Portkey Contract Account via ManagerForwardCall).
Keywords
Readme
@portkey/aelf-signer
aelf 区块链统一签名接口 — 同时支持 EOA(直接私钥签名)和 CA(Portkey 合约账户,通过 ManagerForwardCall 代理签名)。
为什么需要
aelf 上的 DApp skill(如 Awaken DEX、eForest NFT 等)目前只支持 EOA 钱包。Portkey CA 钱包用户无法使用这些 skill,因为 CA 交易需要将每个合约调用包装在 ManagerForwardCall 中。
@portkey/aelf-signer 提供统一的 AelfSigner 接口,DApp skill 接受此接口替代原始的 wallet: any 参数。签名细节由 signer 内部处理 — EOA 直接签名,CA 则包装为 ManagerForwardCall — 对 DApp skill 完全透明。
安装
bun add @portkey/aelf-signer
# 或
npm install @portkey/aelf-signer快速开始
从环境变量自动检测
import { createSignerFromEnv, callViewMethod } from '@portkey/aelf-signer';
// 根据环境变量自动创建 EoaSigner 或 CaSigner
const signer = createSignerFromEnv();
console.log(`身份地址: ${signer.address}`); // EOA 地址或 CA 地址
console.log(`签名密钥: ${signer.keyAddress}`); // EOA 相同, CA 为 Manager 地址
console.log(`是否 CA: ${signer.isCa}`);
// 发送合约调用(透明处理:EOA 直签或 CA 通过 ManagerForwardCall)
const result = await signer.sendContractCall(
'https://aelf-public-node.aelf.io',
tokenContractAddress,
'Transfer',
{ to: recipient, symbol: 'ELF', amount: '100000000', memo: '' },
);
console.log(`交易 ID: ${result.transactionId}`);
// View 调用不需要 signer
const balance = await callViewMethod(rpcUrl, tokenContract, 'GetBalance', {
symbol: 'ELF',
owner: signer.address,
});EOA 模式
import { createEoaSigner } from '@portkey/aelf-signer';
const signer = createEoaSigner(process.env.AELF_PRIVATE_KEY!);
// signer.address === signer.keyAddress === 钱包地址CA 模式
import { createCaSigner } from '@portkey/aelf-signer';
const signer = createCaSigner({
managerPrivateKey: process.env.PORTKEY_PRIVATE_KEY!,
caHash: process.env.PORTKEY_CA_HASH!,
caAddress: process.env.PORTKEY_CA_ADDRESS!,
// 可选:直接提供以避免 API 查询
caContractAddress: process.env.PORTKEY_CA_CONTRACT_ADDRESS,
// 可选:默认为主网
portkeyApiUrl: 'https://aa-portkey.portkey.finance',
});
// signer.address = CA 地址(链上身份)
// signer.keyAddress = Manager 地址(签名密钥)环境变量
EOA 模式
# 设置其一:
AELF_PRIVATE_KEY=<64位 hex 私钥>
# 或
PORTKEY_PRIVATE_KEY=<64位 hex 私钥>CA 模式
# 三个都必须设置:
PORTKEY_PRIVATE_KEY=<Manager 私钥 hex>
PORTKEY_CA_HASH=<CA hash>
PORTKEY_CA_ADDRESS=<CA 地址>
# 可选:
PORTKEY_CA_CONTRACT_ADDRESS=<目标链的 CA 合约地址>
PORTKEY_API_URL=<Portkey API 地址, 默认主网>检测优先级:
- 若
PORTKEY_CA_HASH+PORTKEY_CA_ADDRESS+PORTKEY_PRIVATE_KEY全部设置 -> CA 模式 - 若
AELF_PRIVATE_KEY已设置 -> EOA 模式 - 若仅
PORTKEY_PRIVATE_KEY已设置 -> EOA 模式(降级)
API 参考
AelfSigner 接口
interface AelfSigner {
readonly address: string; // 身份地址
readonly keyAddress: string; // 签名密钥地址
readonly isCa: boolean; // 是否 CA 模式
sendContractCall(
rpcUrl: string,
contractAddress: string,
methodName: string,
params: Record<string, unknown>,
): Promise<SendResult>;
signMessage(message: string): string;
}address: 链上身份地址。EOA 为钱包地址,CA 为合约账户地址。用于owner、from等字段。keyAddress: 实际签名密钥的地址。EOA 等于address,CA 为 Manager 地址。用于 API 认证。sendContractCall(): 发送合约写入调用。EOA 直接签名;CA 将参数 protobuf 编码后包装为ManagerForwardCall。signMessage(): SHA256 哈希并签名消息。返回 hex 签名。
工厂函数
| 函数 | 说明 |
|------|------|
| createSignerFromEnv() | 从环境变量自动检测 signer 类型 |
| createEoaSigner(privateKey) | 创建 EOA signer |
| createCaSigner(config) | 创建 CA signer |
| isEoaSigner(signer) | EoaSigner 类型守卫 |
| isCaSigner(signer) | CaSigner 类型守卫 |
| getSigningAddress(signer) | 获取 keyAddress |
工具函数
| 函数 | 说明 |
|------|------|
| callViewMethod(rpcUrl, addr, method, params?) | 只读合约调用(无需 signer) |
| pollTxResult(rpcUrl, txId) | 轮询交易结果 |
| getAelfInstance(rpcUrl) | 获取缓存的 AElf SDK 实例 |
| fetchChainInfo(apiUrl?) | 从 Portkey API 获取链信息 |
| clearCaches() | 清除所有缓存(测试用) |
DApp Skill 开发者指南
改造前(仅支持钱包)
// awaken-agent-skills/src/core/trade.ts(改造前)
export async function executeSwap(config, wallet: any, params) {
const account = wallet.address; // 只支持 EOA
await approveToken(config, wallet, ...);
await callSendMethod(rpcUrl, contract, 'Swap', wallet, args);
}改造后(兼容 signer)
// awaken-agent-skills/src/core/trade.ts(改造后)
import type { AelfSigner } from '@portkey/aelf-signer';
export async function executeSwap(config, signer: AelfSigner, params) {
const account = signer.address; // CA 返回 caAddress, EOA 返回 walletAddress
await signer.sendContractCall(rpcUrl, tokenContract, 'Approve', { ... });
await signer.sendContractCall(rpcUrl, swapContract, 'Swap', { ... });
}MCP 服务集成
// awaken-agent-skills/src/mcp/server.ts
import { createSignerFromEnv } from '@portkey/aelf-signer';
// 在 tool handler 中:
const signer = createSignerFromEnv(); // 自动检测 EOA 或 CA
const result = await executeSwap(config, signer, params);架构
@portkey/aelf-signer
├── src/
│ ├── types.ts # AelfSigner 接口, 结果类型
│ ├── utils.ts # AElf SDK 封装, TX 轮询, view 调用
│ ├── eoa-signer.ts # EoaSigner: 直接私钥签名
│ ├── ca-signer.ts # CaSigner: ManagerForwardCall + protobuf 编码
│ ├── factory.ts # createSignerFromEnv, 类型守卫
│ └── index.ts # 统一导出
└── __tests__/
└── unit/
└── signer.test.tsCaSigner 工作原理
当调用 CaSigner.sendContractCall(rpcUrl, contractAddr, method, params) 时:
- 解析 CA 合约地址 — 从配置获取或通过 Portkey API 自动发现
- 获取 protobuf 描述 — 目标合约的
getContractFileDescriptorSet - 查找 input 类型 — 在 protobuf schema 中定位方法的输入消息类型
- 编码参数 — 应用 aelf-sdk transform + protobuf 编码为字节
- 调用 ManagerForwardCall — 在 CA 合约上发起
{ caHash, contractAddress, methodName, args } - 轮询交易结果 — 等待出块,返回结果
License
MIT
