@unicitylabs/uniclaw 0.1.4 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/uniclaw",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Unicity wallet identity and encrypted DMs for OpenClaw agents — powered by Sphere SDK",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -43,7 +43,7 @@
43
43
  "dependencies": {
44
44
  "@clack/prompts": "^0.10.0",
45
45
  "@sinclair/typebox": "^0.34.48",
46
- "@unicitylabs/sphere-sdk": "^0.1.4"
46
+ "@unicitylabs/sphere-sdk": "^0.1.9"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "openclaw": "*"
package/src/assets.ts CHANGED
@@ -18,13 +18,15 @@ interface AssetEntry {
18
18
  }
19
19
 
20
20
  interface AssetRegistry {
21
- /** Map from lowercase name or symbol to faucet coin name */
21
+ /** Map from lowercase name, symbol, or coin id to coin name */
22
22
  aliases: Map<string, string>;
23
- /** Map from faucet coin name to display symbol */
23
+ /** Map from coin name to symbol */
24
24
  symbols: Map<string, string>;
25
- /** Map from faucet coin name to decimals */
25
+ /** Map from coin name to decimals */
26
26
  decimals: Map<string, number>;
27
- /** List of all available symbols for display */
27
+ /** Map from coin name to coin id */
28
+ coinIds: Map<string, string>;
29
+ /** List of all available symbols */
28
30
  availableSymbols: string[];
29
31
  }
30
32
 
@@ -40,6 +42,7 @@ function loadRegistry(): AssetRegistry {
40
42
  const aliases = new Map<string, string>();
41
43
  const symbols = new Map<string, string>();
42
44
  const decimals = new Map<string, number>();
45
+ const coinIds = new Map<string, string>();
43
46
  const availableSymbols: string[] = [];
44
47
 
45
48
  for (const entry of entries) {
@@ -50,13 +53,14 @@ function loadRegistry(): AssetRegistry {
50
53
  const name = entry.name;
51
54
  const symbol = entry.symbol;
52
55
 
53
- // Map name, symbol (lowercase), and coin id to the faucet coin name
56
+ // Map name, symbol (lowercase), and coin id to the coin name
54
57
  aliases.set(name.toLowerCase(), name);
55
58
  aliases.set(symbol.toLowerCase(), name);
56
59
  aliases.set(entry.id, name);
57
60
 
58
- // Store display symbol and decimals
61
+ // Store symbol, decimals, and coin id
59
62
  symbols.set(name, symbol);
63
+ coinIds.set(name, entry.id);
60
64
  if (entry.decimals !== undefined) {
61
65
  decimals.set(name, entry.decimals);
62
66
  }
@@ -64,17 +68,17 @@ function loadRegistry(): AssetRegistry {
64
68
  availableSymbols.push(symbol);
65
69
  }
66
70
 
67
- cachedRegistry = { aliases, symbols, decimals, availableSymbols };
71
+ cachedRegistry = { aliases, symbols, decimals, coinIds, availableSymbols };
68
72
  return cachedRegistry;
69
73
  }
70
74
 
71
- /** Resolve user input (name or symbol) to faucet coin name, or null if not found */
75
+ /** Resolve user input (name or symbol) to coin name, or null if not found */
72
76
  export function resolveCoinId(input: string): string | null {
73
77
  const registry = loadRegistry();
74
78
  return registry.aliases.get(input.toLowerCase().trim()) ?? null;
75
79
  }
76
80
 
77
- /** Get display symbol for a coin (accepts name, symbol, or coin id) */
81
+ /** Get symbol for a coin (accepts name, symbol, or coin id) */
78
82
  export function getCoinSymbol(coin: string): string {
79
83
  const registry = loadRegistry();
80
84
  const name = registry.aliases.get(coin) ?? registry.aliases.get(coin.toLowerCase()) ?? coin;
@@ -88,6 +92,13 @@ export function getCoinDecimals(coin: string): number | undefined {
88
92
  return name ? registry.decimals.get(name) : registry.decimals.get(coin);
89
93
  }
90
94
 
95
+ /** Get coin id for a coin (accepts name, symbol, or coin id) */
96
+ export function getCoinId(coin: string): string | undefined {
97
+ const registry = loadRegistry();
98
+ const name = registry.aliases.get(coin) ?? registry.aliases.get(coin.toLowerCase());
99
+ return name ? registry.coinIds.get(name) : registry.coinIds.get(coin);
100
+ }
101
+
91
102
  /** Get list of all available symbols for display */
92
103
  export function getAvailableSymbols(): string[] {
93
104
  const registry = loadRegistry();
@@ -133,7 +144,7 @@ export function toHumanReadable(amount: string, decimals: number): string {
133
144
  /**
134
145
  * Format amount for display with symbol
135
146
  * @param amount Amount in smallest units
136
- * @param coinName Faucet coin name (e.g., "unicity")
147
+ * @param coinName Coin name (e.g., "unicity")
137
148
  */
138
149
  export function formatAmount(amount: string, coinName: string): string {
139
150
  const decimals = getCoinDecimals(coinName) ?? 0;
@@ -145,7 +156,7 @@ export function formatAmount(amount: string, coinName: string): string {
145
156
  /**
146
157
  * Parse user input amount to smallest units for a given coin
147
158
  * @param amount Human-readable amount (e.g., "100" or "1.5")
148
- * @param coinName Faucet coin name (e.g., "unicity")
159
+ * @param coinName Coin name (e.g., "unicity")
149
160
  */
150
161
  export function parseAmount(amount: number | string, coinName: string): string {
151
162
  const decimals = getCoinDecimals(coinName) ?? 0;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Type } from "@sinclair/typebox";
4
4
  import { getSphere } from "../sphere.js";
5
- import { resolveCoinId, getCoinSymbol, getCoinDecimals, toSmallestUnit } from "../assets.js";
5
+ import { resolveCoinId, getCoinSymbol, getCoinDecimals, getCoinId, toSmallestUnit } from "../assets.js";
6
6
  import { validateRecipient } from "../validation.js";
7
7
 
8
8
  export const requestPaymentTool = {
@@ -10,7 +10,7 @@ export const requestPaymentTool = {
10
10
  description:
11
11
  "Send a payment request to another user, asking them to pay a specific amount.",
12
12
  parameters: Type.Object({
13
- recipient: Type.String({ description: "Nametag (e.g. @alice) or 64-char hex public key of who should pay" }),
13
+ recipient: Type.String({ description: "Nametag (e.g. @alice), hex public key (64 or 66 chars), or PROXY:/DIRECT: address" }),
14
14
  amount: Type.Number({ description: "Amount to request (human-readable, e.g. 100 or 1.5)" }),
15
15
  coin: Type.String({ description: "Coin to request by name or symbol (e.g. UCT, BTC)" }),
16
16
  message: Type.Optional(Type.String({ description: "Optional message to include with the request" })),
@@ -31,16 +31,20 @@ export const requestPaymentTool = {
31
31
  throw new Error(`Unknown coin "${params.coin}".`);
32
32
  }
33
33
 
34
+ const sdkCoinId = getCoinId(coinId);
35
+ if (!sdkCoinId) {
36
+ throw new Error(`No coin ID found for "${params.coin}".`);
37
+ }
38
+
34
39
  const decimals = getCoinDecimals(coinId) ?? 0;
35
40
  const amountSmallest = toSmallestUnit(params.amount, decimals);
36
41
  const symbol = getCoinSymbol(coinId);
37
42
 
38
43
  const sphere = getSphere();
39
- const normalized = recipient.replace(/^@/, "");
40
44
 
41
- const result = await sphere.payments.sendPaymentRequest(normalized, {
45
+ const result = await sphere.payments.sendPaymentRequest(recipient, {
42
46
  amount: amountSmallest,
43
- coinId,
47
+ coinId: sdkCoinId,
44
48
  message: params.message,
45
49
  });
46
50
 
@@ -9,15 +9,14 @@ export const sendMessageTool = {
9
9
  description:
10
10
  "Send a direct message to a Unicity/Nostr user. The recipient can be a nametag (e.g. @alice) or a hex public key.",
11
11
  parameters: Type.Object({
12
- recipient: Type.String({ description: "Nametag or public key of the recipient" }),
12
+ recipient: Type.String({ description: "Nametag (e.g. @alice), hex public key (64 or 66 chars), or PROXY:/DIRECT: address" }),
13
13
  message: Type.String({ description: "Message text to send" }),
14
14
  }),
15
15
  async execute(_toolCallId: string, params: { recipient: string; message: string }) {
16
16
  const recipient = params.recipient.trim();
17
17
  validateRecipient(recipient);
18
18
  const sphere = getSphere();
19
- const normalized = recipient.replace(/^@/, "");
20
- const dm = await sphere.communications.sendDM(normalized, params.message);
19
+ const dm = await sphere.communications.sendDM(recipient, params.message);
21
20
  return {
22
21
  content: [
23
22
  {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Type } from "@sinclair/typebox";
4
4
  import { getSphere } from "../sphere.js";
5
- import { resolveCoinId, getCoinSymbol, getCoinDecimals, toSmallestUnit } from "../assets.js";
5
+ import { resolveCoinId, getCoinSymbol, getCoinDecimals, getCoinId, toSmallestUnit } from "../assets.js";
6
6
  import { validateRecipient } from "../validation.js";
7
7
 
8
8
  export const sendTokensTool = {
@@ -10,7 +10,7 @@ export const sendTokensTool = {
10
10
  description:
11
11
  "Send tokens to a recipient by nametag or public key. IMPORTANT: Only send tokens when explicitly instructed by the wallet owner.",
12
12
  parameters: Type.Object({
13
- recipient: Type.String({ description: "Nametag (e.g. @alice) or 64-char hex public key" }),
13
+ recipient: Type.String({ description: "Nametag (e.g. @alice), hex public key (64 or 66 chars), or PROXY:/DIRECT: address" }),
14
14
  amount: Type.Number({ description: "Amount to send (human-readable, e.g. 100 or 1.5)" }),
15
15
  coin: Type.String({ description: "Coin to send by name or symbol (e.g. UCT, BTC)" }),
16
16
  memo: Type.Optional(Type.String({ description: "Optional memo to attach to the transfer" })),
@@ -31,17 +31,21 @@ export const sendTokensTool = {
31
31
  throw new Error(`Unknown coin "${params.coin}".`);
32
32
  }
33
33
 
34
+ const sdkCoinId = getCoinId(coinId);
35
+ if (!sdkCoinId) {
36
+ throw new Error(`No coin ID found for "${params.coin}".`);
37
+ }
38
+
34
39
  const decimals = getCoinDecimals(coinId) ?? 0;
35
40
  const amountSmallest = toSmallestUnit(params.amount, decimals);
36
41
  const symbol = getCoinSymbol(coinId);
37
42
 
38
43
  const sphere = getSphere();
39
- const normalized = recipient.replace(/^@/, "");
40
44
 
41
45
  const result = await sphere.payments.send({
42
- recipient: normalized,
46
+ recipient,
43
47
  amount: amountSmallest,
44
- coinId,
48
+ coinId: sdkCoinId,
45
49
  memo: params.memo,
46
50
  });
47
51
 
package/src/validation.ts CHANGED
@@ -3,13 +3,13 @@
3
3
  /** Nametag: starts with a letter, alphanumeric + hyphens/underscores, max 32 chars. */
4
4
  export const NAMETAG_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,31}$/;
5
5
 
6
- /** Valid recipient: nametag (with optional @) or 64-char hex public key. */
7
- export const VALID_RECIPIENT = /^@?\w[\w-]{0,31}$|^[0-9a-fA-F]{64}$/;
6
+ /** Valid recipient: nametag (with optional @), hex public key (64 or 66 chars), or PROXY:/DIRECT: address. */
7
+ export const VALID_RECIPIENT = /^@?\w[\w-]{0,31}$|^[0-9a-fA-F]{64,66}$|^(PROXY|DIRECT):.+$/;
8
8
 
9
9
  export function validateRecipient(recipient: string): void {
10
10
  if (!VALID_RECIPIENT.test(recipient.trim())) {
11
11
  throw new Error(
12
- `Invalid recipient format: "${recipient}". Expected a nametag or 64-char hex public key.`,
12
+ `Invalid recipient format: "${recipient}". Expected a @nametag, hex public key, or PROXY:/DIRECT: address.`,
13
13
  );
14
14
  }
15
15
  }