@portkey/aelf-signer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Portkey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # @portkey/aelf-signer
2
+
3
+ > Unified signer interface for the aelf blockchain — supports both EOA (direct signing) and CA (Portkey Contract Account via ManagerForwardCall).
4
+
5
+ [中文文档](./README.zh-CN.md)
6
+
7
+ ---
8
+
9
+ ## Why
10
+
11
+ DApp skills on aelf (Awaken DEX, eForest NFT, etc.) currently only work with EOA wallets. Portkey CA wallet users cannot use these skills because CA transactions require wrapping every contract call in `ManagerForwardCall`.
12
+
13
+ `@portkey/aelf-signer` solves this by providing a common `AelfSigner` interface that DApp skills can accept instead of raw `wallet: any`. The signer handles signing details internally — EOA signs directly, CA wraps in ManagerForwardCall — making the wallet type completely transparent to the DApp skill.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ bun add @portkey/aelf-signer
19
+ # or
20
+ npm install @portkey/aelf-signer
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### Auto-detect from Environment
26
+
27
+ ```typescript
28
+ import { createSignerFromEnv, callViewMethod } from '@portkey/aelf-signer';
29
+
30
+ // Automatically creates EoaSigner or CaSigner based on env vars
31
+ const signer = createSignerFromEnv();
32
+
33
+ console.log(`Identity: ${signer.address}`); // EOA address or CA address
34
+ console.log(`Signing key: ${signer.keyAddress}`); // Same for EOA, Manager address for CA
35
+ console.log(`Is CA: ${signer.isCa}`);
36
+
37
+ // Send a contract call (transparent: EOA direct or CA via ManagerForwardCall)
38
+ const result = await signer.sendContractCall(
39
+ 'https://aelf-public-node.aelf.io',
40
+ tokenContractAddress,
41
+ 'Transfer',
42
+ { to: recipient, symbol: 'ELF', amount: '100000000', memo: '' },
43
+ );
44
+ console.log(`TX: ${result.transactionId}`);
45
+
46
+ // View calls don't need a signer
47
+ const balance = await callViewMethod(rpcUrl, tokenContract, 'GetBalance', {
48
+ symbol: 'ELF',
49
+ owner: signer.address,
50
+ });
51
+ ```
52
+
53
+ ### EOA Mode
54
+
55
+ ```typescript
56
+ import { createEoaSigner } from '@portkey/aelf-signer';
57
+
58
+ const signer = createEoaSigner(process.env.AELF_PRIVATE_KEY!);
59
+ // signer.address === signer.keyAddress === wallet address
60
+ ```
61
+
62
+ ### CA Mode
63
+
64
+ ```typescript
65
+ import { createCaSigner } from '@portkey/aelf-signer';
66
+
67
+ const signer = createCaSigner({
68
+ managerPrivateKey: process.env.PORTKEY_PRIVATE_KEY!,
69
+ caHash: process.env.PORTKEY_CA_HASH!,
70
+ caAddress: process.env.PORTKEY_CA_ADDRESS!,
71
+ // Optional: provide directly to avoid API lookup
72
+ caContractAddress: process.env.PORTKEY_CA_CONTRACT_ADDRESS,
73
+ // Optional: defaults to mainnet
74
+ portkeyApiUrl: 'https://aa-portkey.portkey.finance',
75
+ });
76
+
77
+ // signer.address = CA address (on-chain identity)
78
+ // signer.keyAddress = Manager address (signing key)
79
+ ```
80
+
81
+ ## Environment Variables
82
+
83
+ ### EOA Mode
84
+
85
+ ```bash
86
+ # Set one of:
87
+ AELF_PRIVATE_KEY=<64-char hex private key>
88
+ # or
89
+ PORTKEY_PRIVATE_KEY=<64-char hex private key>
90
+ ```
91
+
92
+ ### CA Mode
93
+
94
+ ```bash
95
+ # All three required:
96
+ PORTKEY_PRIVATE_KEY=<manager private key hex>
97
+ PORTKEY_CA_HASH=<CA hash>
98
+ PORTKEY_CA_ADDRESS=<CA address>
99
+
100
+ # Optional:
101
+ PORTKEY_CA_CONTRACT_ADDRESS=<CA contract address on target chain>
102
+ PORTKEY_API_URL=<Portkey API URL, defaults to mainnet>
103
+ ```
104
+
105
+ Detection priority:
106
+ 1. If `PORTKEY_CA_HASH` + `PORTKEY_CA_ADDRESS` + `PORTKEY_PRIVATE_KEY` all set -> **CA mode**
107
+ 2. If `AELF_PRIVATE_KEY` set -> **EOA mode**
108
+ 3. If only `PORTKEY_PRIVATE_KEY` set -> **EOA mode** (fallback)
109
+
110
+ ## API Reference
111
+
112
+ ### `AelfSigner` Interface
113
+
114
+ ```typescript
115
+ interface AelfSigner {
116
+ readonly address: string; // Identity address
117
+ readonly keyAddress: string; // Signing key address
118
+ readonly isCa: boolean; // Whether CA mode
119
+
120
+ sendContractCall(
121
+ rpcUrl: string,
122
+ contractAddress: string,
123
+ methodName: string,
124
+ params: Record<string, unknown>,
125
+ ): Promise<SendResult>;
126
+
127
+ signMessage(message: string): string;
128
+ }
129
+ ```
130
+
131
+ - `address`: The identity on chain. For EOA: wallet address. For CA: CA address. Use for `owner`, `from` fields.
132
+ - `keyAddress`: The address of the actual signing key. For EOA: same as `address`. For CA: Manager address. Use for API auth.
133
+ - `sendContractCall()`: Send a state-changing contract call. EOA signs directly; CA wraps in `ManagerForwardCall` with protobuf encoding.
134
+ - `signMessage()`: SHA256-hash and sign a message. Returns hex signature.
135
+
136
+ ### Factory Functions
137
+
138
+ | Function | Description |
139
+ |----------|-------------|
140
+ | `createSignerFromEnv()` | Auto-detect signer type from env vars |
141
+ | `createEoaSigner(privateKey)` | Create EOA signer |
142
+ | `createCaSigner(config)` | Create CA signer |
143
+ | `isEoaSigner(signer)` | Type guard for EoaSigner |
144
+ | `isCaSigner(signer)` | Type guard for CaSigner |
145
+ | `getSigningAddress(signer)` | Get `keyAddress` |
146
+
147
+ ### Utility Functions
148
+
149
+ | Function | Description |
150
+ |----------|-------------|
151
+ | `callViewMethod(rpcUrl, addr, method, params?)` | Read-only contract call (no signer needed) |
152
+ | `pollTxResult(rpcUrl, txId)` | Poll for transaction result |
153
+ | `getAelfInstance(rpcUrl)` | Get cached AElf SDK instance |
154
+ | `fetchChainInfo(apiUrl?)` | Fetch chain info from Portkey API |
155
+ | `clearCaches()` | Clear all caches (for testing) |
156
+
157
+ ## For DApp Skill Developers
158
+
159
+ ### Before (wallet-only)
160
+
161
+ ```typescript
162
+ // awaken-agent-skills/src/core/trade.ts (before)
163
+ export async function executeSwap(config, wallet: any, params) {
164
+ const account = wallet.address; // Only works with EOA
165
+ await approveToken(config, wallet, ...);
166
+ await callSendMethod(rpcUrl, contract, 'Swap', wallet, args);
167
+ }
168
+ ```
169
+
170
+ ### After (signer-compatible)
171
+
172
+ ```typescript
173
+ // awaken-agent-skills/src/core/trade.ts (after)
174
+ import type { AelfSigner } from '@portkey/aelf-signer';
175
+
176
+ export async function executeSwap(config, signer: AelfSigner, params) {
177
+ const account = signer.address; // caAddress for CA, walletAddress for EOA
178
+ await signer.sendContractCall(rpcUrl, tokenContract, 'Approve', { ... });
179
+ await signer.sendContractCall(rpcUrl, swapContract, 'Swap', { ... });
180
+ }
181
+ ```
182
+
183
+ ### MCP Server Integration
184
+
185
+ ```typescript
186
+ // awaken-agent-skills/src/mcp/server.ts
187
+ import { createSignerFromEnv } from '@portkey/aelf-signer';
188
+
189
+ // In tool handler:
190
+ const signer = createSignerFromEnv(); // Auto-detects EOA or CA
191
+ const result = await executeSwap(config, signer, params);
192
+ ```
193
+
194
+ ## Architecture
195
+
196
+ ```
197
+ @portkey/aelf-signer
198
+ ├── src/
199
+ │ ├── types.ts # AelfSigner interface, result types
200
+ │ ├── utils.ts # AElf SDK wrappers, TX polling, view calls
201
+ │ ├── eoa-signer.ts # EoaSigner: direct private key signing
202
+ │ ├── ca-signer.ts # CaSigner: ManagerForwardCall + protobuf encoding
203
+ │ ├── factory.ts # createSignerFromEnv, type guards
204
+ │ └── index.ts # Re-exports
205
+ └── __tests__/
206
+ └── unit/
207
+ └── signer.test.ts
208
+ ```
209
+
210
+ ### How CaSigner Works
211
+
212
+ When `CaSigner.sendContractCall(rpcUrl, contractAddr, method, params)` is called:
213
+
214
+ 1. **Resolve CA contract address** — from config or auto-discovered via Portkey API
215
+ 2. **Fetch protobuf descriptors** — `getContractFileDescriptorSet` for the target contract
216
+ 3. **Find input type** — locate the method's input message type in the protobuf schema
217
+ 4. **Encode params** — apply aelf-sdk transforms + protobuf encoding to bytes
218
+ 5. **Call ManagerForwardCall** — on the CA contract with `{ caHash, contractAddress, methodName, args }`
219
+ 6. **Poll TX result** — wait for mining, return result
220
+
221
+ ## License
222
+
223
+ MIT
@@ -0,0 +1,223 @@
1
+ # @portkey/aelf-signer
2
+
3
+ > aelf 区块链统一签名接口 — 同时支持 EOA(直接私钥签名)和 CA(Portkey 合约账户,通过 ManagerForwardCall 代理签名)。
4
+
5
+ [English](./README.md)
6
+
7
+ ---
8
+
9
+ ## 为什么需要
10
+
11
+ aelf 上的 DApp skill(如 Awaken DEX、eForest NFT 等)目前只支持 EOA 钱包。Portkey CA 钱包用户无法使用这些 skill,因为 CA 交易需要将每个合约调用包装在 `ManagerForwardCall` 中。
12
+
13
+ `@portkey/aelf-signer` 提供统一的 `AelfSigner` 接口,DApp skill 接受此接口替代原始的 `wallet: any` 参数。签名细节由 signer 内部处理 — EOA 直接签名,CA 则包装为 ManagerForwardCall — 对 DApp skill 完全透明。
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ bun add @portkey/aelf-signer
19
+ # 或
20
+ npm install @portkey/aelf-signer
21
+ ```
22
+
23
+ ## 快速开始
24
+
25
+ ### 从环境变量自动检测
26
+
27
+ ```typescript
28
+ import { createSignerFromEnv, callViewMethod } from '@portkey/aelf-signer';
29
+
30
+ // 根据环境变量自动创建 EoaSigner 或 CaSigner
31
+ const signer = createSignerFromEnv();
32
+
33
+ console.log(`身份地址: ${signer.address}`); // EOA 地址或 CA 地址
34
+ console.log(`签名密钥: ${signer.keyAddress}`); // EOA 相同, CA 为 Manager 地址
35
+ console.log(`是否 CA: ${signer.isCa}`);
36
+
37
+ // 发送合约调用(透明处理:EOA 直签或 CA 通过 ManagerForwardCall)
38
+ const result = await signer.sendContractCall(
39
+ 'https://aelf-public-node.aelf.io',
40
+ tokenContractAddress,
41
+ 'Transfer',
42
+ { to: recipient, symbol: 'ELF', amount: '100000000', memo: '' },
43
+ );
44
+ console.log(`交易 ID: ${result.transactionId}`);
45
+
46
+ // View 调用不需要 signer
47
+ const balance = await callViewMethod(rpcUrl, tokenContract, 'GetBalance', {
48
+ symbol: 'ELF',
49
+ owner: signer.address,
50
+ });
51
+ ```
52
+
53
+ ### EOA 模式
54
+
55
+ ```typescript
56
+ import { createEoaSigner } from '@portkey/aelf-signer';
57
+
58
+ const signer = createEoaSigner(process.env.AELF_PRIVATE_KEY!);
59
+ // signer.address === signer.keyAddress === 钱包地址
60
+ ```
61
+
62
+ ### CA 模式
63
+
64
+ ```typescript
65
+ import { createCaSigner } from '@portkey/aelf-signer';
66
+
67
+ const signer = createCaSigner({
68
+ managerPrivateKey: process.env.PORTKEY_PRIVATE_KEY!,
69
+ caHash: process.env.PORTKEY_CA_HASH!,
70
+ caAddress: process.env.PORTKEY_CA_ADDRESS!,
71
+ // 可选:直接提供以避免 API 查询
72
+ caContractAddress: process.env.PORTKEY_CA_CONTRACT_ADDRESS,
73
+ // 可选:默认为主网
74
+ portkeyApiUrl: 'https://aa-portkey.portkey.finance',
75
+ });
76
+
77
+ // signer.address = CA 地址(链上身份)
78
+ // signer.keyAddress = Manager 地址(签名密钥)
79
+ ```
80
+
81
+ ## 环境变量
82
+
83
+ ### EOA 模式
84
+
85
+ ```bash
86
+ # 设置其一:
87
+ AELF_PRIVATE_KEY=<64位 hex 私钥>
88
+ # 或
89
+ PORTKEY_PRIVATE_KEY=<64位 hex 私钥>
90
+ ```
91
+
92
+ ### CA 模式
93
+
94
+ ```bash
95
+ # 三个都必须设置:
96
+ PORTKEY_PRIVATE_KEY=<Manager 私钥 hex>
97
+ PORTKEY_CA_HASH=<CA hash>
98
+ PORTKEY_CA_ADDRESS=<CA 地址>
99
+
100
+ # 可选:
101
+ PORTKEY_CA_CONTRACT_ADDRESS=<目标链的 CA 合约地址>
102
+ PORTKEY_API_URL=<Portkey API 地址, 默认主网>
103
+ ```
104
+
105
+ 检测优先级:
106
+ 1. 若 `PORTKEY_CA_HASH` + `PORTKEY_CA_ADDRESS` + `PORTKEY_PRIVATE_KEY` 全部设置 -> **CA 模式**
107
+ 2. 若 `AELF_PRIVATE_KEY` 已设置 -> **EOA 模式**
108
+ 3. 若仅 `PORTKEY_PRIVATE_KEY` 已设置 -> **EOA 模式**(降级)
109
+
110
+ ## API 参考
111
+
112
+ ### `AelfSigner` 接口
113
+
114
+ ```typescript
115
+ interface AelfSigner {
116
+ readonly address: string; // 身份地址
117
+ readonly keyAddress: string; // 签名密钥地址
118
+ readonly isCa: boolean; // 是否 CA 模式
119
+
120
+ sendContractCall(
121
+ rpcUrl: string,
122
+ contractAddress: string,
123
+ methodName: string,
124
+ params: Record<string, unknown>,
125
+ ): Promise<SendResult>;
126
+
127
+ signMessage(message: string): string;
128
+ }
129
+ ```
130
+
131
+ - `address`: 链上身份地址。EOA 为钱包地址,CA 为合约账户地址。用于 `owner`、`from` 等字段。
132
+ - `keyAddress`: 实际签名密钥的地址。EOA 等于 `address`,CA 为 Manager 地址。用于 API 认证。
133
+ - `sendContractCall()`: 发送合约写入调用。EOA 直接签名;CA 将参数 protobuf 编码后包装为 `ManagerForwardCall`。
134
+ - `signMessage()`: SHA256 哈希并签名消息。返回 hex 签名。
135
+
136
+ ### 工厂函数
137
+
138
+ | 函数 | 说明 |
139
+ |------|------|
140
+ | `createSignerFromEnv()` | 从环境变量自动检测 signer 类型 |
141
+ | `createEoaSigner(privateKey)` | 创建 EOA signer |
142
+ | `createCaSigner(config)` | 创建 CA signer |
143
+ | `isEoaSigner(signer)` | EoaSigner 类型守卫 |
144
+ | `isCaSigner(signer)` | CaSigner 类型守卫 |
145
+ | `getSigningAddress(signer)` | 获取 `keyAddress` |
146
+
147
+ ### 工具函数
148
+
149
+ | 函数 | 说明 |
150
+ |------|------|
151
+ | `callViewMethod(rpcUrl, addr, method, params?)` | 只读合约调用(无需 signer) |
152
+ | `pollTxResult(rpcUrl, txId)` | 轮询交易结果 |
153
+ | `getAelfInstance(rpcUrl)` | 获取缓存的 AElf SDK 实例 |
154
+ | `fetchChainInfo(apiUrl?)` | 从 Portkey API 获取链信息 |
155
+ | `clearCaches()` | 清除所有缓存(测试用) |
156
+
157
+ ## DApp Skill 开发者指南
158
+
159
+ ### 改造前(仅支持钱包)
160
+
161
+ ```typescript
162
+ // awaken-agent-skills/src/core/trade.ts(改造前)
163
+ export async function executeSwap(config, wallet: any, params) {
164
+ const account = wallet.address; // 只支持 EOA
165
+ await approveToken(config, wallet, ...);
166
+ await callSendMethod(rpcUrl, contract, 'Swap', wallet, args);
167
+ }
168
+ ```
169
+
170
+ ### 改造后(兼容 signer)
171
+
172
+ ```typescript
173
+ // awaken-agent-skills/src/core/trade.ts(改造后)
174
+ import type { AelfSigner } from '@portkey/aelf-signer';
175
+
176
+ export async function executeSwap(config, signer: AelfSigner, params) {
177
+ const account = signer.address; // CA 返回 caAddress, EOA 返回 walletAddress
178
+ await signer.sendContractCall(rpcUrl, tokenContract, 'Approve', { ... });
179
+ await signer.sendContractCall(rpcUrl, swapContract, 'Swap', { ... });
180
+ }
181
+ ```
182
+
183
+ ### MCP 服务集成
184
+
185
+ ```typescript
186
+ // awaken-agent-skills/src/mcp/server.ts
187
+ import { createSignerFromEnv } from '@portkey/aelf-signer';
188
+
189
+ // 在 tool handler 中:
190
+ const signer = createSignerFromEnv(); // 自动检测 EOA 或 CA
191
+ const result = await executeSwap(config, signer, params);
192
+ ```
193
+
194
+ ## 架构
195
+
196
+ ```
197
+ @portkey/aelf-signer
198
+ ├── src/
199
+ │ ├── types.ts # AelfSigner 接口, 结果类型
200
+ │ ├── utils.ts # AElf SDK 封装, TX 轮询, view 调用
201
+ │ ├── eoa-signer.ts # EoaSigner: 直接私钥签名
202
+ │ ├── ca-signer.ts # CaSigner: ManagerForwardCall + protobuf 编码
203
+ │ ├── factory.ts # createSignerFromEnv, 类型守卫
204
+ │ └── index.ts # 统一导出
205
+ └── __tests__/
206
+ └── unit/
207
+ └── signer.test.ts
208
+ ```
209
+
210
+ ### CaSigner 工作原理
211
+
212
+ 当调用 `CaSigner.sendContractCall(rpcUrl, contractAddr, method, params)` 时:
213
+
214
+ 1. **解析 CA 合约地址** — 从配置获取或通过 Portkey API 自动发现
215
+ 2. **获取 protobuf 描述** — 目标合约的 `getContractFileDescriptorSet`
216
+ 3. **查找 input 类型** — 在 protobuf schema 中定位方法的输入消息类型
217
+ 4. **编码参数** — 应用 aelf-sdk transform + protobuf 编码为字节
218
+ 5. **调用 ManagerForwardCall** — 在 CA 合约上发起 `{ caHash, contractAddress, methodName, args }`
219
+ 6. **轮询交易结果** — 等待出块,返回结果
220
+
221
+ ## License
222
+
223
+ MIT
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@portkey/aelf-signer",
3
+ "version": "1.0.0",
4
+ "description": "Unified signer interface for aelf blockchain — supports both EOA (direct signing) and CA (Portkey Contract Account via ManagerForwardCall).",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "files": [
11
+ "src/",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "license": "MIT",
19
+ "scripts": {
20
+ "test": "bun test __tests__/",
21
+ "test:unit": "bun test __tests__/unit/"
22
+ },
23
+ "dependencies": {
24
+ "aelf-sdk": "^3.5.1-beta.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/bun": "latest",
28
+ "typescript": "^5.7.0"
29
+ }
30
+ }