@elisym/mcp 0.2.3 → 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/README.md +4 -27
- package/dist/index.js +128 -76
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -12,33 +12,6 @@ Enables AI assistants (Claude, Cursor, Windsurf, any MCP-compatible client) to d
|
|
|
12
12
|
|
|
13
13
|
Currently customer-mode only. To run a provider agent, use [`@elisym/cli`](../cli).
|
|
14
14
|
|
|
15
|
-
## Flow
|
|
16
|
-
|
|
17
|
-
```mermaid
|
|
18
|
-
sequenceDiagram
|
|
19
|
-
participant AI as AI Assistant<br/>(Claude/Cursor/Windsurf)
|
|
20
|
-
participant MCP as @elisym/mcp
|
|
21
|
-
participant Nostr as Nostr relays
|
|
22
|
-
participant Prov as Provider agent
|
|
23
|
-
participant Chain as Settlement layer
|
|
24
|
-
|
|
25
|
-
AI->>MCP: find agents by capability
|
|
26
|
-
MCP->>Nostr: NIP-89 discovery query
|
|
27
|
-
Nostr-->>MCP: capability cards
|
|
28
|
-
MCP-->>AI: matching providers
|
|
29
|
-
|
|
30
|
-
AI->>MCP: submit job to <npub>
|
|
31
|
-
MCP->>Nostr: NIP-90 job request (kind 5100)
|
|
32
|
-
Nostr->>Prov: delivered
|
|
33
|
-
Prov->>Nostr: NIP-17 payment request (encrypted)
|
|
34
|
-
Nostr-->>MCP: payment request
|
|
35
|
-
MCP->>Chain: pay provider
|
|
36
|
-
Chain-->>Prov: settled
|
|
37
|
-
Prov->>Nostr: NIP-90 result (kind 6100)
|
|
38
|
-
Nostr-->>MCP: result
|
|
39
|
-
MCP-->>AI: job complete
|
|
40
|
-
```
|
|
41
|
-
|
|
42
15
|
## Install
|
|
43
16
|
|
|
44
17
|
```bash
|
|
@@ -165,6 +138,10 @@ Check my wallet balance
|
|
|
165
138
|
|
|
166
139
|
The assistant will use elisym MCP tools automatically to discover agents, submit jobs, handle payments, and receive results.
|
|
167
140
|
|
|
141
|
+
### Agent Skill (Claude Code, Hermes, OpenClaw)
|
|
142
|
+
|
|
143
|
+
Alongside the MCP server, elisym ships an [agentskills.io](https://agentskills.io)-compatible skill at [`skills/elisym/SKILL.md`](../../skills/elisym/SKILL.md) in the monorepo root. Any agent runtime that reads the agentskills format (Claude Code, Hermes by Nous Research, OpenClaw by Bankr) can drop the file into its skills directory - the skill teaches the agent when to reach for elisym and walks it through discovery, job submission, payment, and result handling on top of this MCP server.
|
|
144
|
+
|
|
168
145
|
## Security
|
|
169
146
|
|
|
170
147
|
`withdraw` and `switch_agent` are gated behind opt-in flags that must be explicitly enabled per-agent:
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { LIMITS, DEFAULT_KIND_OFFSET, ElisymIdentity, ElisymClient, RELAYS, validateAgentName, serializeConfig, toDTag,
|
|
3
|
-
import {
|
|
2
|
+
import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, ElisymIdentity, ElisymClient, RELAYS, validateAgentName, serializeConfig, toDTag, getProtocolProgramId, getProtocolConfig } from '@elisym/sdk';
|
|
3
|
+
import { generateKeyPairSigner, createKeyPairSignerFromBytes, createSolanaRpc, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, isAddress } from '@solana/kit';
|
|
4
4
|
import bs58 from 'bs58';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools';
|
|
@@ -16,6 +16,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSche
|
|
|
16
16
|
import { z, ZodError } from 'zod';
|
|
17
17
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
18
18
|
import { randomBytes } from 'node:crypto';
|
|
19
|
+
import { getTransferSolInstruction } from '@solana-program/system';
|
|
19
20
|
|
|
20
21
|
async function writeFileAtomic(path, content, mode = 384) {
|
|
21
22
|
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
@@ -159,11 +160,16 @@ async function listAgentNames() {
|
|
|
159
160
|
return [];
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
|
|
163
|
-
// src/context.ts
|
|
164
163
|
function rpcUrlFor(network) {
|
|
165
164
|
return network === "mainnet" ? "https://api.mainnet-beta.solana.com" : "https://api.devnet.solana.com";
|
|
166
165
|
}
|
|
166
|
+
async function fetchProtocolConfig(network) {
|
|
167
|
+
const cluster = network === "mainnet" ? "mainnet" : "devnet";
|
|
168
|
+
const programId = getProtocolProgramId(cluster);
|
|
169
|
+
const rpc = createSolanaRpc(rpcUrlFor(cluster));
|
|
170
|
+
const config = await getProtocolConfig(rpc, programId, { forceRefresh: true });
|
|
171
|
+
return { feeBps: config.feeBps, treasury: config.treasury };
|
|
172
|
+
}
|
|
167
173
|
function explorerClusterFor(network) {
|
|
168
174
|
return network === "mainnet" ? "mainnet-beta" : "devnet";
|
|
169
175
|
}
|
|
@@ -606,6 +612,19 @@ function errorResult(text) {
|
|
|
606
612
|
}
|
|
607
613
|
|
|
608
614
|
// src/tools/agent.ts
|
|
615
|
+
async function exportKeyPairBytes(signer) {
|
|
616
|
+
const { privateKey, publicKey } = signer.keyPair;
|
|
617
|
+
const [pkcs8, rawPub] = await Promise.all([
|
|
618
|
+
crypto.subtle.exportKey("pkcs8", privateKey),
|
|
619
|
+
crypto.subtle.exportKey("raw", publicKey)
|
|
620
|
+
]);
|
|
621
|
+
const privateBytes = new Uint8Array(pkcs8).slice(16);
|
|
622
|
+
const publicBytes = new Uint8Array(rawPub);
|
|
623
|
+
const bytes = new Uint8Array(64);
|
|
624
|
+
bytes.set(privateBytes, 0);
|
|
625
|
+
bytes.set(publicBytes, 32);
|
|
626
|
+
return bytes;
|
|
627
|
+
}
|
|
609
628
|
var CreateAgentSchema = z.object({
|
|
610
629
|
name: z.string().min(1).max(64),
|
|
611
630
|
description: z.string().default("Elisym MCP agent"),
|
|
@@ -624,7 +643,7 @@ var ListAgentsSchema = z.object({});
|
|
|
624
643
|
var StopAgentSchema = z.object({
|
|
625
644
|
name: z.string()
|
|
626
645
|
});
|
|
627
|
-
function buildAgentInstance(name, config) {
|
|
646
|
+
async function buildAgentInstance(name, config) {
|
|
628
647
|
let identity;
|
|
629
648
|
if (config.nostrSecretKey.startsWith("nsec")) {
|
|
630
649
|
const decoded = nip19.decode(config.nostrSecretKey);
|
|
@@ -639,10 +658,11 @@ function buildAgentInstance(name, config) {
|
|
|
639
658
|
let solanaKeypair;
|
|
640
659
|
if (config.solanaSecretKey) {
|
|
641
660
|
try {
|
|
642
|
-
const
|
|
661
|
+
const decoded = bs58.decode(config.solanaSecretKey);
|
|
662
|
+
const signer = await createKeyPairSignerFromBytes(decoded);
|
|
643
663
|
solanaKeypair = {
|
|
644
|
-
publicKey:
|
|
645
|
-
secretKey:
|
|
664
|
+
publicKey: signer.address,
|
|
665
|
+
secretKey: decoded
|
|
646
666
|
};
|
|
647
667
|
} catch {
|
|
648
668
|
console.error(`[mcp:warn] Invalid Solana key for agent "${name}" - payments disabled`);
|
|
@@ -693,21 +713,22 @@ var agentTools = [
|
|
|
693
713
|
}
|
|
694
714
|
}
|
|
695
715
|
const nostrSecretKey = generateSecretKey();
|
|
696
|
-
const
|
|
716
|
+
const solanaSigner = await generateKeyPairSigner(true);
|
|
717
|
+
const solanaSecretBytes = await exportKeyPairBytes(solanaSigner);
|
|
697
718
|
const nostrSecretHex = Buffer.from(nostrSecretKey).toString("hex");
|
|
698
|
-
const solanaSecretBase58 = bs58.encode(
|
|
719
|
+
const solanaSecretBase58 = bs58.encode(solanaSecretBytes);
|
|
699
720
|
await saveAgentConfig(input.name, {
|
|
700
721
|
name: input.name,
|
|
701
722
|
description: input.description,
|
|
702
723
|
relays: [...RELAYS],
|
|
703
724
|
nostrSecretKey: nostrSecretHex,
|
|
704
725
|
solanaSecretKey: solanaSecretBase58,
|
|
705
|
-
solanaAddress:
|
|
726
|
+
solanaAddress: solanaSigner.address,
|
|
706
727
|
network: input.network,
|
|
707
728
|
security: { withdrawals_enabled: false, agent_switch_enabled: false },
|
|
708
729
|
passphrase: input.passphrase
|
|
709
730
|
});
|
|
710
|
-
const instance = buildAgentInstance(input.name, {
|
|
731
|
+
const instance = await buildAgentInstance(input.name, {
|
|
711
732
|
nostrSecretKey: nostrSecretHex,
|
|
712
733
|
solanaSecretKey: solanaSecretBase58,
|
|
713
734
|
network: input.network,
|
|
@@ -717,7 +738,7 @@ var agentTools = [
|
|
|
717
738
|
return textResult(
|
|
718
739
|
`Agent "${input.name}" created.
|
|
719
740
|
Nostr: ${instance.identity.npub}
|
|
720
|
-
Solana: ${
|
|
741
|
+
Solana: ${solanaSigner.address}
|
|
721
742
|
` + (input.activate ? "Activated as current agent." : "")
|
|
722
743
|
);
|
|
723
744
|
}
|
|
@@ -762,7 +783,7 @@ Solana: ${solanaKeypair.publicKey.toBase58()}
|
|
|
762
783
|
}
|
|
763
784
|
try {
|
|
764
785
|
const config = await loadAgentConfig(input.name);
|
|
765
|
-
const instance = buildAgentInstance(input.name, config);
|
|
786
|
+
const instance = await buildAgentInstance(input.name, config);
|
|
766
787
|
ctx.register(instance, true);
|
|
767
788
|
const npub = instance.identity.npub;
|
|
768
789
|
return textResult(`Loaded and switched to agent "${input.name}" (${npub}).`);
|
|
@@ -1102,6 +1123,10 @@ function providerSolanaAddress(provider, dTag) {
|
|
|
1102
1123
|
}
|
|
1103
1124
|
return void 0;
|
|
1104
1125
|
}
|
|
1126
|
+
function wsUrlFor(httpUrl) {
|
|
1127
|
+
return httpUrl.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
|
|
1128
|
+
}
|
|
1129
|
+
var paymentStrategy = new SolanaPaymentStrategy();
|
|
1105
1130
|
async function executePaymentFlow(agent, paymentRequest, jobId, providerPubkey, expectedRecipient) {
|
|
1106
1131
|
let requestData;
|
|
1107
1132
|
try {
|
|
@@ -1109,25 +1134,30 @@ async function executePaymentFlow(agent, paymentRequest, jobId, providerPubkey,
|
|
|
1109
1134
|
} catch {
|
|
1110
1135
|
throw new Error("Provider sent a malformed payment_request (not valid JSON).");
|
|
1111
1136
|
}
|
|
1112
|
-
const
|
|
1137
|
+
const protocolConfig = await fetchProtocolConfig(agent.network);
|
|
1138
|
+
const validation = payment().validatePaymentRequest(
|
|
1139
|
+
paymentRequest,
|
|
1140
|
+
protocolConfig,
|
|
1141
|
+
expectedRecipient
|
|
1142
|
+
);
|
|
1113
1143
|
if (validation !== null) {
|
|
1114
1144
|
throw new Error(`Payment validation failed: ${validation.message}`);
|
|
1115
1145
|
}
|
|
1116
1146
|
if (!agent.solanaKeypair) {
|
|
1117
1147
|
throw new Error("Solana payments not configured for this agent.");
|
|
1118
1148
|
}
|
|
1119
|
-
const
|
|
1120
|
-
const
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
);
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
const signature = await sendAndConfirmTransaction(connection, tx, [keypair], {
|
|
1128
|
-
commitment: "confirmed",
|
|
1129
|
-
preflightCommitment: "confirmed"
|
|
1149
|
+
const signer = await createKeyPairSignerFromBytes(agent.solanaKeypair.secretKey);
|
|
1150
|
+
const httpUrl = rpcUrlFor(agent.network);
|
|
1151
|
+
const rpc = createSolanaRpc(httpUrl);
|
|
1152
|
+
const signedTx = await paymentStrategy.buildTransaction(requestData, signer, rpc, protocolConfig);
|
|
1153
|
+
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrlFor(httpUrl));
|
|
1154
|
+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
|
|
1155
|
+
await sendAndConfirm(signedTx, {
|
|
1156
|
+
commitment: "confirmed"
|
|
1130
1157
|
});
|
|
1158
|
+
const signature = getSignatureFromTransaction(
|
|
1159
|
+
signedTx
|
|
1160
|
+
);
|
|
1131
1161
|
try {
|
|
1132
1162
|
await agent.client.marketplace.submitPaymentConfirmation(
|
|
1133
1163
|
agent.identity,
|
|
@@ -1930,26 +1960,28 @@ var SendPaymentSchema = z.object({
|
|
|
1930
1960
|
expected_solana_recipient: z.string().describe("Base58 Solana address you expect to receive the payment (from the provider card).")
|
|
1931
1961
|
});
|
|
1932
1962
|
var WithdrawSchema = z.object({
|
|
1933
|
-
address: z.string().describe("Destination Solana address (base58). Must be a valid
|
|
1963
|
+
address: z.string().describe("Destination Solana address (base58). Must be a valid address."),
|
|
1934
1964
|
amount_sol: z.string().describe('Amount in SOL as a decimal string (e.g. "0.5"), or the literal "all".'),
|
|
1935
1965
|
nonce: z.string().optional().describe("Confirmation nonce from a previous preview call. Omit to request a preview.")
|
|
1936
1966
|
});
|
|
1937
|
-
function
|
|
1938
|
-
return
|
|
1967
|
+
async function agentSigner(secretKey) {
|
|
1968
|
+
return createKeyPairSignerFromBytes(secretKey);
|
|
1969
|
+
}
|
|
1970
|
+
function rpcFor(agent) {
|
|
1971
|
+
return createSolanaRpc(rpcUrlFor(agent.network));
|
|
1939
1972
|
}
|
|
1940
|
-
function
|
|
1941
|
-
return
|
|
1973
|
+
function wsUrlFor2(httpUrl) {
|
|
1974
|
+
return httpUrl.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
|
|
1942
1975
|
}
|
|
1943
1976
|
function explorerUrl(agent, signature) {
|
|
1944
1977
|
return `https://explorer.solana.com/tx/${signature}?cluster=${explorerClusterFor(agent.network)}`;
|
|
1945
1978
|
}
|
|
1946
1979
|
function assertSolanaAddress(field, value) {
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
} catch {
|
|
1950
|
-
throw new Error(`${field} is not a valid Solana address (base58 PublicKey).`);
|
|
1980
|
+
if (!isAddress(value)) {
|
|
1981
|
+
throw new Error(`${field} is not a valid Solana address.`);
|
|
1951
1982
|
}
|
|
1952
1983
|
}
|
|
1984
|
+
var paymentStrategy2 = new SolanaPaymentStrategy();
|
|
1953
1985
|
var walletTools = [
|
|
1954
1986
|
defineTool({
|
|
1955
1987
|
name: "get_balance",
|
|
@@ -1961,9 +1993,10 @@ var walletTools = [
|
|
|
1961
1993
|
if (!agent.solanaKeypair) {
|
|
1962
1994
|
return errorResult("Solana payments not configured for this agent.");
|
|
1963
1995
|
}
|
|
1964
|
-
const
|
|
1965
|
-
const
|
|
1966
|
-
const
|
|
1996
|
+
const rpc = rpcFor(agent);
|
|
1997
|
+
const walletAddress = address(agent.solanaKeypair.publicKey);
|
|
1998
|
+
const { value: balanceLamports } = await rpc.getBalance(walletAddress).send();
|
|
1999
|
+
const balance = Number(balanceLamports);
|
|
1967
2000
|
return textResult(
|
|
1968
2001
|
`Address: ${agent.solanaKeypair.publicKey}
|
|
1969
2002
|
Network: ${agent.network}
|
|
@@ -1994,32 +2027,39 @@ Balance: ${formatSol(BigInt(balance))} (${balance} lamports)`
|
|
|
1994
2027
|
} catch {
|
|
1995
2028
|
return errorResult("Malformed payment_request: not valid JSON.");
|
|
1996
2029
|
}
|
|
2030
|
+
const protocolConfig = await fetchProtocolConfig(agent.network);
|
|
1997
2031
|
const validation = payment().validatePaymentRequest(
|
|
1998
2032
|
input.payment_request,
|
|
2033
|
+
protocolConfig,
|
|
1999
2034
|
input.expected_solana_recipient
|
|
2000
2035
|
);
|
|
2001
2036
|
if (validation !== null) {
|
|
2002
2037
|
return errorResult(`Payment validation failed: ${validation.message}`);
|
|
2003
2038
|
}
|
|
2004
|
-
const
|
|
2005
|
-
const
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2039
|
+
const signer = await agentSigner(agent.solanaKeypair.secretKey);
|
|
2040
|
+
const rpc = rpcFor(agent);
|
|
2041
|
+
const signedTx = await paymentStrategy2.buildTransaction(
|
|
2042
|
+
requestData,
|
|
2043
|
+
signer,
|
|
2044
|
+
rpc,
|
|
2045
|
+
protocolConfig
|
|
2009
2046
|
);
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
|
|
2047
|
+
const httpUrl = rpcUrlFor(agent.network);
|
|
2048
|
+
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrlFor2(httpUrl));
|
|
2049
|
+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
|
|
2050
|
+
await sendAndConfirm(signedTx, {
|
|
2051
|
+
commitment: "confirmed"
|
|
2015
2052
|
});
|
|
2016
|
-
const
|
|
2053
|
+
const signature = getSignatureFromTransaction(
|
|
2054
|
+
signedTx
|
|
2055
|
+
);
|
|
2056
|
+
const { value: balanceLamports } = await rpc.getBalance(address(agent.solanaKeypair.publicKey)).send();
|
|
2017
2057
|
return textResult(
|
|
2018
2058
|
`Payment sent.
|
|
2019
2059
|
Signature: ${signature}
|
|
2020
2060
|
Amount: ${formatSol(BigInt(requestData.amount))}
|
|
2021
2061
|
Recipient: ${requestData.recipient}
|
|
2022
|
-
Remaining balance: ${formatSol(
|
|
2062
|
+
Remaining balance: ${formatSol(balanceLamports)}
|
|
2023
2063
|
Explorer: ${explorerUrl(agent, signature)}`
|
|
2024
2064
|
);
|
|
2025
2065
|
}
|
|
@@ -2055,15 +2095,15 @@ Balance: ${formatSol(BigInt(balance))} (${balance} lamports)`
|
|
|
2055
2095
|
`Withdrawals are disabled for agent "${agent.name}". Enable with: elisym-mcp enable-withdrawals ${agent.name}`
|
|
2056
2096
|
);
|
|
2057
2097
|
}
|
|
2058
|
-
let destination;
|
|
2059
2098
|
try {
|
|
2060
|
-
|
|
2099
|
+
assertSolanaAddress("address", input.address);
|
|
2061
2100
|
} catch (e) {
|
|
2062
2101
|
return errorResult(e instanceof Error ? e.message : String(e));
|
|
2063
2102
|
}
|
|
2064
|
-
const
|
|
2065
|
-
const
|
|
2066
|
-
const
|
|
2103
|
+
const signer = await agentSigner(agent.solanaKeypair.secretKey);
|
|
2104
|
+
const rpc = rpcFor(agent);
|
|
2105
|
+
const { value: balanceLamports } = await rpc.getBalance(address(agent.solanaKeypair.publicKey)).send();
|
|
2106
|
+
const balance = balanceLamports;
|
|
2067
2107
|
const TX_FEE_RESERVE = 5000n;
|
|
2068
2108
|
let lamports;
|
|
2069
2109
|
try {
|
|
@@ -2115,26 +2155,36 @@ To execute, call withdraw again with the SAME address and amount_sol, plus nonce
|
|
|
2115
2155
|
"Nonce does not match the current {agent, address, amount}. Re-run the preview step."
|
|
2116
2156
|
);
|
|
2117
2157
|
}
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2158
|
+
const destination = address(input.address);
|
|
2159
|
+
const transferIx = getTransferSolInstruction({
|
|
2160
|
+
source: signer,
|
|
2161
|
+
destination,
|
|
2162
|
+
amount: lamports
|
|
2163
|
+
});
|
|
2164
|
+
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
2165
|
+
const message = pipe(
|
|
2166
|
+
createTransactionMessage({ version: 0 }),
|
|
2167
|
+
(msg) => setTransactionMessageFeePayerSigner(signer, msg),
|
|
2168
|
+
(msg) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, msg),
|
|
2169
|
+
(msg) => appendTransactionMessageInstructions([transferIx], msg)
|
|
2124
2170
|
);
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2171
|
+
const signedTx = await signTransactionMessageWithSigners(message);
|
|
2172
|
+
const httpUrl = rpcUrlFor(agent.network);
|
|
2173
|
+
const rpcSubscriptions = createSolanaRpcSubscriptions(wsUrlFor2(httpUrl));
|
|
2174
|
+
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
|
|
2175
|
+
await sendAndConfirm(signedTx, {
|
|
2176
|
+
commitment: "confirmed"
|
|
2130
2177
|
});
|
|
2131
|
-
const
|
|
2178
|
+
const signature = getSignatureFromTransaction(
|
|
2179
|
+
signedTx
|
|
2180
|
+
);
|
|
2181
|
+
const { value: newBalanceLamports } = await rpc.getBalance(address(agent.solanaKeypair.publicKey)).send();
|
|
2132
2182
|
return textResult(
|
|
2133
2183
|
`Withdrawal complete.
|
|
2134
2184
|
Signature: ${signature}
|
|
2135
2185
|
Amount: ${formatSol(lamports)}
|
|
2136
2186
|
Destination: ${input.address}
|
|
2137
|
-
New balance: ${formatSol(
|
|
2187
|
+
New balance: ${formatSol(newBalanceLamports)}
|
|
2138
2188
|
Explorer: ${explorerUrl(agent, signature)}`
|
|
2139
2189
|
);
|
|
2140
2190
|
}
|
|
@@ -2270,8 +2320,9 @@ async function startServer(ctx) {
|
|
|
2270
2320
|
if (!agent.solanaKeypair) {
|
|
2271
2321
|
throw new Error("Solana payments not configured");
|
|
2272
2322
|
}
|
|
2273
|
-
const
|
|
2274
|
-
const
|
|
2323
|
+
const rpc = createSolanaRpc(rpcUrlFor(agent.network));
|
|
2324
|
+
const { value: balanceLamports } = await rpc.getBalance(address(agent.solanaKeypair.publicKey)).send();
|
|
2325
|
+
const balance = Number(balanceLamports);
|
|
2275
2326
|
return {
|
|
2276
2327
|
contents: [
|
|
2277
2328
|
{
|
|
@@ -2343,7 +2394,7 @@ program.action(async () => {
|
|
|
2343
2394
|
if (agentName) {
|
|
2344
2395
|
try {
|
|
2345
2396
|
const config = await loadAgentConfig(agentName);
|
|
2346
|
-
const instance = buildAgentInstance(agentName, config);
|
|
2397
|
+
const instance = await buildAgentInstance(agentName, config);
|
|
2347
2398
|
ctx.register(instance);
|
|
2348
2399
|
console.error(`Loaded agent: ${agentName}`);
|
|
2349
2400
|
} catch (e) {
|
|
@@ -2373,7 +2424,7 @@ program.action(async () => {
|
|
|
2373
2424
|
const name = names[0];
|
|
2374
2425
|
try {
|
|
2375
2426
|
const config = await loadAgentConfig(name);
|
|
2376
|
-
const instance = buildAgentInstance(name, config);
|
|
2427
|
+
const instance = await buildAgentInstance(name, config);
|
|
2377
2428
|
ctx.register(instance);
|
|
2378
2429
|
console.error(`Loaded default agent: ${name} (${instance.network})`);
|
|
2379
2430
|
} catch (e) {
|
|
@@ -2432,14 +2483,15 @@ program.command("init [name]").description("Create a new agent identity").option
|
|
|
2432
2483
|
]);
|
|
2433
2484
|
const nostrSecretKey = generateSecretKey();
|
|
2434
2485
|
const nostrPubkey = getPublicKey(nostrSecretKey);
|
|
2435
|
-
const
|
|
2486
|
+
const solanaSigner = await generateKeyPairSigner(true);
|
|
2487
|
+
const solanaSecretBytes = await exportKeyPairBytes(solanaSigner);
|
|
2436
2488
|
await saveAgentConfig(name, {
|
|
2437
2489
|
name,
|
|
2438
2490
|
description: options.description,
|
|
2439
2491
|
relays: [...RELAYS],
|
|
2440
2492
|
nostrSecretKey: Buffer.from(nostrSecretKey).toString("hex"),
|
|
2441
|
-
solanaSecretKey: bs58.encode(
|
|
2442
|
-
solanaAddress:
|
|
2493
|
+
solanaSecretKey: bs58.encode(solanaSecretBytes),
|
|
2494
|
+
solanaAddress: solanaSigner.address,
|
|
2443
2495
|
network: options.network,
|
|
2444
2496
|
security: { withdrawals_enabled: false, agent_switch_enabled: false },
|
|
2445
2497
|
passphrase: passphrase || void 0
|
|
@@ -2447,7 +2499,7 @@ program.command("init [name]").description("Create a new agent identity").option
|
|
|
2447
2499
|
const npub = nip19.npubEncode(nostrPubkey);
|
|
2448
2500
|
console.log(`Agent "${name}" created.`);
|
|
2449
2501
|
console.log(` Nostr: ${npub}`);
|
|
2450
|
-
console.log(` Solana: ${
|
|
2502
|
+
console.log(` Solana: ${solanaSigner.address}`);
|
|
2451
2503
|
console.log(` Network: ${options.network}`);
|
|
2452
2504
|
console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
|
|
2453
2505
|
console.log(` Config: ~/.elisym/agents/${name}/config.json`);
|