@eforest-finance/agent-skills 0.2.0 → 0.3.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/.env.example +9 -1
- package/README.md +26 -2
- package/lib/aelf-client.ts +27 -14
- package/lib/config.ts +6 -1
- package/lib/types.ts +5 -1
- package/openclaw.json +5 -5
- package/package.json +2 -1
- package/src/core/issue.ts +1 -3
- package/src/core/seed.ts +2 -4
- package/src/core/token.ts +2 -4
- package/src/mcp/server.ts +11 -13
package/.env.example
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
# ---- Wallet Configuration (pick ONE mode) ----
|
|
2
|
+
|
|
3
|
+
# Mode 1: EOA (direct private key signing)
|
|
1
4
|
# aelf wallet private key (hex string, 64 chars)
|
|
2
|
-
# Used for signing transactions on aelf blockchain.
|
|
3
5
|
# WARNING: Never commit real private keys to version control.
|
|
4
6
|
AELF_PRIVATE_KEY=your_private_key_here
|
|
5
7
|
|
|
8
|
+
# Mode 2: CA (Portkey Contract Account via ManagerForwardCall)
|
|
9
|
+
# PORTKEY_PRIVATE_KEY=your_manager_private_key
|
|
10
|
+
# PORTKEY_CA_HASH=your_ca_hash
|
|
11
|
+
# PORTKEY_CA_ADDRESS=your_ca_address
|
|
12
|
+
# PORTKEY_ORIGIN_CHAIN_ID=AELF
|
|
13
|
+
|
|
6
14
|
# Optional: override environment (default: mainnet)
|
|
7
15
|
# AELF_ENV=testnet
|
|
8
16
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ AI Agent Kit for aelf token lifecycle on [eForest](https://www.eforest.finance).
|
|
|
13
13
|
### Prerequisites
|
|
14
14
|
|
|
15
15
|
- [Bun](https://bun.sh) >= 1.0
|
|
16
|
-
- An aelf wallet private key
|
|
16
|
+
- An aelf wallet private key (EOA) or Portkey CA wallet credentials
|
|
17
17
|
|
|
18
18
|
### Install
|
|
19
19
|
|
|
@@ -76,6 +76,8 @@ Then edit the generated config to replace `<YOUR_PRIVATE_KEY>` with your actual
|
|
|
76
76
|
|
|
77
77
|
If you prefer manual configuration, add this to your MCP settings:
|
|
78
78
|
|
|
79
|
+
**EOA mode** (direct private key signing):
|
|
80
|
+
|
|
79
81
|
```json
|
|
80
82
|
{
|
|
81
83
|
"mcpServers": {
|
|
@@ -91,6 +93,25 @@ If you prefer manual configuration, add this to your MCP settings:
|
|
|
91
93
|
}
|
|
92
94
|
```
|
|
93
95
|
|
|
96
|
+
**CA mode** (Portkey Contract Account):
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"eforest-token": {
|
|
102
|
+
"command": "bun",
|
|
103
|
+
"args": ["run", "/path/to/eforest-agent-skills/src/mcp/server.ts"],
|
|
104
|
+
"env": {
|
|
105
|
+
"PORTKEY_PRIVATE_KEY": "your_manager_private_key",
|
|
106
|
+
"PORTKEY_CA_HASH": "your_ca_hash",
|
|
107
|
+
"PORTKEY_CA_ADDRESS": "your_ca_address",
|
|
108
|
+
"EFOREST_NETWORK": "mainnet"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
94
115
|
## OpenClaw
|
|
95
116
|
|
|
96
117
|
```bash
|
|
@@ -176,7 +197,10 @@ bun test:integration # Integration tests only
|
|
|
176
197
|
|
|
177
198
|
| Variable | Description | Default |
|
|
178
199
|
|----------|-------------|---------|
|
|
179
|
-
| `AELF_PRIVATE_KEY` | aelf wallet private key
|
|
200
|
+
| `AELF_PRIVATE_KEY` | aelf wallet private key (EOA mode) | — |
|
|
201
|
+
| `PORTKEY_PRIVATE_KEY` | Portkey Manager private key (CA mode) | — |
|
|
202
|
+
| `PORTKEY_CA_HASH` | Portkey CA hash (CA mode) | — |
|
|
203
|
+
| `PORTKEY_CA_ADDRESS` | Portkey CA address (CA mode) | — |
|
|
180
204
|
| `EFOREST_NETWORK` / `AELF_ENV` | `mainnet` or `testnet` | `mainnet` |
|
|
181
205
|
| `EFOREST_API_URL` / `AELF_API_URL` | Backend API URL | auto |
|
|
182
206
|
| `EFOREST_RPC_URL` / `AELF_RPC_URL` | AELF MainChain RPC | auto |
|
package/lib/aelf-client.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import AElf from 'aelf-sdk';
|
|
8
|
+
import type { AelfSigner } from '@portkey/aelf-signer';
|
|
8
9
|
import { TX_POLL_INTERVAL_MS, TX_POLL_MAX_RETRIES } from './types';
|
|
9
10
|
|
|
10
11
|
// ============================================================================
|
|
@@ -23,16 +24,24 @@ export function getWallet(privateKey?: string): any {
|
|
|
23
24
|
const key =
|
|
24
25
|
process.env.AELF_PRIVATE_KEY ||
|
|
25
26
|
process.env.EFOREST_PRIVATE_KEY ||
|
|
27
|
+
process.env.PORTKEY_PRIVATE_KEY ||
|
|
26
28
|
privateKey;
|
|
27
29
|
|
|
28
30
|
if (!key) {
|
|
29
31
|
throw new Error(
|
|
30
|
-
'Private key is required. Set AELF_PRIVATE_KEY env var or pass --private-key.',
|
|
32
|
+
'Private key is required. Set AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY (CA) env var, or pass --private-key.',
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
return AElf.wallet.getWalletByPrivateKey(key);
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
// Singleton view wallet for read-only calls (no real identity needed)
|
|
39
|
+
let _viewWallet: any = null;
|
|
40
|
+
function getViewWallet(): any {
|
|
41
|
+
if (!_viewWallet) _viewWallet = AElf.wallet.createNewWallet();
|
|
42
|
+
return _viewWallet;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
// ============================================================================
|
|
37
46
|
// Contract
|
|
38
47
|
// ============================================================================
|
|
@@ -105,32 +114,36 @@ export async function getTxResult(
|
|
|
105
114
|
// Contract Call Helpers
|
|
106
115
|
// ============================================================================
|
|
107
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Send a state-changing contract call via AelfSigner.
|
|
119
|
+
* Supports both EOA (direct signing) and CA (ManagerForwardCall) transparently.
|
|
120
|
+
*/
|
|
108
121
|
export async function callContractSend(
|
|
109
122
|
rpcUrl: string,
|
|
110
123
|
contractAddress: string,
|
|
111
124
|
methodName: string,
|
|
112
125
|
params: any,
|
|
113
|
-
|
|
126
|
+
signer: AelfSigner,
|
|
114
127
|
): Promise<{ TransactionId: string; txResult: any }> {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`Failed to get TransactionId from ${methodName}. Response: ${JSON.stringify(tx)}`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
await sleep(TX_POLL_INTERVAL_MS);
|
|
124
|
-
return getTxResult(rpcUrl, transactionId);
|
|
128
|
+
const result = await signer.sendContractCall(rpcUrl, contractAddress, methodName, params);
|
|
129
|
+
return {
|
|
130
|
+
TransactionId: result.transactionId,
|
|
131
|
+
txResult: result.txResult,
|
|
132
|
+
};
|
|
125
133
|
}
|
|
126
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Read-only contract call. Wallet parameter is optional — uses an internal
|
|
137
|
+
* ephemeral wallet if not provided.
|
|
138
|
+
*/
|
|
127
139
|
export async function callContractView(
|
|
128
140
|
rpcUrl: string,
|
|
129
141
|
contractAddress: string,
|
|
130
142
|
methodName: string,
|
|
131
143
|
params: any,
|
|
132
|
-
wallet
|
|
144
|
+
wallet?: any,
|
|
133
145
|
): Promise<any> {
|
|
134
|
-
const
|
|
146
|
+
const w = wallet || getViewWallet();
|
|
147
|
+
const contract = await getContractInstance(rpcUrl, contractAddress, w);
|
|
135
148
|
return await contract[methodName].call(params);
|
|
136
149
|
}
|
package/lib/config.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
16
16
|
import { resolve, dirname } from 'path';
|
|
17
17
|
import { fileURLToPath } from 'url';
|
|
18
18
|
|
|
19
|
+
import { createSignerFromEnv } from '@portkey/aelf-signer';
|
|
19
20
|
import type { CmsConfigItems, ResolvedConfig } from './types';
|
|
20
21
|
import { ENV_PRESETS } from './types';
|
|
21
22
|
import { getWallet } from './aelf-client';
|
|
@@ -124,8 +125,11 @@ export async function getNetworkConfig(opts?: {
|
|
|
124
125
|
'',
|
|
125
126
|
};
|
|
126
127
|
|
|
128
|
+
// Unified signer: detects EOA/CA mode from env vars automatically
|
|
129
|
+
const signer = createSignerFromEnv();
|
|
130
|
+
// Raw wallet for API auth (fetchAuthToken needs keyPair for signature)
|
|
127
131
|
const wallet = getWallet(o.privateKey);
|
|
128
|
-
const walletAddress =
|
|
132
|
+
const walletAddress = signer.address;
|
|
129
133
|
|
|
130
134
|
return {
|
|
131
135
|
apiUrl,
|
|
@@ -133,6 +137,7 @@ export async function getNetworkConfig(opts?: {
|
|
|
133
137
|
connectUrl,
|
|
134
138
|
rpcUrls,
|
|
135
139
|
contracts: cmsConfig,
|
|
140
|
+
signer,
|
|
136
141
|
wallet,
|
|
137
142
|
walletAddress,
|
|
138
143
|
};
|
package/lib/types.ts
CHANGED
|
@@ -104,7 +104,11 @@ export interface ResolvedConfig {
|
|
|
104
104
|
connectUrl: string;
|
|
105
105
|
rpcUrls: Record<string, string>;
|
|
106
106
|
contracts: CmsConfigItems;
|
|
107
|
-
|
|
107
|
+
/** Unified signer — supports both EOA and CA wallets. Use for all contract calls. */
|
|
108
|
+
signer: import('@portkey/aelf-signer').AelfSigner;
|
|
109
|
+
/** Raw wallet for API auth (fetchAuthToken). For CA: manager wallet. */
|
|
110
|
+
wallet: any;
|
|
111
|
+
/** Identity address: CA address (CA mode) or wallet address (EOA mode). */
|
|
108
112
|
walletAddress: string;
|
|
109
113
|
}
|
|
110
114
|
|
package/openclaw.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
{
|
|
4
4
|
"name": "aelf-buy-seed",
|
|
5
5
|
"command": "bun run create_token_skill.ts buy-seed --symbol {{symbol}} --issuer {{issuer}} --env {{env}} {{#if force}}--force {{force}}{{/if}} {{#if privateKey}}--private-key {{privateKey}}{{/if}} {{#if dryRun}}--dry-run{{/if}}",
|
|
6
|
-
"description": "Purchase a SEED on the aelf MainChain. A SEED is a prerequisite for creating a token on aelf blockchain. IMPORTANT: By default this tool REFUSES to buy and outputs the SEED price. You MUST first run with --dry-run to check the price, then ASK THE USER if they want to proceed, then re-run with --force to confirm. Workflow: (1) dry-run to see price, (2) show price to user and ask for confirmation, (3) run with --force or --force <maxELF> to execute. --force with no value buys unconditionally. --force 2 buys only if price <= 2 ELF. The full buy flow is: query price -> check ELF balance -> approve ELF -> call SymbolRegister.Buy -> parse SEED symbol from tx logs. IMPORTANT: The output includes 'seedSymbol' (e.g. 'SEED-321') which is the real on-chain SEED identifier. You MUST use this seedSymbol value for the subsequent create-token --seed-symbol parameter, NOT the token symbol name. Requires
|
|
6
|
+
"description": "Purchase a SEED on the aelf MainChain. A SEED is a prerequisite for creating a token on aelf blockchain. IMPORTANT: By default this tool REFUSES to buy and outputs the SEED price. You MUST first run with --dry-run to check the price, then ASK THE USER if they want to proceed, then re-run with --force to confirm. Workflow: (1) dry-run to see price, (2) show price to user and ask for confirmation, (3) run with --force or --force <maxELF> to execute. --force with no value buys unconditionally. --force 2 buys only if price <= 2 ELF. The full buy flow is: query price -> check ELF balance -> approve ELF -> call SymbolRegister.Buy -> parse SEED symbol from tx logs. IMPORTANT: The output includes 'seedSymbol' (e.g. 'SEED-321') which is the real on-chain SEED identifier. You MUST use this seedSymbol value for the subsequent create-token --seed-symbol parameter, NOT the token symbol name. Requires wallet env vars: AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY + PORTKEY_CA_HASH + PORTKEY_CA_ADDRESS (CA). Output: JSON with {success, transactionId, seedSymbol, data} on success, or '[ERROR] ...' with price info on refusal.",
|
|
7
7
|
"working_directory": "scripts/skills",
|
|
8
8
|
"parameters": {
|
|
9
9
|
"symbol": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"privateKey": {
|
|
31
31
|
"type": "string",
|
|
32
32
|
"required": false,
|
|
33
|
-
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY env var instead."
|
|
33
|
+
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY (CA) env var instead."
|
|
34
34
|
},
|
|
35
35
|
"dryRun": {
|
|
36
36
|
"type": "boolean",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
{
|
|
44
44
|
"name": "aelf-create-token",
|
|
45
45
|
"command": "bun run create_token_skill.ts create-token --symbol {{symbol}} --token-name '{{tokenName}}' --seed-symbol {{seedSymbol}} --total-supply {{totalSupply}} --decimals {{decimals}} {{#if issuer}}--issuer {{issuer}}{{/if}} --issue-chain {{issueChain}} {{#if isBurnable}}--is-burnable{{else}}--no-is-burnable{{/if}} {{#if tokenImage}}--token-image '{{tokenImage}}'{{/if}} --env {{env}} {{#if privateKey}}--private-key {{privateKey}}{{/if}} {{#if dryRun}}--dry-run{{/if}}",
|
|
46
|
-
"description": "Create a new fungible token (FT) on the aelf blockchain using an owned SEED. The full flow is: (1) Check and approve SEED allowance for the TokenAdapter contract, (2) Call TokenAdapter.CreateToken on MainChain, (3) Query GetTokenInfo for proxyIssuer, (4) Save token metadata to backend, (5) Sync token to the issue chain (with graceful degradation on timeout). IMPORTANT: --seed-symbol must be the SEED-{number} value from buy-seed output (e.g. 'SEED-321'), NOT the token name. --issuer is OPTIONAL and defaults to your wallet address. NOTE: The on-chain issuer will be a proxy account created by TokenAdapter, NOT the address you pass. The output includes 'proxyIssuer' which is needed by issue-token. Cross-chain sync to side chain may take several minutes; if it times out, the output includes a warning but success=true (token was created on MainChain). Requires
|
|
46
|
+
"description": "Create a new fungible token (FT) on the aelf blockchain using an owned SEED. The full flow is: (1) Check and approve SEED allowance for the TokenAdapter contract, (2) Call TokenAdapter.CreateToken on MainChain, (3) Query GetTokenInfo for proxyIssuer, (4) Save token metadata to backend, (5) Sync token to the issue chain (with graceful degradation on timeout). IMPORTANT: --seed-symbol must be the SEED-{number} value from buy-seed output (e.g. 'SEED-321'), NOT the token name. --issuer is OPTIONAL and defaults to your wallet address. NOTE: The on-chain issuer will be a proxy account created by TokenAdapter, NOT the address you pass. The output includes 'proxyIssuer' which is needed by issue-token. Cross-chain sync to side chain may take several minutes; if it times out, the output includes a warning but success=true (token was created on MainChain). Requires wallet env vars: AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY + PORTKEY_CA_HASH + PORTKEY_CA_ADDRESS (CA). Output: JSON with {success, transactionId, proxyIssuer, crossChainSynced, data} on success.",
|
|
47
47
|
"working_directory": "scripts/skills",
|
|
48
48
|
"parameters": {
|
|
49
49
|
"symbol": {
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"privateKey": {
|
|
102
102
|
"type": "string",
|
|
103
103
|
"required": false,
|
|
104
|
-
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY env var instead."
|
|
104
|
+
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY (CA) env var instead."
|
|
105
105
|
},
|
|
106
106
|
"dryRun": {
|
|
107
107
|
"type": "boolean",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"privateKey": {
|
|
157
157
|
"type": "string",
|
|
158
158
|
"required": false,
|
|
159
|
-
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY env var instead."
|
|
159
|
+
"description": "aelf wallet private key. Prefer using AELF_PRIVATE_KEY (EOA) or PORTKEY_PRIVATE_KEY (CA) env var instead."
|
|
160
160
|
},
|
|
161
161
|
"dryRun": {
|
|
162
162
|
"type": "boolean",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eforest-finance/agent-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "AI Agent Kit for aelf token lifecycle on eForest: buy-seed, create-token, issue-token. Supports CLI, MCP, and SDK.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
43
|
+
"@portkey/aelf-signer": "^1.0.0",
|
|
43
44
|
"aelf-sdk": "^3.2.44",
|
|
44
45
|
"axios": "^1.7.0",
|
|
45
46
|
"commander": "^12.1.0",
|
package/src/core/issue.ts
CHANGED
|
@@ -133,7 +133,6 @@ export async function issueToken(
|
|
|
133
133
|
multiTokenAddr,
|
|
134
134
|
'GetTokenInfo',
|
|
135
135
|
{ symbol: opts.symbol },
|
|
136
|
-
config.wallet,
|
|
137
136
|
);
|
|
138
137
|
proxyIssuerAddress =
|
|
139
138
|
typeof tokenInfo?.issuer === 'string' ? tokenInfo.issuer : '';
|
|
@@ -151,7 +150,6 @@ export async function issueToken(
|
|
|
151
150
|
proxyAddr,
|
|
152
151
|
'GetProxyAccountByProxyAccountAddress',
|
|
153
152
|
proxyIssuerAddress,
|
|
154
|
-
config.wallet,
|
|
155
153
|
);
|
|
156
154
|
|
|
157
155
|
const proxyAccountHash = proxyAccountResult?.proxyAccountHash;
|
|
@@ -175,7 +173,7 @@ export async function issueToken(
|
|
|
175
173
|
methodName: 'Issue',
|
|
176
174
|
args: Buffer.from(encodedArgs).toString('base64'),
|
|
177
175
|
},
|
|
178
|
-
config.
|
|
176
|
+
config.signer,
|
|
179
177
|
);
|
|
180
178
|
|
|
181
179
|
return {
|
package/src/core/seed.ts
CHANGED
|
@@ -170,7 +170,6 @@ export async function buySeed(
|
|
|
170
170
|
multiTokenAddr,
|
|
171
171
|
'GetBalance',
|
|
172
172
|
{ symbol: 'ELF', owner: config.walletAddress },
|
|
173
|
-
config.wallet,
|
|
174
173
|
);
|
|
175
174
|
const balanceELF =
|
|
176
175
|
Number(balance?.balance ?? 0) / 10 ** ELF_DECIMALS;
|
|
@@ -193,7 +192,6 @@ export async function buySeed(
|
|
|
193
192
|
owner: config.walletAddress,
|
|
194
193
|
spender: contractAddr,
|
|
195
194
|
},
|
|
196
|
-
config.wallet,
|
|
197
195
|
);
|
|
198
196
|
if (Number(allowance?.allowance ?? 0) < priceInSmallUnits) {
|
|
199
197
|
await callContractSend(
|
|
@@ -205,7 +203,7 @@ export async function buySeed(
|
|
|
205
203
|
symbol: 'ELF',
|
|
206
204
|
amount: String(priceInSmallUnits),
|
|
207
205
|
},
|
|
208
|
-
config.
|
|
206
|
+
config.signer,
|
|
209
207
|
);
|
|
210
208
|
}
|
|
211
209
|
}
|
|
@@ -216,7 +214,7 @@ export async function buySeed(
|
|
|
216
214
|
contractAddr,
|
|
217
215
|
'Buy',
|
|
218
216
|
{ symbol: params.symbol, issueTo: params.issueTo },
|
|
219
|
-
config.
|
|
217
|
+
config.signer,
|
|
220
218
|
);
|
|
221
219
|
|
|
222
220
|
const seedSymbol = parseSeedSymbolFromLogs(result.txResult?.Logs);
|
package/src/core/token.ts
CHANGED
|
@@ -127,7 +127,6 @@ export async function createToken(
|
|
|
127
127
|
owner: config.walletAddress,
|
|
128
128
|
spender: tokenAdapterAddr,
|
|
129
129
|
},
|
|
130
|
-
config.wallet,
|
|
131
130
|
);
|
|
132
131
|
|
|
133
132
|
// Approve if needed
|
|
@@ -141,7 +140,7 @@ export async function createToken(
|
|
|
141
140
|
symbol: opts.seedSymbol,
|
|
142
141
|
amount: '1',
|
|
143
142
|
},
|
|
144
|
-
config.
|
|
143
|
+
config.signer,
|
|
145
144
|
);
|
|
146
145
|
}
|
|
147
146
|
|
|
@@ -151,7 +150,7 @@ export async function createToken(
|
|
|
151
150
|
tokenAdapterAddr,
|
|
152
151
|
'CreateToken',
|
|
153
152
|
createParams,
|
|
154
|
-
config.
|
|
153
|
+
config.signer,
|
|
155
154
|
);
|
|
156
155
|
|
|
157
156
|
const createTxId = createResult.TransactionId;
|
|
@@ -164,7 +163,6 @@ export async function createToken(
|
|
|
164
163
|
multiTokenAddr,
|
|
165
164
|
'GetTokenInfo',
|
|
166
165
|
{ symbol: opts.symbol },
|
|
167
|
-
config.wallet,
|
|
168
166
|
);
|
|
169
167
|
proxyIssuer =
|
|
170
168
|
typeof tokenInfo?.issuer === 'string' ? tokenInfo.issuer : '';
|
package/src/mcp/server.ts
CHANGED
|
@@ -4,22 +4,18 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Registers core functions as Model Context Protocol (MCP) tools.
|
|
6
6
|
* Each tool maps to a core function with Zod input validation.
|
|
7
|
+
* Supports both EOA and CA (Portkey) wallets via @portkey/aelf-signer.
|
|
7
8
|
*
|
|
8
9
|
* Usage:
|
|
9
10
|
* bun run src/mcp/server.ts # stdio transport (default)
|
|
10
11
|
* EFOREST_NETWORK=testnet bun run src/mcp/server.ts
|
|
11
12
|
*
|
|
12
|
-
* MCP config example
|
|
13
|
-
* {
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* "cwd": "<path-to-skills-dir>",
|
|
19
|
-
* "env": { "AELF_PRIVATE_KEY": "xxx", "EFOREST_NETWORK": "mainnet" }
|
|
20
|
-
* }
|
|
21
|
-
* }
|
|
22
|
-
* }
|
|
13
|
+
* MCP config example — EOA mode:
|
|
14
|
+
* { "env": { "AELF_PRIVATE_KEY": "xxx", "EFOREST_NETWORK": "mainnet" } }
|
|
15
|
+
*
|
|
16
|
+
* MCP config example — CA (Portkey) mode:
|
|
17
|
+
* { "env": { "PORTKEY_PRIVATE_KEY": "xxx", "PORTKEY_CA_HASH": "xxx",
|
|
18
|
+
* "PORTKEY_CA_ADDRESS": "xxx", "EFOREST_NETWORK": "mainnet" } }
|
|
23
19
|
*/
|
|
24
20
|
|
|
25
21
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
@@ -72,7 +68,8 @@ const server = new McpServer({
|
|
|
72
68
|
// --- aelf-buy-seed ---
|
|
73
69
|
server.tool(
|
|
74
70
|
'aelf-buy-seed',
|
|
75
|
-
`Purchase a SEED on aelf MainChain.
|
|
71
|
+
`Purchase a SEED on aelf MainChain. Supports both EOA and CA (Portkey) wallets.
|
|
72
|
+
Returns seedSymbol (e.g. "SEED-321") needed for create-token.
|
|
76
73
|
Performs pre-flight availability check, ELF balance check, and Approve before Buy.
|
|
77
74
|
Price safety: requires --force or force param. Use force=2 for max 2 ELF.`,
|
|
78
75
|
{
|
|
@@ -120,6 +117,7 @@ Price safety: requires --force or force param. Use force=2 for max 2 ELF.`,
|
|
|
120
117
|
server.tool(
|
|
121
118
|
'aelf-create-token',
|
|
122
119
|
`Create a new FT token on aelf using an owned SEED (from buy-seed output seedSymbol).
|
|
120
|
+
Supports both EOA and CA (Portkey) wallets.
|
|
123
121
|
Handles SEED Approve, TokenAdapter.CreateToken, backend save, and cross-chain sync.
|
|
124
122
|
Returns proxyIssuer (proxy account address) needed for issue-token.
|
|
125
123
|
Cross-chain sync has graceful degradation: success=true even if sync times out.`,
|
|
@@ -172,7 +170,7 @@ Cross-chain sync has graceful degradation: success=true even if sync times out.`
|
|
|
172
170
|
// --- aelf-issue-token ---
|
|
173
171
|
server.tool(
|
|
174
172
|
'aelf-issue-token',
|
|
175
|
-
`Issue tokens to an address via Proxy ForwardCall.
|
|
173
|
+
`Issue tokens to an address via Proxy ForwardCall. Supports both EOA and CA (Portkey) wallets.
|
|
176
174
|
Because TokenAdapter creates a proxy account as on-chain issuer, this routes through ProxyContract.
|
|
177
175
|
Auto-detects proxyIssuer from GetTokenInfo if not provided.
|
|
178
176
|
Steps: GetTokenInfo → GetProxyAccount → encode IssueInput → ForwardCall.`,
|