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.
- package/dist/src/scripts/baemail/commands.js +12 -11
- package/dist/src/scripts/wallet/identity.d.ts +3 -2
- package/dist/src/scripts/wallet/identity.js +6 -5
- package/dist/src/test/network-address.test.d.ts +9 -0
- package/dist/src/test/network-address.test.js +37 -0
- package/package.json +3 -3
- package/src/scripts/baemail/commands.ts +12 -11
- package/src/scripts/wallet/identity.ts +6 -5
- package/src/test/network-address.test.ts +46 -0
|
@@ -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 =
|
|
198
|
-
const
|
|
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(
|
|
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(
|
|
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,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.
|
|
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.
|
|
29
|
+
"@bsv/wallet-toolbox": "^2.1.18",
|
|
30
30
|
"dotenv": "^17.3.1",
|
|
31
|
-
"knex": "^3.2.
|
|
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 =
|
|
216
|
-
const
|
|
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(
|
|
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(
|
|
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
|
+
});
|