openclaw-overlay-plugin 0.7.68 โ†’ 0.7.70

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.
@@ -4,7 +4,8 @@ import os from 'node:os';
4
4
  import fs from 'node:fs';
5
5
  import process from 'node:process';
6
6
  import { ok, fail } from '../output.js';
7
- import { loadIdentity } from '../wallet/identity.js';
7
+ import { loadIdentity, deriveWalletAddress } from '../wallet/identity.js';
8
+ import { NETWORK } from '../config.js';
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
10
11
  // Define paths relative to home directory
@@ -172,7 +173,7 @@ export async function cmdBaemailRefund(requestId) {
172
173
  return fail('Refund already processed for this request');
173
174
  }
174
175
  // Load wallet and SDK
175
- const { identityKey, privKey } = await loadIdentity();
176
+ const { identityKey, privKey: rootKey } = await loadIdentity();
176
177
  const walletIdentityRaw = fs.readFileSync(PATHS.walletIdentity, 'utf-8');
177
178
  const walletIdentity = JSON.parse(walletIdentityRaw);
178
179
  // Dynamic import SDK
@@ -191,20 +192,20 @@ export async function cmdBaemailRefund(requestId) {
191
192
  }
192
193
  // Derive refund address from sender's identity key
193
194
  const senderPubKey = PublicKey.fromString(entry.from);
194
- const refundAddress = senderPubKey.toAddress().toString();
195
+ const refundAddress = senderPubKey.toAddress(NETWORK).toString();
195
196
  try {
196
- // Load UTXOs
197
- const address = walletIdentity.address;
198
- const utxosResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent/all`);
197
+ // Load UTXOs - Derive local address correctly
198
+ const { address } = await deriveWalletAddress(rootKey);
199
+ const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';
200
+ const utxosResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/${wocNet}/address/${address}/unspent/all`);
199
201
  const data = await utxosResp.json();
200
202
  const utxos = data.result || [];
201
203
  if (!utxos || utxos.length === 0) {
202
- return fail('No UTXOs available for refund');
204
+ return fail(`No UTXOs available for refund at ${address}`);
203
205
  }
204
206
  // Build transaction
205
207
  const tx = new Transaction();
206
208
  let totalInput = 0;
207
- const rootKey = PrivateKey.fromHex(walletIdentity.rootKeyHex);
208
209
  for (const utxo of utxos) {
209
210
  if (totalInput >= refundSats + 50)
210
211
  break;
@@ -212,7 +213,7 @@ export async function cmdBaemailRefund(requestId) {
212
213
  sourceTXID: utxo.tx_hash,
213
214
  sourceOutputIndex: utxo.tx_pos,
214
215
  sourceSatoshis: utxo.value,
215
- script: new P2PKH().lock(rootKey.toPublicKey().toAddress()).toHex(),
216
+ script: new P2PKH().lock(rootKey.toPublicKey().toAddress(NETWORK)).toHex(),
216
217
  unlockingScriptTemplate: new P2PKH().unlock(rootKey),
217
218
  });
218
219
  totalInput += utxo.value;
@@ -231,7 +232,7 @@ export async function cmdBaemailRefund(requestId) {
231
232
  if (change > 1) {
232
233
  tx.addOutput({
233
234
  satoshis: change,
234
- lockingScript: new P2PKH().lock(rootKey.toPublicKey().toAddress()),
235
+ lockingScript: new P2PKH().lock(rootKey.toPublicKey().toAddress(NETWORK)),
235
236
  });
236
237
  }
237
238
  await tx.sign();
@@ -246,7 +247,7 @@ export async function cmdBaemailRefund(requestId) {
246
247
  });
247
248
  }
248
249
  else {
249
- broadcastResp = await fetchWithTimeout('https://api.whatsonchain.com/v1/bsv/main/tx/raw', {
250
+ broadcastResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/${wocNet}/tx/raw`, {
250
251
  method: 'POST',
251
252
  headers: { 'Content-Type': 'application/json' },
252
253
  body: JSON.stringify({ txhex: tx.toHex() }),
@@ -47,7 +47,7 @@ export declare function verifyRelaySignature(fromKey: string, to: string, type:
47
47
  *
48
48
  * Use deriveWalletKeys() to get both the address and signing key.
49
49
  */
50
- export declare function deriveWalletAddress(privKey: any): Promise<{
50
+ export declare function deriveWalletAddress(privKey: any, network?: string): Promise<{
51
51
  address: string;
52
52
  hash160: Uint8Array;
53
53
  pubKey: any;
@@ -60,9 +60,10 @@ export declare function deriveWalletAddress(privKey: any): Promise<{
60
60
  * root private key - it will cause signature verification failures!
61
61
  *
62
62
  * @param rootPrivKey - Root private key from wallet identity
63
+ * @param network - Optional network override
63
64
  * @returns Object with address, hash160, and CHILD private key for signing
64
65
  */
65
- export declare function deriveWalletKeys(rootPrivKey: any): Promise<{
66
+ export declare function deriveWalletKeys(rootPrivKey: any, network?: string): Promise<{
66
67
  address: string;
67
68
  hash160: Uint8Array;
68
69
  pubKey: any;
@@ -2,7 +2,7 @@
2
2
  * Wallet identity helpers.
3
3
  */
4
4
  import fs from 'node:fs';
5
- import { PATHS } from '../config.js';
5
+ import { PATHS, NETWORK } from '../config.js';
6
6
  import { CachedKeyDeriver, Utils } from '@bsv/sdk';
7
7
  import { brc29ProtocolID } from '@bsv/wallet-toolbox';
8
8
  // Dynamic import for @bsv/sdk
@@ -119,10 +119,10 @@ export async function verifyRelaySignature(fromKey, to, type, payload, signature
119
119
  *
120
120
  * Use deriveWalletKeys() to get both the address and signing key.
121
121
  */
122
- export async function deriveWalletAddress(privKey) {
122
+ export async function deriveWalletAddress(privKey, network = NETWORK) {
123
123
  const keyDeriver = new CachedKeyDeriver(privKey);
124
124
  const pubKey = keyDeriver.derivePublicKey(brc29ProtocolID, Utils.toBase64(Utils.toArray('import')) + ' ' + Utils.toBase64(Utils.toArray('now')), 'self', true);
125
- const address = pubKey.toAddress();
125
+ const address = pubKey.toAddress(network);
126
126
  const hash160 = Buffer.from(pubKey.toHash());
127
127
  return { address, hash160, pubKey };
128
128
  }
@@ -134,9 +134,10 @@ export async function deriveWalletAddress(privKey) {
134
134
  * root private key - it will cause signature verification failures!
135
135
  *
136
136
  * @param rootPrivKey - Root private key from wallet identity
137
+ * @param network - Optional network override
137
138
  * @returns Object with address, hash160, and CHILD private key for signing
138
139
  */
139
- export async function deriveWalletKeys(rootPrivKey) {
140
+ export async function deriveWalletKeys(rootPrivKey, network = NETWORK) {
140
141
  const keyDeriver = new CachedKeyDeriver(rootPrivKey);
141
142
  const derivationPrefix = Utils.toBase64(Utils.toArray('import'));
142
143
  const derivationSuffix = Utils.toBase64(Utils.toArray('now'));
@@ -145,7 +146,7 @@ export async function deriveWalletKeys(rootPrivKey) {
145
146
  const childPrivKey = keyDeriver.derivePrivateKey(brc29ProtocolID, keyString, 'self');
146
147
  // Derive child public key (for address)
147
148
  const pubKey = keyDeriver.derivePublicKey(brc29ProtocolID, keyString, 'self', true);
148
- const address = pubKey.toAddress();
149
+ const address = pubKey.toAddress(network);
149
150
  const hash160 = Buffer.from(pubKey.toHash());
150
151
  return { address, hash160, pubKey, childPrivKey };
151
152
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Unit tests for network-specific address generation.
3
+ *
4
+ * These tests verify that address generation correctly uses the specified
5
+ * network prefix (mainnet vs testnet).
6
+ *
7
+ * Run: npx tsx src/test/network-address.test.ts
8
+ */
9
+ export {};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Unit tests for network-specific address generation.
3
+ *
4
+ * These tests verify that address generation correctly uses the specified
5
+ * network prefix (mainnet vs testnet).
6
+ *
7
+ * Run: npx tsx src/test/network-address.test.ts
8
+ */
9
+ import { PrivateKey } from '@bsv/sdk';
10
+ import { deriveWalletAddress } from '../scripts/wallet/identity.js';
11
+ async function assert(condition, message) {
12
+ if (!condition) {
13
+ throw new Error(`Assertion failed: ${message}`);
14
+ }
15
+ }
16
+ async function runTests() {
17
+ console.log('๐Ÿงช Running Network Address Generation Tests...\n');
18
+ const privKey = PrivateKey.fromRandom();
19
+ // Test 1: Mainnet Address Generation
20
+ console.log('โœ“ Test 1: Mainnet address starts with 1');
21
+ const mainnet = await deriveWalletAddress(privKey, 'mainnet');
22
+ console.log(` Mainnet: ${mainnet.address}`);
23
+ await assert(mainnet.address.startsWith('1'), 'Mainnet address should start with 1');
24
+ // Test 2: Testnet Address Generation
25
+ console.log('โœ“ Test 2: Testnet address starts with m or n');
26
+ const testnet = await deriveWalletAddress(privKey, 'testnet');
27
+ console.log(` Testnet: ${testnet.address}`);
28
+ await assert(testnet.address.startsWith('m') || testnet.address.startsWith('n'), 'Testnet address should start with m or n');
29
+ // Test 3: Addresses are different
30
+ console.log('โœ“ Test 3: Mainnet and testnet addresses for same key are different');
31
+ await assert(mainnet.address !== testnet.address, 'Addresses should be different across networks');
32
+ console.log('\nโœ… All network address tests passed!\n');
33
+ }
34
+ runTests().catch((err) => {
35
+ console.error('\nโŒ Tests failed:', err.message);
36
+ process.exit(1);
37
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-overlay-plugin",
3
- "version": "0.7.68",
3
+ "version": "0.7.70",
4
4
  "description": "Openclaw BSV Overlay โ€” agent discovery, service marketplace, and micropayments on the BSV blockchain",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@bsv/sdk": "^2.0.13",
29
- "@bsv/wallet-toolbox": "^2.1.17",
29
+ "@bsv/wallet-toolbox": "^2.1.18",
30
30
  "dotenv": "^17.3.1",
31
- "knex": "^3.2.8",
31
+ "knex": "^3.2.9",
32
32
  "sqlite3": "^5.1.7"
33
33
  },
34
34
  "devDependencies": {
@@ -5,7 +5,8 @@ import fs from 'node:fs';
5
5
  import process from 'node:process';
6
6
  import { Buffer } from 'node:buffer';
7
7
  import { ok, fail } from '../output.js';
8
- import { loadIdentity } from '../wallet/identity.js';
8
+ import { loadIdentity, deriveWalletAddress } from '../wallet/identity.js';
9
+ import { NETWORK } from '../config.js';
9
10
 
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
@@ -186,7 +187,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
186
187
  }
187
188
 
188
189
  // Load wallet and SDK
189
- const { identityKey, privKey } = await loadIdentity();
190
+ const { identityKey, privKey: rootKey } = await loadIdentity();
190
191
  const walletIdentityRaw = fs.readFileSync(PATHS.walletIdentity, 'utf-8');
191
192
  const walletIdentity = JSON.parse(walletIdentityRaw);
192
193
 
@@ -208,23 +209,23 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
208
209
 
209
210
  // Derive refund address from sender's identity key
210
211
  const senderPubKey = PublicKey.fromString(entry.from);
211
- const refundAddress = senderPubKey.toAddress().toString();
212
+ const refundAddress = senderPubKey.toAddress(NETWORK).toString();
212
213
 
213
214
  try {
214
- // Load UTXOs
215
- const address = walletIdentity.address;
216
- const utxosResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent/all`);
215
+ // Load UTXOs - Derive local address correctly
216
+ const { address } = await deriveWalletAddress(rootKey);
217
+ const wocNet = NETWORK === 'mainnet' ? 'main' : 'test';
218
+ const utxosResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/${wocNet}/address/${address}/unspent/all`);
217
219
  const data = await utxosResp.json();
218
220
  const utxos = data.result || [];
219
221
 
220
222
  if (!utxos || utxos.length === 0) {
221
- return fail('No UTXOs available for refund');
223
+ return fail(`No UTXOs available for refund at ${address}`);
222
224
  }
223
225
 
224
226
  // Build transaction
225
227
  const tx = new Transaction();
226
228
  let totalInput = 0;
227
- const rootKey = PrivateKey.fromHex(walletIdentity.rootKeyHex);
228
229
 
229
230
  for (const utxo of utxos) {
230
231
  if (totalInput >= refundSats + 50) break;
@@ -232,7 +233,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
232
233
  sourceTXID: utxo.tx_hash,
233
234
  sourceOutputIndex: utxo.tx_pos,
234
235
  sourceSatoshis: utxo.value,
235
- script: new P2PKH().lock(rootKey.toPublicKey().toAddress()).toHex(),
236
+ script: new P2PKH().lock(rootKey.toPublicKey().toAddress(NETWORK)).toHex(),
236
237
  unlockingScriptTemplate: new P2PKH().unlock(rootKey),
237
238
  });
238
239
  totalInput += utxo.value;
@@ -254,7 +255,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
254
255
  if (change > 1) {
255
256
  tx.addOutput({
256
257
  satoshis: change,
257
- lockingScript: new P2PKH().lock(rootKey.toPublicKey().toAddress()),
258
+ lockingScript: new P2PKH().lock(rootKey.toPublicKey().toAddress(NETWORK)),
258
259
  });
259
260
  }
260
261
 
@@ -271,7 +272,7 @@ export async function cmdBaemailRefund(requestId: string | undefined): Promise<n
271
272
  body: JSON.stringify({ rawTx: tx.toHex() }),
272
273
  });
273
274
  } else {
274
- broadcastResp = await fetchWithTimeout('https://api.whatsonchain.com/v1/bsv/main/tx/raw', {
275
+ broadcastResp = await fetchWithTimeout(`https://api.whatsonchain.com/v1/bsv/${wocNet}/tx/raw`, {
275
276
  method: 'POST',
276
277
  headers: { 'Content-Type': 'application/json' },
277
278
  body: JSON.stringify({ txhex: tx.toHex() }),
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import fs from 'node:fs';
6
- import { PATHS } from '../config.js';
6
+ import { PATHS, NETWORK } from '../config.js';
7
7
  import type { WalletIdentity } from '../types.js';
8
8
  import { CachedKeyDeriver, Utils } from '@bsv/sdk';
9
9
  import { brc29ProtocolID } from '@bsv/wallet-toolbox';
@@ -139,7 +139,7 @@ export async function verifyRelaySignature(
139
139
  *
140
140
  * Use deriveWalletKeys() to get both the address and signing key.
141
141
  */
142
- export async function deriveWalletAddress(privKey: any): Promise<{
142
+ export async function deriveWalletAddress(privKey: any, network: string = NETWORK): Promise<{
143
143
  address: string;
144
144
  hash160: Uint8Array;
145
145
  pubKey: any;
@@ -153,7 +153,7 @@ export async function deriveWalletAddress(privKey: any): Promise<{
153
153
  true
154
154
  );
155
155
 
156
- const address = pubKey.toAddress();
156
+ const address = pubKey.toAddress(network);
157
157
  const hash160 = Buffer.from(pubKey.toHash());
158
158
 
159
159
  return { address, hash160, pubKey };
@@ -167,9 +167,10 @@ export async function deriveWalletAddress(privKey: any): Promise<{
167
167
  * root private key - it will cause signature verification failures!
168
168
  *
169
169
  * @param rootPrivKey - Root private key from wallet identity
170
+ * @param network - Optional network override
170
171
  * @returns Object with address, hash160, and CHILD private key for signing
171
172
  */
172
- export async function deriveWalletKeys(rootPrivKey: any): Promise<{
173
+ export async function deriveWalletKeys(rootPrivKey: any, network: string = NETWORK): Promise<{
173
174
  address: string;
174
175
  hash160: Uint8Array;
175
176
  pubKey: any;
@@ -196,7 +197,7 @@ export async function deriveWalletKeys(rootPrivKey: any): Promise<{
196
197
  true
197
198
  );
198
199
 
199
- const address = pubKey.toAddress();
200
+ const address = pubKey.toAddress(network);
200
201
  const hash160 = Buffer.from(pubKey.toHash());
201
202
 
202
203
  return { address, hash160, pubKey, childPrivKey };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Unit tests for network-specific address generation.
3
+ *
4
+ * These tests verify that address generation correctly uses the specified
5
+ * network prefix (mainnet vs testnet).
6
+ *
7
+ * Run: npx tsx src/test/network-address.test.ts
8
+ */
9
+
10
+ import { PrivateKey } from '@bsv/sdk';
11
+ import { deriveWalletAddress } from '../scripts/wallet/identity.js';
12
+
13
+ async function assert(condition: boolean, message: string) {
14
+ if (!condition) {
15
+ throw new Error(`Assertion failed: ${message}`);
16
+ }
17
+ }
18
+
19
+ async function runTests() {
20
+ console.log('๐Ÿงช Running Network Address Generation Tests...\n');
21
+
22
+ const privKey = PrivateKey.fromRandom();
23
+
24
+ // Test 1: Mainnet Address Generation
25
+ console.log('โœ“ Test 1: Mainnet address starts with 1');
26
+ const mainnet = await deriveWalletAddress(privKey, 'mainnet');
27
+ console.log(` Mainnet: ${mainnet.address}`);
28
+ await assert(mainnet.address.startsWith('1'), 'Mainnet address should start with 1');
29
+
30
+ // Test 2: Testnet Address Generation
31
+ console.log('โœ“ Test 2: Testnet address starts with m or n');
32
+ const testnet = await deriveWalletAddress(privKey, 'testnet');
33
+ console.log(` Testnet: ${testnet.address}`);
34
+ await assert(testnet.address.startsWith('m') || testnet.address.startsWith('n'), 'Testnet address should start with m or n');
35
+
36
+ // Test 3: Addresses are different
37
+ console.log('โœ“ Test 3: Mainnet and testnet addresses for same key are different');
38
+ await assert(mainnet.address !== testnet.address, 'Addresses should be different across networks');
39
+
40
+ console.log('\nโœ… All network address tests passed!\n');
41
+ }
42
+
43
+ runTests().catch((err) => {
44
+ console.error('\nโŒ Tests failed:', err.message);
45
+ process.exit(1);
46
+ });