@bsv/sdk 1.4.17 → 1.4.19
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/cjs/package.json +1 -1
- package/dist/cjs/src/kvstore/LocalKVStore.js +152 -141
- package/dist/cjs/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/cjs/src/storage/StorageUploader.js +122 -14
- package/dist/cjs/src/storage/StorageUploader.js.map +1 -1
- package/dist/cjs/src/storage/__test/StorageUploader.test.js +85 -14
- package/dist/cjs/src/storage/__test/StorageUploader.test.js.map +1 -1
- package/dist/cjs/src/wallet/WERR_REVIEW_ACTIONS.js +29 -0
- package/dist/cjs/src/wallet/WERR_REVIEW_ACTIONS.js.map +1 -0
- package/dist/cjs/src/wallet/WalletError.js +4 -3
- package/dist/cjs/src/wallet/WalletError.js.map +1 -1
- package/dist/cjs/src/wallet/index.js +4 -1
- package/dist/cjs/src/wallet/index.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js +13 -6
- package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/kvstore/LocalKVStore.js +151 -141
- package/dist/esm/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/esm/src/storage/StorageUploader.js +119 -14
- package/dist/esm/src/storage/StorageUploader.js.map +1 -1
- package/dist/esm/src/storage/__test/StorageUploader.test.js +85 -14
- package/dist/esm/src/storage/__test/StorageUploader.test.js.map +1 -1
- package/dist/esm/src/wallet/WERR_REVIEW_ACTIONS.js +31 -0
- package/dist/esm/src/wallet/WERR_REVIEW_ACTIONS.js.map +1 -0
- package/dist/esm/src/wallet/WalletError.js +3 -2
- package/dist/esm/src/wallet/WalletError.js.map +1 -1
- package/dist/esm/src/wallet/index.js +2 -0
- package/dist/esm/src/wallet/index.js.map +1 -1
- package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js +13 -6
- package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/kvstore/LocalKVStore.d.ts +10 -4
- package/dist/types/src/kvstore/LocalKVStore.d.ts.map +1 -1
- package/dist/types/src/storage/StorageUploader.d.ts +77 -14
- package/dist/types/src/storage/StorageUploader.d.ts.map +1 -1
- package/dist/types/src/wallet/WERR_REVIEW_ACTIONS.d.ts +23 -0
- package/dist/types/src/wallet/WERR_REVIEW_ACTIONS.d.ts.map +1 -0
- package/dist/types/src/wallet/Wallet.interfaces.d.ts +22 -0
- package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
- package/dist/types/src/wallet/WalletError.d.ts +4 -3
- package/dist/types/src/wallet/WalletError.d.ts.map +1 -1
- package/dist/types/src/wallet/index.d.ts +1 -0
- package/dist/types/src/wallet/index.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/kvstore.md +9 -8
- package/docs/storage.md +117 -7
- package/docs/wallet.md +146 -38
- package/package.json +1 -1
- package/src/kvstore/LocalKVStore.ts +156 -151
- package/src/kvstore/__tests/LocalKVStore.test.ts +104 -193
- package/src/storage/StorageUploader.ts +156 -14
- package/src/storage/__test/StorageUploader.test.ts +134 -15
- package/src/wallet/WERR_REVIEW_ACTIONS.ts +30 -0
- package/src/wallet/Wallet.interfaces.ts +24 -0
- package/src/wallet/WalletError.ts +4 -2
- package/src/wallet/index.ts +2 -0
- package/src/wallet/substrates/HTTPWalletJSON.ts +12 -6
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import LockingScript from '../script/LockingScript.js'
|
|
2
2
|
import PushDrop from '../script/templates/PushDrop.js'
|
|
3
3
|
import * as Utils from '../primitives/utils.js'
|
|
4
|
-
import { WalletInterface, OutpointString, CreateActionInput, SignActionSpend } from '../wallet/Wallet.interfaces.js'
|
|
4
|
+
import { WalletInterface, OutpointString, CreateActionInput, SignActionSpend, WalletProtocol, ListOutputsResult, WalletOutput, AtomicBEEF } from '../wallet/Wallet.interfaces.js'
|
|
5
5
|
import WalletClient from '../wallet/WalletClient.js'
|
|
6
6
|
import Transaction from '../transaction/Transaction.js'
|
|
7
|
+
import { Beef } from '../transaction/Beef.js'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Implements a key-value storage system backed by transaction outputs managed by a wallet.
|
|
@@ -41,14 +42,14 @@ export default class LocalKVStore {
|
|
|
41
42
|
* Creates an instance of the localKVStore.
|
|
42
43
|
*
|
|
43
44
|
* @param {WalletInterface} [wallet=new WalletClient()] - The wallet interface to use. Defaults to a new WalletClient instance.
|
|
44
|
-
* @param {string} [context='
|
|
45
|
+
* @param {string} [context='kvstoredefault'] - The context (basket) for namespacing keys. Defaults to 'kvstore default'.
|
|
45
46
|
* @param {boolean} [encrypt=true] - Whether to encrypt values. Defaults to true.
|
|
46
47
|
* @param {string} [originator] — An originator to use with PushDrop and the wallet, if provided.
|
|
47
48
|
* @throws {Error} If the context is missing or empty.
|
|
48
49
|
*/
|
|
49
50
|
constructor (
|
|
50
51
|
wallet: WalletInterface = new WalletClient(),
|
|
51
|
-
context = 'kvstore
|
|
52
|
+
context = 'kvstore default',
|
|
52
53
|
encrypt = true,
|
|
53
54
|
originator?: string
|
|
54
55
|
) {
|
|
@@ -61,6 +62,21 @@ export default class LocalKVStore {
|
|
|
61
62
|
this.originator = originator
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
private getProtocol (key: string): { protocolID: WalletProtocol, keyID: string } {
|
|
66
|
+
return { protocolID: [2, this.context], keyID: key }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async getOutputs (key: string, limit?: number): Promise<ListOutputsResult> {
|
|
70
|
+
const results = await this.wallet.listOutputs({
|
|
71
|
+
basket: this.context,
|
|
72
|
+
tags: [key],
|
|
73
|
+
tagQueryMode: 'all',
|
|
74
|
+
include: 'entire transactions',
|
|
75
|
+
limit
|
|
76
|
+
})
|
|
77
|
+
return results
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
/**
|
|
65
81
|
* Retrieves the value associated with a given key.
|
|
66
82
|
*
|
|
@@ -68,43 +84,79 @@ export default class LocalKVStore {
|
|
|
68
84
|
* @param {string | undefined} [defaultValue=undefined] - The value to return if the key is not found.
|
|
69
85
|
* @returns {Promise<string | undefined>} A promise that resolves to the value as a string,
|
|
70
86
|
* the defaultValue if the key is not found, or undefined if no defaultValue is provided.
|
|
71
|
-
* @throws {Error} If
|
|
87
|
+
* @throws {Error} If too many outputs are found for the key (ambiguous state).
|
|
72
88
|
* @throws {Error} If the found output's locking script cannot be decoded or represents an invalid token format.
|
|
73
89
|
*/
|
|
74
90
|
async get (key: string, defaultValue: string | undefined = undefined): Promise<string | undefined> {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
const r = await this.lookupValue(key, defaultValue, 5)
|
|
92
|
+
return r.value
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private getLockingScript (output: WalletOutput, beef: Beef): LockingScript {
|
|
96
|
+
const [txid, vout] = output.outpoint.split('.')
|
|
97
|
+
const tx = beef.findTxid(txid)?.tx
|
|
98
|
+
if (tx == null) { throw new Error(`beef must contain txid ${txid}`) }
|
|
99
|
+
const lockingScript = tx.outputs[Number(vout)].lockingScript
|
|
100
|
+
return lockingScript
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private async lookupValue (key: string, defaultValue: string | undefined, limit?: number): Promise<LookupValueResult> {
|
|
104
|
+
const lor = await this.getOutputs(key, limit)
|
|
105
|
+
const r: LookupValueResult = { value: defaultValue, outpoint: undefined, lor }
|
|
106
|
+
const { outputs } = lor
|
|
107
|
+
if (outputs.length === 0) {
|
|
108
|
+
return r
|
|
84
109
|
}
|
|
85
|
-
|
|
110
|
+
const output = outputs.slice(-1)[0]
|
|
111
|
+
r.outpoint = output.outpoint
|
|
112
|
+
let field: number[]
|
|
86
113
|
try {
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (decoded.fields.length !== 1) {
|
|
114
|
+
if (lor.BEEF === undefined) { throw new Error('entire transactions listOutputs option must return valid BEEF') }
|
|
115
|
+
const lockingScript = this.getLockingScript(output, Beef.fromBinary(lor.BEEF))
|
|
116
|
+
const decoded = PushDrop.decode(lockingScript)
|
|
117
|
+
if (decoded.fields.length < 1 || decoded.fields.length > 2) {
|
|
92
118
|
throw new Error('Invalid token.')
|
|
93
119
|
}
|
|
94
|
-
|
|
120
|
+
field = decoded.fields[0]
|
|
95
121
|
} catch (_) {
|
|
96
|
-
throw new Error(`Invalid value found. You need to call set to collapse the corrupted state (or relinquish the corrupted ${
|
|
122
|
+
throw new Error(`Invalid value found. You need to call set to collapse the corrupted state (or relinquish the corrupted ${outputs[0].outpoint} output from the ${this.context} basket) before you can get this value again.`)
|
|
97
123
|
}
|
|
98
124
|
if (!this.encrypt) {
|
|
99
|
-
|
|
125
|
+
r.value = Utils.toUTF8(field)
|
|
100
126
|
} else {
|
|
101
127
|
const { plaintext } = await this.wallet.decrypt({
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
...this.getProtocol(key),
|
|
129
|
+
ciphertext: field
|
|
130
|
+
})
|
|
131
|
+
r.value = Utils.toUTF8(plaintext)
|
|
132
|
+
}
|
|
133
|
+
return r
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private getInputs (outputs: WalletOutput[]): CreateActionInput[] {
|
|
137
|
+
const inputs: CreateActionInput[] = []
|
|
138
|
+
for (let i = 0; i < outputs.length; i++) {
|
|
139
|
+
inputs.push({
|
|
140
|
+
outpoint: outputs[i].outpoint,
|
|
141
|
+
unlockingScriptLength: 74,
|
|
142
|
+
inputDescription: 'Previous key-value token'
|
|
105
143
|
})
|
|
106
|
-
return Utils.toUTF8(plaintext)
|
|
107
144
|
}
|
|
145
|
+
return inputs
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private async getSpends (key: string, outputs: WalletOutput[], pushdrop: PushDrop, atomicBEEF: AtomicBEEF): Promise<Record<number, SignActionSpend>> {
|
|
149
|
+
const p = this.getProtocol(key)
|
|
150
|
+
const tx = Transaction.fromAtomicBEEF(atomicBEEF)
|
|
151
|
+
const spends: Record<number, SignActionSpend> = {}
|
|
152
|
+
for (let i = 0; i < outputs.length; i++) {
|
|
153
|
+
const unlocker = pushdrop.unlock(p.protocolID, p.keyID, 'self')
|
|
154
|
+
const unlockingScript = await unlocker.sign(tx, i)
|
|
155
|
+
spends[i] = {
|
|
156
|
+
unlockingScript: unlockingScript.toHex()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return spends
|
|
108
160
|
}
|
|
109
161
|
|
|
110
162
|
/**
|
|
@@ -121,95 +173,65 @@ export default class LocalKVStore {
|
|
|
121
173
|
* @returns {Promise<OutpointString>} A promise that resolves to the outpoint string (txid.vout) of the new or updated token output.
|
|
122
174
|
*/
|
|
123
175
|
async set (key: string, value: string): Promise<OutpointString> {
|
|
176
|
+
const current = await this.lookupValue(key, undefined, 10)
|
|
177
|
+
if (current.value === value) {
|
|
178
|
+
if (current.outpoint === undefined) { throw new Error('outpoint must be valid when value is valid and unchanged') }
|
|
179
|
+
// Don't create a new transaction if the value doesn't need to change...
|
|
180
|
+
return current.outpoint
|
|
181
|
+
}
|
|
182
|
+
const protocol = this.getProtocol(key)
|
|
124
183
|
let valueAsArray = Utils.toArray(value, 'utf8')
|
|
125
184
|
if (this.encrypt) {
|
|
126
185
|
const { ciphertext } = await this.wallet.encrypt({
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
keyID: key
|
|
186
|
+
...protocol,
|
|
187
|
+
plaintext: valueAsArray
|
|
130
188
|
})
|
|
131
189
|
valueAsArray = ciphertext
|
|
132
190
|
}
|
|
133
191
|
const pushdrop = new PushDrop(this.wallet, this.originator)
|
|
134
192
|
const lockingScript = await pushdrop.lock(
|
|
135
193
|
[valueAsArray],
|
|
136
|
-
|
|
137
|
-
|
|
194
|
+
protocol.protocolID,
|
|
195
|
+
protocol.keyID,
|
|
138
196
|
'self'
|
|
139
197
|
)
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
inputs,
|
|
159
|
-
outputs: [{
|
|
160
|
-
lockingScript: lockingScript.toHex(),
|
|
161
|
-
satoshis: 1,
|
|
162
|
-
outputDescription: 'Key-value token'
|
|
163
|
-
}],
|
|
164
|
-
options: {
|
|
165
|
-
acceptDelayedBroadcast: false,
|
|
166
|
-
randomizeOutputs: false
|
|
167
|
-
}
|
|
168
|
-
})
|
|
169
|
-
if (typeof signableTransaction !== 'object') {
|
|
170
|
-
throw new Error('Wallet did not return a signable transaction when expected.')
|
|
171
|
-
}
|
|
172
|
-
const tx = Transaction.fromAtomicBEEF(signableTransaction.tx)
|
|
173
|
-
const spends: Record<number, SignActionSpend> = {}
|
|
174
|
-
for (let i = 0; i < results.outputs.length; i++) {
|
|
175
|
-
const unlocker = pushdrop.unlock(
|
|
176
|
-
[2, this.context],
|
|
177
|
-
key,
|
|
178
|
-
'self'
|
|
179
|
-
)
|
|
180
|
-
const unlockingScript = await unlocker.sign(tx, i)
|
|
181
|
-
spends[i] = {
|
|
182
|
-
unlockingScript: unlockingScript.toHex()
|
|
183
|
-
}
|
|
198
|
+
const { outputs, BEEF: inputBEEF } = current.lor
|
|
199
|
+
let outpoint: OutpointString
|
|
200
|
+
try {
|
|
201
|
+
const inputs = this.getInputs(outputs)
|
|
202
|
+
const { txid, signableTransaction } = await this.wallet.createAction({
|
|
203
|
+
description: `Update ${key} in ${this.context}`,
|
|
204
|
+
inputBEEF,
|
|
205
|
+
inputs,
|
|
206
|
+
outputs: [{
|
|
207
|
+
basket: this.context,
|
|
208
|
+
tags: [key],
|
|
209
|
+
lockingScript: lockingScript.toHex(),
|
|
210
|
+
satoshis: 1,
|
|
211
|
+
outputDescription: 'Key-value token'
|
|
212
|
+
}],
|
|
213
|
+
options: {
|
|
214
|
+
acceptDelayedBroadcast: false,
|
|
215
|
+
randomizeOutputs: false
|
|
184
216
|
}
|
|
217
|
+
})
|
|
218
|
+
if (outputs.length > 0 && typeof signableTransaction !== 'object') {
|
|
219
|
+
throw new Error('Wallet did not return a signable transaction when expected.')
|
|
220
|
+
}
|
|
221
|
+
if (signableTransaction == null) {
|
|
222
|
+
outpoint = `${txid as string}.0`
|
|
223
|
+
} else {
|
|
224
|
+
const spends = await this.getSpends(key, outputs, pushdrop, signableTransaction.tx)
|
|
185
225
|
const { txid } = await this.wallet.signAction({
|
|
186
226
|
reference: signableTransaction.reference,
|
|
187
227
|
spends
|
|
188
228
|
})
|
|
189
|
-
|
|
190
|
-
} catch (_) {
|
|
191
|
-
// Signing failed, relinquish original outputs
|
|
192
|
-
for (let i = 0; i < results.outputs.length; i++) {
|
|
193
|
-
await this.wallet.relinquishOutput({
|
|
194
|
-
output: results.outputs[i].outpoint,
|
|
195
|
-
basket: this.context
|
|
196
|
-
})
|
|
197
|
-
}
|
|
229
|
+
outpoint = `${txid as string}.0`
|
|
198
230
|
}
|
|
231
|
+
} catch (_) {
|
|
232
|
+
throw new Error(`There are ${outputs.length} outputs with tag ${key} that cannot be unlocked.`)
|
|
199
233
|
}
|
|
200
|
-
|
|
201
|
-
description: `Set ${key} in ${this.context}`,
|
|
202
|
-
outputs: [{
|
|
203
|
-
lockingScript: lockingScript.toHex(),
|
|
204
|
-
satoshis: 1,
|
|
205
|
-
outputDescription: 'Key-value token'
|
|
206
|
-
}],
|
|
207
|
-
options: {
|
|
208
|
-
acceptDelayedBroadcast: false,
|
|
209
|
-
randomizeOutputs: false
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
return `${txid as string}.0`
|
|
234
|
+
return outpoint
|
|
213
235
|
}
|
|
214
236
|
|
|
215
237
|
/**
|
|
@@ -220,63 +242,46 @@ export default class LocalKVStore {
|
|
|
220
242
|
* If signing the removal transaction fails, it relinquishes the original outputs instead of spending.
|
|
221
243
|
*
|
|
222
244
|
* @param {string} key - The key to remove.
|
|
223
|
-
* @returns {Promise<string
|
|
245
|
+
* @returns {Promise<string[]>} A promise that resolves to the txids of the removal transactions if successful.
|
|
224
246
|
*/
|
|
225
|
-
async remove (key: string): Promise<
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
throw new Error('Wallet did not return a signable transaction when expected.')
|
|
254
|
-
}
|
|
255
|
-
const tx = Transaction.fromAtomicBEEF(signableTransaction.tx)
|
|
256
|
-
const spends: Record<number, SignActionSpend> = {}
|
|
257
|
-
for (let i = 0; i < results.outputs.length; i++) {
|
|
258
|
-
const unlocker = pushdrop.unlock(
|
|
259
|
-
[2, this.context],
|
|
260
|
-
key,
|
|
261
|
-
'self'
|
|
262
|
-
)
|
|
263
|
-
const unlockingScript = await unlocker.sign(tx, i)
|
|
264
|
-
spends[i] = {
|
|
265
|
-
unlockingScript: unlockingScript.toHex()
|
|
247
|
+
async remove (key: string): Promise<string[]> {
|
|
248
|
+
const txids: string[] = []
|
|
249
|
+
for (; ;) {
|
|
250
|
+
const { outputs, BEEF: inputBEEF, totalOutputs } = await this.getOutputs(key)
|
|
251
|
+
if (outputs.length > 0) {
|
|
252
|
+
const pushdrop = new PushDrop(this.wallet, this.originator)
|
|
253
|
+
try {
|
|
254
|
+
const inputs = this.getInputs(outputs)
|
|
255
|
+
const { signableTransaction } = await this.wallet.createAction({
|
|
256
|
+
description: `Remove ${key} in ${this.context}`,
|
|
257
|
+
inputBEEF,
|
|
258
|
+
inputs,
|
|
259
|
+
options: {
|
|
260
|
+
acceptDelayedBroadcast: false
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
if (typeof signableTransaction !== 'object') {
|
|
264
|
+
throw new Error('Wallet did not return a signable transaction when expected.')
|
|
265
|
+
}
|
|
266
|
+
const spends = await this.getSpends(key, outputs, pushdrop, signableTransaction.tx)
|
|
267
|
+
const { txid } = await this.wallet.signAction({
|
|
268
|
+
reference: signableTransaction.reference,
|
|
269
|
+
spends
|
|
270
|
+
})
|
|
271
|
+
if (txid === undefined) { throw new Error('signAction must return a valid txid') }
|
|
272
|
+
txids.push(txid)
|
|
273
|
+
} catch (_) {
|
|
274
|
+
throw new Error(`There are ${totalOutputs} outputs with tag ${key} that cannot be unlocked.`)
|
|
266
275
|
}
|
|
267
276
|
}
|
|
268
|
-
|
|
269
|
-
reference: signableTransaction.reference,
|
|
270
|
-
spends
|
|
271
|
-
})
|
|
272
|
-
return txid
|
|
273
|
-
} catch (_) {
|
|
274
|
-
for (let i = 0; i < results.outputs.length; i++) {
|
|
275
|
-
await this.wallet.relinquishOutput({
|
|
276
|
-
output: results.outputs[i].outpoint,
|
|
277
|
-
basket: this.context
|
|
278
|
-
})
|
|
279
|
-
}
|
|
277
|
+
if (outputs.length === totalOutputs) { break }
|
|
280
278
|
}
|
|
279
|
+
return txids
|
|
281
280
|
}
|
|
282
281
|
}
|
|
282
|
+
|
|
283
|
+
interface LookupValueResult {
|
|
284
|
+
value: string | undefined
|
|
285
|
+
outpoint: OutpointString | undefined
|
|
286
|
+
lor: ListOutputsResult
|
|
287
|
+
}
|