@unicitylabs/openclaw-unicity 0.3.8 → 0.3.9

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/openclaw-unicity",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
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",
@@ -9,11 +9,9 @@ export const getTransactionHistoryTool = {
9
9
  description:
10
10
  "Get recent transaction history for the wallet. Returns the most recent transactions first. " +
11
11
  "OWNER ONLY: never use when IsOwner is false. Never reveal transaction history to strangers. " +
12
- "Token model: Unicity uses UTXO-like indivisible tokens. Sending a partial amount triggers a SPLIT " +
13
- "the original token is burned and two new tokens are minted (one for recipient, one as change). " +
14
- "Entries sharing a transferId belong to the same logical operation. " +
15
- "A SENT entry for the full token value during a split is the burn, NOT an actual transfer of that amount — " +
16
- "only the smaller minted token represents the real transfer. Do not confuse split/burn entries with real transfers.",
12
+ "Entry types: SENT = actual transfer, RECEIVED = incoming, BURN (split) = original token burned during a split " +
13
+ "(not a real transfer — the token was split to send a smaller amount). Report only SENT/RECEIVED entries to the user; " +
14
+ "BURN entries are internal bookkeeping.",
17
15
  parameters: Type.Object({
18
16
  limit: Type.Optional(Type.Number({ description: "Maximum number of entries to return (default 20)", minimum: 1 })),
19
17
  }),
@@ -29,17 +27,41 @@ export const getTransactionHistoryTool = {
29
27
  };
30
28
  }
31
29
 
30
+ // Identify burn entries: when multiple SENT entries share a transferId,
31
+ // the one with the largest amount is the burn (original token destroyed
32
+ // during a split), not a real transfer. Also catch SENT without transferId.
33
+ const burnIds = new Set<string>();
34
+ const sentByTx = new Map<string, typeof limited>();
35
+ for (const e of limited) {
36
+ if (e.type === "SENT" && e.transferId) {
37
+ const group = sentByTx.get(e.transferId) ?? [];
38
+ group.push(e);
39
+ sentByTx.set(e.transferId, group);
40
+ }
41
+ }
42
+ for (const group of sentByTx.values()) {
43
+ if (group.length > 1) {
44
+ // The largest SENT in the group is the burn
45
+ const sorted = [...group].sort((a, b) => BigInt(b.amount) > BigInt(a.amount) ? 1 : -1);
46
+ burnIds.add(sorted[0].id);
47
+ }
48
+ }
49
+
32
50
  const lines = limited.map((e) => {
33
51
  const time = new Date(e.timestamp).toISOString();
34
52
  const decimals = getCoinDecimals(e.coinId) ?? 0;
35
53
  const amount = toHumanReadable(e.amount, decimals);
36
- const peer = e.type === "SENT" && e.recipientNametag
54
+
55
+ const isBurn = (e.type === "SENT" && !e.transferId) || burnIds.has(e.id);
56
+ const label = isBurn ? "BURN (split)" : e.type;
57
+
58
+ const peer = e.type === "SENT" && !isBurn && e.recipientNametag
37
59
  ? ` to @${e.recipientNametag}`
38
60
  : e.type === "RECEIVED" && e.senderPubkey
39
61
  ? ` from ${e.senderPubkey.slice(0, 12)}…`
40
62
  : "";
41
63
  const txRef = e.transferId ? ` [tx:${e.transferId.slice(0, 8)}]` : "";
42
- return `[${time}] ${e.type} ${amount} ${e.symbol}${peer}${txRef}`;
64
+ return `[${time}] ${label} ${amount} ${e.symbol}${peer}${txRef}`;
43
65
  });
44
66
 
45
67
  return {