@bsv/wallet-toolbox 1.1.3 → 1.1.4
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/docs/README.md +1 -0
- package/docs/client.md +324 -84
- package/docs/setup.md +358 -67
- package/docs/wallet.md +324 -84
- package/out/src/Setup.d.ts +28 -30
- package/out/src/Setup.d.ts.map +1 -1
- package/out/src/Setup.js +14 -10
- package/out/src/Setup.js.map +1 -1
- package/out/src/SetupClient.d.ts +99 -41
- package/out/src/SetupClient.d.ts.map +1 -1
- package/out/src/SetupClient.js +124 -90
- package/out/src/SetupClient.js.map +1 -1
- package/out/src/Wallet.d.ts +10 -0
- package/out/src/Wallet.d.ts.map +1 -1
- package/out/src/Wallet.js.map +1 -1
- package/out/src/index.all.d.ts +1 -1
- package/out/src/index.all.d.ts.map +1 -1
- package/out/src/index.all.js +3 -2
- package/out/src/index.all.js.map +1 -1
- package/out/src/sdk/types.d.ts +5 -0
- package/out/src/sdk/types.d.ts.map +1 -1
- package/out/src/sdk/types.js.map +1 -1
- package/out/test/examples/README.man.test.js +8 -7
- package/out/test/examples/README.man.test.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Setup.ts +41 -54
- package/src/SetupClient.ts +219 -150
- package/src/Wallet.ts +10 -0
- package/src/index.all.ts +9 -1
- package/src/sdk/types.ts +7 -0
- package/test/examples/README.man.test.ts +9 -9
- package/ts2md.json +0 -8
package/package.json
CHANGED
package/src/Setup.ts
CHANGED
|
@@ -1,61 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Beef,
|
|
3
|
-
CreateActionArgs,
|
|
4
|
-
CreateActionOutput,
|
|
5
|
-
CreateActionResult,
|
|
6
|
-
KeyDeriver,
|
|
7
|
-
P2PKH,
|
|
8
|
-
PrivateKey,
|
|
9
|
-
PublicKey,
|
|
10
|
-
SignActionArgs,
|
|
11
|
-
SignActionResult,
|
|
12
|
-
WalletInterface
|
|
13
|
-
} from '@bsv/sdk'
|
|
1
|
+
import { KeyDeriver, PrivateKey } from '@bsv/sdk'
|
|
14
2
|
import {
|
|
15
3
|
Monitor,
|
|
16
4
|
sdk,
|
|
17
5
|
Services,
|
|
18
6
|
SetupClient,
|
|
19
|
-
StorageClient,
|
|
20
|
-
verifyTruthy,
|
|
21
7
|
Wallet,
|
|
22
8
|
WalletStorageManager
|
|
23
9
|
} from './index.client'
|
|
24
|
-
import { PrivilegedKeyManager } from './sdk'
|
|
25
10
|
import { Knex, knex as makeKnex } from 'knex'
|
|
26
|
-
import {
|
|
11
|
+
import { SetupWallet, SetupWalletArgs, StorageKnex } from './index.all'
|
|
27
12
|
|
|
28
13
|
/**
|
|
29
|
-
*
|
|
14
|
+
* The 'Setup` class provides static setup functions to construct BRC-100 compatible
|
|
30
15
|
* wallets in a variety of configurations.
|
|
31
16
|
*
|
|
32
17
|
* It serves as a starting point for experimentation and customization.
|
|
33
18
|
*
|
|
19
|
+
* `SetupClient` references only browser compatible code including storage via `StorageClient`.
|
|
20
|
+
* `Setup` extends `SetupClient` adding database storage via `Knex` and `StorageKnex`.
|
|
21
|
+
*
|
|
34
22
|
*/
|
|
35
23
|
export abstract class Setup extends SetupClient {
|
|
36
24
|
/**
|
|
37
25
|
* Adds `Knex` based storage to a `Wallet` configured by `Setup.createWalletOnly`
|
|
38
26
|
*
|
|
39
|
-
* @param args
|
|
40
|
-
*
|
|
27
|
+
* @param args.knex `Knex` object configured for either MySQL or SQLite database access.
|
|
28
|
+
* Schema will be created and migrated as needed.
|
|
29
|
+
* For MySQL, a schema corresponding to databaseName must exist with full access permissions.
|
|
30
|
+
* @param args.databaseName Name for this storage. For MySQL, the schema name within the MySQL instance.
|
|
31
|
+
* @param args.chain Which chain this wallet is on: 'main' or 'test'. Defaults to 'test'.
|
|
32
|
+
* @param args.rootKeyHex
|
|
33
|
+
*
|
|
34
|
+
* @publicbody
|
|
41
35
|
*/
|
|
42
|
-
static async createKnexWallet(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* For MySQL, a schema corresponding to databaseName must exist with full access permissions.
|
|
47
|
-
*/
|
|
48
|
-
knex: Knex<any, any[]>
|
|
49
|
-
databaseName: string
|
|
50
|
-
chain?: sdk.Chain
|
|
51
|
-
rootKeyHex?: string
|
|
52
|
-
privKeyHex?: string
|
|
53
|
-
}): Promise<SetupWallet> {
|
|
54
|
-
const wo = await Setup.createWalletOnly({
|
|
55
|
-
chain: args.chain,
|
|
56
|
-
rootKeyHex: args.rootKeyHex,
|
|
57
|
-
privKeyHex: args.privKeyHex
|
|
58
|
-
})
|
|
36
|
+
static async createKnexWallet(
|
|
37
|
+
args: SetupWalletKnexArgs
|
|
38
|
+
): Promise<SetupWalletKnex> {
|
|
39
|
+
const wo = await Setup.createWallet(args)
|
|
59
40
|
const activeStorage = new StorageKnex({
|
|
60
41
|
chain: wo.chain,
|
|
61
42
|
knex: args.knex,
|
|
@@ -68,7 +49,7 @@ export abstract class Setup extends SetupClient {
|
|
|
68
49
|
await wo.storage.addWalletStorageProvider(activeStorage)
|
|
69
50
|
const { user, isNew } = await activeStorage.findOrInsertUser(wo.identityKey)
|
|
70
51
|
const userId = user.userId
|
|
71
|
-
const r:
|
|
52
|
+
const r: SetupWalletKnex = {
|
|
72
53
|
...wo,
|
|
73
54
|
activeStorage,
|
|
74
55
|
userId
|
|
@@ -101,26 +82,18 @@ export abstract class Setup extends SetupClient {
|
|
|
101
82
|
return knex
|
|
102
83
|
}
|
|
103
84
|
|
|
104
|
-
static async createMySQLWallet(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
rootKeyHex?: string
|
|
108
|
-
privKeyHex?: string
|
|
109
|
-
}): Promise<SetupWallet> {
|
|
110
|
-
const env = Setup.getEnv(args.chain || 'test')
|
|
85
|
+
static async createMySQLWallet(
|
|
86
|
+
args: SetupWalletMySQLArgs
|
|
87
|
+
): Promise<SetupWalletKnex> {
|
|
111
88
|
return await this.createKnexWallet({
|
|
112
89
|
...args,
|
|
113
|
-
knex: Setup.createMySQLKnex(env.mySQLConnection, args.databaseName)
|
|
90
|
+
knex: Setup.createMySQLKnex(args.env.mySQLConnection, args.databaseName)
|
|
114
91
|
})
|
|
115
92
|
}
|
|
116
93
|
|
|
117
|
-
static async createSQLiteWallet(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
chain?: sdk.Chain
|
|
121
|
-
rootKeyHex?: string
|
|
122
|
-
privKeyHex?: string
|
|
123
|
-
}): Promise<SetupWallet> {
|
|
94
|
+
static async createSQLiteWallet(
|
|
95
|
+
args: SetupWalletSQLiteArgs
|
|
96
|
+
): Promise<SetupWalletKnex> {
|
|
124
97
|
return await this.createKnexWallet({
|
|
125
98
|
...args,
|
|
126
99
|
knex: Setup.createSQLiteKnex(args.filePath)
|
|
@@ -128,7 +101,21 @@ export abstract class Setup extends SetupClient {
|
|
|
128
101
|
}
|
|
129
102
|
}
|
|
130
103
|
|
|
131
|
-
export interface
|
|
104
|
+
export interface SetupWalletKnexArgs extends SetupWalletArgs {
|
|
105
|
+
knex: Knex<any, any[]>
|
|
106
|
+
databaseName: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface SetupWalletMySQLArgs extends SetupWalletArgs {
|
|
110
|
+
databaseName: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface SetupWalletSQLiteArgs extends SetupWalletArgs {
|
|
114
|
+
filePath: string
|
|
115
|
+
databaseName: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface SetupWalletKnex extends SetupWallet {
|
|
132
119
|
activeStorage: StorageKnex
|
|
133
120
|
userId: number
|
|
134
121
|
|
package/src/SetupClient.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BEEF,
|
|
2
3
|
Beef,
|
|
3
4
|
CreateActionArgs,
|
|
5
|
+
CreateActionOptions,
|
|
4
6
|
CreateActionOutput,
|
|
5
7
|
CreateActionResult,
|
|
6
8
|
KeyDeriver,
|
|
@@ -9,6 +11,8 @@ import {
|
|
|
9
11
|
PublicKey,
|
|
10
12
|
SignActionArgs,
|
|
11
13
|
SignActionResult,
|
|
14
|
+
Transaction,
|
|
15
|
+
UnlockingScript,
|
|
12
16
|
WalletInterface
|
|
13
17
|
} from '@bsv/sdk'
|
|
14
18
|
import {
|
|
@@ -20,17 +24,29 @@ import {
|
|
|
20
24
|
Wallet,
|
|
21
25
|
WalletStorageManager
|
|
22
26
|
} from './index.client'
|
|
23
|
-
import { PrivilegedKeyManager } from './sdk'
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
|
-
*
|
|
29
|
+
* The `SetupClient` class provides static setup functions to construct BRC-100 compatible
|
|
27
30
|
* wallets in a variety of configurations.
|
|
28
31
|
*
|
|
29
32
|
* It serves as a starting point for experimentation and customization.
|
|
30
33
|
*
|
|
34
|
+
* `SetupClient` references only browser compatible code including storage via `StorageClient`.
|
|
35
|
+
* `Setup` extends `SetupClient` adding database storage via `Knex` and `StorageKnex`.
|
|
36
|
+
*
|
|
31
37
|
*/
|
|
32
38
|
export abstract class SetupClient {
|
|
33
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Creates content for .env file with some private keys, identity keys, sample API keys, and sample MySQL connection string.
|
|
41
|
+
*
|
|
42
|
+
* Two new, random private keys are generated each time, with their associated public identity keys.
|
|
43
|
+
*
|
|
44
|
+
* Loading secrets from a .env file is intended only for experimentation and getting started.
|
|
45
|
+
* Private keys should never be included directly in your source code.
|
|
46
|
+
*
|
|
47
|
+
* @publicBody
|
|
48
|
+
*/
|
|
49
|
+
static makeEnv(): string {
|
|
34
50
|
const testPrivKey1 = PrivateKey.fromRandom()
|
|
35
51
|
const testIdentityKey1 = testPrivKey1.toPublicKey().toString()
|
|
36
52
|
const testPrivKey2 = PrivateKey.fromRandom()
|
|
@@ -57,18 +73,24 @@ export abstract class SetupClient {
|
|
|
57
73
|
MYSQL_CONNECTION='{"port":3306,"host":"127.0.0.1","user":"root","password":"<your_password>","database":"<your_database>", "timezone": "Z"}'
|
|
58
74
|
`
|
|
59
75
|
console.log(log)
|
|
76
|
+
|
|
77
|
+
return log
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Reads a .env file of the format created by `makeEnv`.
|
|
82
|
+
*
|
|
83
|
+
* Returns values for designated `chain`.
|
|
84
|
+
*
|
|
85
|
+
* Access private keys through the `devKeys` object: `devKeys[identityKey]`
|
|
86
|
+
*
|
|
87
|
+
* @param chain Which chain to use: 'test' or 'main'
|
|
88
|
+
* @returns {SetupEnv} with configuration environment secrets used by `Setup` functions.
|
|
89
|
+
*
|
|
90
|
+
* @publicBody
|
|
91
|
+
*/
|
|
92
|
+
static getEnv(chain: sdk.Chain): SetupEnv {
|
|
63
93
|
// Identity keys of the lead maintainer of this repo...
|
|
64
|
-
const mainTaalApiKey = verifyTruthy(
|
|
65
|
-
process.env.MAIN_TAAL_API_KEY || '',
|
|
66
|
-
`.env value for 'mainTaalApiKey' is required.`
|
|
67
|
-
)
|
|
68
|
-
const testTaalApiKey = verifyTruthy(
|
|
69
|
-
process.env.TEST_TAAL_API_KEY || '',
|
|
70
|
-
`.env value for 'testTaalApiKey' is required.`
|
|
71
|
-
)
|
|
72
94
|
const identityKey =
|
|
73
95
|
chain === 'main'
|
|
74
96
|
? process.env.MY_MAIN_IDENTITY
|
|
@@ -79,137 +101,39 @@ export abstract class SetupClient {
|
|
|
79
101
|
: process.env.MY_TEST_IDENTITY2
|
|
80
102
|
const DEV_KEYS = process.env.DEV_KEYS || '{}'
|
|
81
103
|
const mySQLConnection = process.env.MYSQL_CONNECTION || '{}'
|
|
104
|
+
const taalApiKey = verifyTruthy(
|
|
105
|
+
chain === 'main'
|
|
106
|
+
? process.env.MAIN_TAAL_API_KEY
|
|
107
|
+
: process.env.TEST_TAAL_API_KEY,
|
|
108
|
+
`.env value for '${chain.toUpperCase()}_TAAL_API_KEY' is required.`
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if (!identityKey || !identityKey2)
|
|
112
|
+
throw new sdk.WERR_INVALID_OPERATION(
|
|
113
|
+
'.env is not a valid SetupEnv configuration.'
|
|
114
|
+
)
|
|
82
115
|
|
|
83
116
|
return {
|
|
84
117
|
chain,
|
|
85
118
|
identityKey,
|
|
86
119
|
identityKey2,
|
|
87
|
-
|
|
88
|
-
testTaalApiKey,
|
|
120
|
+
taalApiKey,
|
|
89
121
|
devKeys: JSON.parse(DEV_KEYS) as Record<string, string>,
|
|
90
122
|
mySQLConnection
|
|
91
123
|
}
|
|
92
124
|
}
|
|
93
125
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}> {
|
|
106
|
-
return await SetupClient.createNoSendP2PKHOutpoints(
|
|
107
|
-
1,
|
|
108
|
-
basket,
|
|
109
|
-
address,
|
|
110
|
-
satoshis,
|
|
111
|
-
noSendChange,
|
|
112
|
-
wallet
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
static async createNoSendP2PKHOutpoints(
|
|
117
|
-
count: number,
|
|
118
|
-
basket: string,
|
|
119
|
-
address: string,
|
|
120
|
-
satoshis: number,
|
|
121
|
-
noSendChange: string[] | undefined,
|
|
122
|
-
wallet: WalletInterface
|
|
123
|
-
): Promise<{
|
|
124
|
-
noSendChange: string[]
|
|
125
|
-
txid: string
|
|
126
|
-
cr: CreateActionResult
|
|
127
|
-
sr: SignActionResult
|
|
128
|
-
}> {
|
|
129
|
-
const outputs: CreateActionOutput[] = []
|
|
130
|
-
for (let i = 0; i < count; i++) {
|
|
131
|
-
outputs.push({
|
|
132
|
-
basket,
|
|
133
|
-
satoshis,
|
|
134
|
-
lockingScript: SetupClient.getLockP2PKH(address).toHex(),
|
|
135
|
-
outputDescription: `p2pkh ${i}`
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const createArgs: CreateActionArgs = {
|
|
140
|
-
description: `to ${address}`,
|
|
141
|
-
outputs,
|
|
142
|
-
options: {
|
|
143
|
-
noSendChange,
|
|
144
|
-
randomizeOutputs: false,
|
|
145
|
-
signAndProcess: false,
|
|
146
|
-
noSend: true
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const cr = await wallet.createAction(createArgs)
|
|
151
|
-
noSendChange = cr.noSendChange
|
|
152
|
-
|
|
153
|
-
const st = cr.signableTransaction!
|
|
154
|
-
// const tx = Transaction.fromAtomicBEEF(st.tx) // Transaction doesn't support V2 Beef yet.
|
|
155
|
-
const beef = Beef.fromBinary(st.tx)
|
|
156
|
-
const btx = beef.txs.slice(-1)[0]
|
|
157
|
-
const tx = beef.findAtomicTransaction(btx.txid)
|
|
158
|
-
|
|
159
|
-
// sign and complete
|
|
160
|
-
const signArgs: SignActionArgs = {
|
|
161
|
-
reference: st.reference,
|
|
162
|
-
spends: {},
|
|
163
|
-
options: {
|
|
164
|
-
returnTXIDOnly: true,
|
|
165
|
-
noSend: true
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const sr = await wallet.signAction(signArgs)
|
|
170
|
-
|
|
171
|
-
let txid = sr.txid!
|
|
172
|
-
// Update the noSendChange txid to final signed value.
|
|
173
|
-
noSendChange = noSendChange!.map(op => `${txid}.${op.split('.')[1]}`)
|
|
174
|
-
return { noSendChange, txid, cr, sr }
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
static getKeyPair(priv?: string | PrivateKey): KeyPairAddress {
|
|
178
|
-
if (priv === undefined) priv = PrivateKey.fromRandom()
|
|
179
|
-
else if (typeof priv === 'string') priv = new PrivateKey(priv, 'hex')
|
|
180
|
-
|
|
181
|
-
const pub = PublicKey.fromPrivateKey(priv)
|
|
182
|
-
const address = pub.toAddress()
|
|
183
|
-
return { privateKey: priv, publicKey: pub, address }
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
static getLockP2PKH(address: string) {
|
|
187
|
-
const p2pkh = new P2PKH()
|
|
188
|
-
const lock = p2pkh.lock(address)
|
|
189
|
-
return lock
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
static getUnlockP2PKH(priv: PrivateKey, satoshis: number) {
|
|
193
|
-
const p2pkh = new P2PKH()
|
|
194
|
-
const lock = SetupClient.getLockP2PKH(SetupClient.getKeyPair(priv).address)
|
|
195
|
-
// Prepare to pay with SIGHASH_ALL and without ANYONE_CAN_PAY.
|
|
196
|
-
// In otherwords:
|
|
197
|
-
// - all outputs must remain in the current order, amount and locking scripts.
|
|
198
|
-
// - all inputs must remain from the current outpoints and sequence numbers.
|
|
199
|
-
// (unlock scripts are never signed)
|
|
200
|
-
const unlock = p2pkh.unlock(priv, 'all', false, satoshis, lock)
|
|
201
|
-
return unlock
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
static async createWalletOnly(args: {
|
|
205
|
-
chain?: sdk.Chain
|
|
206
|
-
rootKeyHex?: string
|
|
207
|
-
active?: sdk.WalletStorageProvider
|
|
208
|
-
backups?: sdk.WalletStorageProvider[]
|
|
209
|
-
privKeyHex?: string
|
|
210
|
-
}): Promise<SetupWalletOnly> {
|
|
211
|
-
args.chain ||= 'test'
|
|
212
|
-
args.rootKeyHex ||= '1'.repeat(64)
|
|
126
|
+
/**
|
|
127
|
+
* Create a `Wallet`. Storage can optionally be provided or configured later.
|
|
128
|
+
*
|
|
129
|
+
* The following components are configured: KeyDeriver, WalletStorageManager, WalletService, WalletStorage.
|
|
130
|
+
* Optionally, PrivilegedKeyManager is also configured.
|
|
131
|
+
*
|
|
132
|
+
* @publicbody
|
|
133
|
+
*/
|
|
134
|
+
static async createWallet(args: SetupWalletArgs): Promise<SetupWallet> {
|
|
135
|
+
args.chain ||= args.env.chain
|
|
136
|
+
args.rootKeyHex ||= args.env.devKeys[args.env.identityKey]
|
|
213
137
|
const rootKey = PrivateKey.fromHex(args.rootKeyHex)
|
|
214
138
|
const identityKey = rootKey.toPublicKey().toString()
|
|
215
139
|
const keyDeriver = new KeyDeriver(rootKey)
|
|
@@ -220,6 +144,8 @@ export abstract class SetupClient {
|
|
|
220
144
|
args.backups
|
|
221
145
|
)
|
|
222
146
|
if (storage.stores.length > 0) await storage.makeAvailable()
|
|
147
|
+
const serviceOptions = Services.createDefaultOptions(chain)
|
|
148
|
+
serviceOptions.taalApiKey = args.env.taalApiKey
|
|
223
149
|
const services = new Services(args.chain)
|
|
224
150
|
const monopts = Monitor.createDefaultWalletMonitorOptions(
|
|
225
151
|
chain,
|
|
@@ -228,10 +154,10 @@ export abstract class SetupClient {
|
|
|
228
154
|
)
|
|
229
155
|
const monitor = new Monitor(monopts)
|
|
230
156
|
monitor.addDefaultTasks()
|
|
231
|
-
let privilegedKeyManager: PrivilegedKeyManager | undefined = undefined
|
|
157
|
+
let privilegedKeyManager: sdk.PrivilegedKeyManager | undefined = undefined
|
|
232
158
|
if (args.privKeyHex) {
|
|
233
159
|
const privKey = PrivateKey.fromString(args.privKeyHex)
|
|
234
|
-
privilegedKeyManager = new PrivilegedKeyManager(async () => privKey)
|
|
160
|
+
privilegedKeyManager = new sdk.PrivilegedKeyManager(async () => privKey)
|
|
235
161
|
}
|
|
236
162
|
const wallet = new Wallet({
|
|
237
163
|
chain,
|
|
@@ -241,7 +167,7 @@ export abstract class SetupClient {
|
|
|
241
167
|
monitor,
|
|
242
168
|
privilegedKeyManager
|
|
243
169
|
})
|
|
244
|
-
const r:
|
|
170
|
+
const r: SetupWallet = {
|
|
245
171
|
rootKey,
|
|
246
172
|
identityKey,
|
|
247
173
|
keyDeriver,
|
|
@@ -254,36 +180,171 @@ export abstract class SetupClient {
|
|
|
254
180
|
return r
|
|
255
181
|
}
|
|
256
182
|
|
|
257
|
-
static async createWalletWithStorageClient(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (args.chain === 'main')
|
|
183
|
+
static async createWalletWithStorageClient(
|
|
184
|
+
args: SetupWalletClientArgs
|
|
185
|
+
): Promise<SetupWalletClient> {
|
|
186
|
+
const wo = await Setup.createWallet(args)
|
|
187
|
+
if (wo.chain === 'main')
|
|
263
188
|
throw new sdk.WERR_INVALID_PARAMETER(
|
|
264
189
|
'chain',
|
|
265
190
|
`'test' for now, 'main' is not yet supported.`
|
|
266
191
|
)
|
|
267
192
|
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
})
|
|
272
|
-
args.endpointUrl ||= 'https://staging-dojo.babbage.systems'
|
|
273
|
-
const client = new StorageClient(wo.wallet, args.endpointUrl)
|
|
193
|
+
const endpointUrl =
|
|
194
|
+
args.endpointUrl || 'https://staging-dojo.babbage.systems'
|
|
195
|
+
const client = new StorageClient(wo.wallet, endpointUrl)
|
|
274
196
|
await wo.storage.addWalletStorageProvider(client)
|
|
275
197
|
await wo.storage.makeAvailable()
|
|
276
|
-
return
|
|
198
|
+
return {
|
|
199
|
+
...wo,
|
|
200
|
+
endpointUrl
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static getKeyPair(priv?: string | PrivateKey): KeyPairAddress {
|
|
205
|
+
if (priv === undefined) priv = PrivateKey.fromRandom()
|
|
206
|
+
else if (typeof priv === 'string') priv = new PrivateKey(priv, 'hex')
|
|
207
|
+
|
|
208
|
+
const pub = PublicKey.fromPrivateKey(priv)
|
|
209
|
+
const address = pub.toAddress()
|
|
210
|
+
return { privateKey: priv, publicKey: pub, address }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static getLockP2PKH(address: string) {
|
|
214
|
+
const p2pkh = new P2PKH()
|
|
215
|
+
const lock = p2pkh.lock(address)
|
|
216
|
+
return lock
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static getUnlockP2PKH(
|
|
220
|
+
priv: PrivateKey,
|
|
221
|
+
satoshis: number
|
|
222
|
+
): sdk.ScriptTemplateUnlock {
|
|
223
|
+
const p2pkh = new P2PKH()
|
|
224
|
+
const lock = Setup.getLockP2PKH(Setup.getKeyPair(priv).address)
|
|
225
|
+
// Prepare to pay with SIGHASH_ALL and without ANYONE_CAN_PAY.
|
|
226
|
+
// In otherwords:
|
|
227
|
+
// - all outputs must remain in the current order, amount and locking scripts.
|
|
228
|
+
// - all inputs must remain from the current outpoints and sequence numbers.
|
|
229
|
+
// (unlock scripts are never signed)
|
|
230
|
+
const unlock = p2pkh.unlock(priv, 'all', false, satoshis, lock)
|
|
231
|
+
return unlock
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static createP2PKHOutputs(
|
|
235
|
+
outputs: {
|
|
236
|
+
address: string
|
|
237
|
+
satoshis: number
|
|
238
|
+
outputDescription?: string
|
|
239
|
+
basket?: string
|
|
240
|
+
tags?: string[]
|
|
241
|
+
}[]
|
|
242
|
+
): CreateActionOutput[] {
|
|
243
|
+
const os: CreateActionOutput[] = []
|
|
244
|
+
const count = outputs.length
|
|
245
|
+
for (let i = 0; i < count; i++) {
|
|
246
|
+
const o = outputs[i]
|
|
247
|
+
os.push({
|
|
248
|
+
basket: o.basket,
|
|
249
|
+
tags: o.tags,
|
|
250
|
+
satoshis: o.satoshis,
|
|
251
|
+
lockingScript: Setup.getLockP2PKH(o.address).toHex(),
|
|
252
|
+
outputDescription: o.outputDescription || `p2pkh ${i}`
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
return os
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static async createP2PKHOutputsAction(
|
|
259
|
+
wallet: WalletInterface,
|
|
260
|
+
outputs: {
|
|
261
|
+
address: string
|
|
262
|
+
satoshis: number
|
|
263
|
+
outputDescription?: string
|
|
264
|
+
basket?: string
|
|
265
|
+
tags?: string[]
|
|
266
|
+
}[],
|
|
267
|
+
options?: CreateActionOptions
|
|
268
|
+
): Promise<{
|
|
269
|
+
cr: CreateActionResult
|
|
270
|
+
outpoints: string[] | undefined
|
|
271
|
+
}> {
|
|
272
|
+
const os = Setup.createP2PKHOutputs(outputs)
|
|
273
|
+
|
|
274
|
+
const createArgs: CreateActionArgs = {
|
|
275
|
+
description: `createP2PKHOutputs`,
|
|
276
|
+
outputs: os,
|
|
277
|
+
options: {
|
|
278
|
+
...options,
|
|
279
|
+
// Don't randomize so we can simplify outpoint creation
|
|
280
|
+
randomizeOutputs: false
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const cr = await wallet.createAction(createArgs)
|
|
285
|
+
|
|
286
|
+
let outpoints: string[] | undefined
|
|
287
|
+
|
|
288
|
+
if (cr.txid) {
|
|
289
|
+
outpoints = os.map((o, i) => `${cr.txid}.${i}`)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return { cr, outpoints }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
static async fundWalletFromP2PKHOutpoints(
|
|
296
|
+
wallet: WalletInterface,
|
|
297
|
+
outpoints: string[],
|
|
298
|
+
p2pkhKey: KeyPairAddress,
|
|
299
|
+
inputBEEF?: BEEF
|
|
300
|
+
) {
|
|
301
|
+
// TODO
|
|
277
302
|
}
|
|
278
303
|
}
|
|
279
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Enables code that imports only from `SetupClient` to still reference everything as just `Setup`
|
|
307
|
+
*/
|
|
308
|
+
export class Setup extends SetupClient {}
|
|
309
|
+
|
|
280
310
|
export type KeyPairAddress = {
|
|
281
311
|
privateKey: PrivateKey
|
|
282
312
|
publicKey: PublicKey
|
|
283
313
|
address: string
|
|
284
314
|
}
|
|
285
315
|
|
|
286
|
-
export interface
|
|
316
|
+
export interface SetupEnv {
|
|
317
|
+
chain: sdk.Chain
|
|
318
|
+
identityKey: string
|
|
319
|
+
identityKey2: string
|
|
320
|
+
taalApiKey: string
|
|
321
|
+
devKeys: Record<string, string>
|
|
322
|
+
mySQLConnection: string
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Arguments used to construct a `Wallet`
|
|
327
|
+
*
|
|
328
|
+
* @param env Configuration "secrets" typically obtained by `Setup.makeEnv` and `Setup.getEnv` functions.
|
|
329
|
+
* @param chain Optional. Which chain this wallet is on: 'main' or 'test'.
|
|
330
|
+
* Defaults to `env.chain`.
|
|
331
|
+
* @param rootKeyHex Optional. The non-privileged private key used to initialize the `KeyDeriver` and determine the `identityKey`.
|
|
332
|
+
* Defaults to `env.devKeys[env.identityKey]
|
|
333
|
+
* @param privKeyHex Optional. The privileged private key used to initialize the `PrivilegedKeyManager`.
|
|
334
|
+
* Defaults to undefined.
|
|
335
|
+
* @param active. Optional. Active wallet storage. Can be added later.
|
|
336
|
+
* @param backups. Optional. One or more storage providers managed as backup destinations. Can be added later.
|
|
337
|
+
*/
|
|
338
|
+
export interface SetupWalletArgs {
|
|
339
|
+
env: SetupEnv
|
|
340
|
+
chain?: sdk.Chain
|
|
341
|
+
rootKeyHex?: string
|
|
342
|
+
privKeyHex?: string
|
|
343
|
+
active?: sdk.WalletStorageProvider
|
|
344
|
+
backups?: sdk.WalletStorageProvider[]
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface SetupWallet {
|
|
287
348
|
rootKey: PrivateKey
|
|
288
349
|
identityKey: string
|
|
289
350
|
keyDeriver: KeyDeriver
|
|
@@ -293,3 +354,11 @@ export interface SetupWalletOnly {
|
|
|
293
354
|
monitor: Monitor
|
|
294
355
|
wallet: Wallet
|
|
295
356
|
}
|
|
357
|
+
|
|
358
|
+
export interface SetupWalletClientArgs extends SetupWalletArgs {
|
|
359
|
+
endpointUrl?: string
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export interface SetupWalletClient extends SetupWallet {
|
|
363
|
+
endpointUrl: string
|
|
364
|
+
}
|
package/src/Wallet.ts
CHANGED
|
@@ -101,6 +101,16 @@ export class Wallet implements WalletInterface, ProtoWallet {
|
|
|
101
101
|
|
|
102
102
|
identityKey: string
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The wallet creates a `BeefParty` when it is created.
|
|
106
|
+
* All the Beefs that pass through the wallet are merged into this beef.
|
|
107
|
+
* Thus what it contains at any time is the union of all transactions and proof data processed.
|
|
108
|
+
* The class `BeefParty` derives from `Beef`, adding the ability to track the source of merged data.
|
|
109
|
+
*
|
|
110
|
+
* This allows it to generate beefs to send to a particular “party” (storage or the user)
|
|
111
|
+
* that includes “txid only proofs” for transactions they already know about.
|
|
112
|
+
* Over time, this allows an active wallet to drastically reduce the amount of data transmitted.
|
|
113
|
+
*/
|
|
104
114
|
beef: BeefParty
|
|
105
115
|
trustSelf?: TrustSelf
|
|
106
116
|
userParty: string
|
package/src/index.all.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export * as sdk from './sdk/index'
|
|
2
2
|
export * from './utility/index.all'
|
|
3
3
|
export * from './Wallet'
|
|
4
|
-
export
|
|
4
|
+
export {
|
|
5
|
+
SetupClient,
|
|
6
|
+
KeyPairAddress,
|
|
7
|
+
SetupEnv,
|
|
8
|
+
SetupWalletArgs,
|
|
9
|
+
SetupWallet,
|
|
10
|
+
SetupWalletClient,
|
|
11
|
+
SetupWalletClientArgs
|
|
12
|
+
} from './SetupClient'
|
|
5
13
|
export * from './Setup'
|
|
6
14
|
export * from './signer/WalletSigner'
|
|
7
15
|
export * from './storage/index.all'
|
package/src/sdk/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Transaction, UnlockingScript } from '@bsv/sdk'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Identifies a unique transaction output by its `txid` and index `vout`
|
|
3
5
|
*/
|
|
@@ -113,3 +115,8 @@ export interface EntityTimeStamp {
|
|
|
113
115
|
created_at: Date
|
|
114
116
|
updated_at: Date
|
|
115
117
|
}
|
|
118
|
+
|
|
119
|
+
export interface ScriptTemplateUnlock {
|
|
120
|
+
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
121
|
+
estimateLength: (tx: Transaction, inputIndex: number) => Promise<number>
|
|
122
|
+
}
|