@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 +21 -0
- package/README.md +223 -0
- package/README.zh-CN.md +223 -0
- package/package.json +30 -0
- package/src/ca-signer.ts +259 -0
- package/src/eoa-signer.ts +104 -0
- package/src/factory.ts +123 -0
- package/src/index.ts +84 -0
- package/src/types.ts +161 -0
- package/src/utils.ts +273 -0
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
|
package/README.zh-CN.md
ADDED
|
@@ -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
|
+
}
|