@buildonspark/issuer-sdk 0.1.6 → 0.1.7
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/CHANGELOG.md +7 -0
- package/dist/index.browser.d.ts +244 -238
- package/dist/index.browser.js +629 -917
- package/dist/index.node.cjs +654 -935
- package/dist/index.node.d.cts +248 -239
- package/dist/index.node.d.ts +248 -239
- package/dist/index.node.js +635 -914
- package/dist/native/index.react-native.cjs +684 -925
- package/dist/native/index.react-native.d.cts +245 -239
- package/dist/native/index.react-native.d.ts +245 -239
- package/dist/native/index.react-native.js +665 -913
- package/dist/proto/spark.cjs +6 -37
- package/dist/proto/spark.d.cts +1 -4
- package/dist/proto/spark.d.ts +1 -4
- package/dist/proto/spark.js +2 -3
- package/dist/proto/spark_token.cjs +6 -37
- package/dist/proto/spark_token.d.cts +1 -1
- package/dist/proto/spark_token.d.ts +1 -1
- package/dist/proto/spark_token.js +2 -3
- package/dist/tests/test-utils.cjs +6 -37
- package/dist/tests/test-utils.d.cts +1 -1
- package/dist/tests/test-utils.d.ts +1 -1
- package/dist/tests/test-utils.js +2 -3
- package/dist/types/index.cjs +6 -37
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +2 -3
- package/package.json +4 -4
- package/dist/chunk-7B4B24XF.js +0 -17
|
@@ -1,938 +1,690 @@
|
|
|
1
1
|
require("react-native-get-random-values");
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { DefaultSparkSigner, SparkError, SparkRequestError, SparkValidationError, SparkWallet, TokenTransactionService, UnsafeStatelessSparkSigner, WalletConfig, collectResponses, decodeBech32mTokenIdentifier, decodeSparkAddress, encodeBech32mTokenIdentifier, encodeSparkAddress, hashFinalTokenTransaction } from "@buildonspark/spark-sdk";
|
|
3
|
+
import { bytesToHex, bytesToNumberBE, hexToBytes, numberToBytesBE } from "@noble/curves/utils";
|
|
4
|
+
import { sha256 } from "@scure/btc-signer/utils";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (
|
|
9
|
-
|
|
6
|
+
//#region src/utils/token-hashing.ts
|
|
7
|
+
function hashFreezeTokensPayload(payload) {
|
|
8
|
+
if (!payload) throw new SparkValidationError("Freeze tokens payload cannot be nil", {
|
|
9
|
+
field: "payload",
|
|
10
|
+
value: payload,
|
|
11
|
+
expected: "valid freeze tokens payload"
|
|
12
|
+
});
|
|
13
|
+
let allHashes = [];
|
|
14
|
+
const versionHashObj = sha256.create();
|
|
15
|
+
const versionBytes = new Uint8Array(4);
|
|
16
|
+
new DataView(versionBytes.buffer).setUint32(0, payload.version, false);
|
|
17
|
+
versionHashObj.update(versionBytes);
|
|
18
|
+
allHashes.push(versionHashObj.digest());
|
|
19
|
+
const ownerPubKeyHash = sha256.create();
|
|
20
|
+
if (payload.ownerPublicKey) ownerPubKeyHash.update(payload.ownerPublicKey);
|
|
21
|
+
allHashes.push(ownerPubKeyHash.digest());
|
|
22
|
+
const tokenIdentifierHash = sha256.create();
|
|
23
|
+
if (payload.tokenIdentifier) tokenIdentifierHash.update(payload.tokenIdentifier);
|
|
24
|
+
allHashes.push(tokenIdentifierHash.digest());
|
|
25
|
+
const shouldUnfreezeHash = sha256.create();
|
|
26
|
+
shouldUnfreezeHash.update(new Uint8Array([payload.shouldUnfreeze ? 1 : 0]));
|
|
27
|
+
allHashes.push(shouldUnfreezeHash.digest());
|
|
28
|
+
const timestampHash = sha256.create();
|
|
29
|
+
if (payload.issuerProvidedTimestamp) {
|
|
30
|
+
const timestampBytes = new Uint8Array(8);
|
|
31
|
+
new DataView(timestampBytes.buffer).setBigUint64(0, BigInt(payload.issuerProvidedTimestamp), true);
|
|
32
|
+
timestampHash.update(timestampBytes);
|
|
33
|
+
}
|
|
34
|
+
allHashes.push(timestampHash.digest());
|
|
35
|
+
const operatorPubKeyHash = sha256.create();
|
|
36
|
+
if (payload.operatorIdentityPublicKey) operatorPubKeyHash.update(payload.operatorIdentityPublicKey);
|
|
37
|
+
allHashes.push(operatorPubKeyHash.digest());
|
|
38
|
+
const finalHash = sha256.create();
|
|
39
|
+
for (const hash of allHashes) finalHash.update(hash);
|
|
40
|
+
return finalHash.digest();
|
|
10
41
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/typeof.js
|
|
45
|
+
function _typeof(o) {
|
|
46
|
+
"@babel/helpers - typeof";
|
|
47
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
48
|
+
return typeof o$1;
|
|
49
|
+
} : function(o$1) {
|
|
50
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
51
|
+
}, _typeof(o);
|
|
18
52
|
}
|
|
19
53
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/toPrimitive.js
|
|
56
|
+
function toPrimitive(t, r) {
|
|
57
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
58
|
+
var e = t[Symbol.toPrimitive];
|
|
59
|
+
if (void 0 !== e) {
|
|
60
|
+
var i = e.call(t, r || "default");
|
|
61
|
+
if ("object" != _typeof(i)) return i;
|
|
62
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
63
|
+
}
|
|
64
|
+
return ("string" === r ? String : Number)(t);
|
|
65
|
+
}
|
|
32
66
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/toPropertyKey.js
|
|
69
|
+
function toPropertyKey(t) {
|
|
70
|
+
var i = toPrimitive(t, "string");
|
|
71
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
72
|
+
}
|
|
39
73
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
let allHashes = [];
|
|
52
|
-
const versionHashObj = sha256.create();
|
|
53
|
-
const versionBytes = new Uint8Array(4);
|
|
54
|
-
new DataView(versionBytes.buffer).setUint32(
|
|
55
|
-
0,
|
|
56
|
-
payload.version,
|
|
57
|
-
false
|
|
58
|
-
// false for big-endian
|
|
59
|
-
);
|
|
60
|
-
versionHashObj.update(versionBytes);
|
|
61
|
-
allHashes.push(versionHashObj.digest());
|
|
62
|
-
const ownerPubKeyHash = sha256.create();
|
|
63
|
-
if (payload.ownerPublicKey) {
|
|
64
|
-
ownerPubKeyHash.update(payload.ownerPublicKey);
|
|
65
|
-
}
|
|
66
|
-
allHashes.push(ownerPubKeyHash.digest());
|
|
67
|
-
const tokenIdentifierHash = sha256.create();
|
|
68
|
-
if (payload.tokenIdentifier) {
|
|
69
|
-
tokenIdentifierHash.update(payload.tokenIdentifier);
|
|
70
|
-
}
|
|
71
|
-
allHashes.push(tokenIdentifierHash.digest());
|
|
72
|
-
const shouldUnfreezeHash = sha256.create();
|
|
73
|
-
shouldUnfreezeHash.update(new Uint8Array([payload.shouldUnfreeze ? 1 : 0]));
|
|
74
|
-
allHashes.push(shouldUnfreezeHash.digest());
|
|
75
|
-
const timestampHash = sha256.create();
|
|
76
|
-
if (payload.issuerProvidedTimestamp) {
|
|
77
|
-
const timestampBytes = new Uint8Array(8);
|
|
78
|
-
new DataView(timestampBytes.buffer).setBigUint64(
|
|
79
|
-
0,
|
|
80
|
-
BigInt(payload.issuerProvidedTimestamp),
|
|
81
|
-
true
|
|
82
|
-
// true for little-endian
|
|
83
|
-
);
|
|
84
|
-
timestampHash.update(timestampBytes);
|
|
85
|
-
}
|
|
86
|
-
allHashes.push(timestampHash.digest());
|
|
87
|
-
const operatorPubKeyHash = sha256.create();
|
|
88
|
-
if (payload.operatorIdentityPublicKey) {
|
|
89
|
-
operatorPubKeyHash.update(payload.operatorIdentityPublicKey);
|
|
90
|
-
}
|
|
91
|
-
allHashes.push(operatorPubKeyHash.digest());
|
|
92
|
-
const finalHash = sha256.create();
|
|
93
|
-
for (const hash of allHashes) {
|
|
94
|
-
finalHash.update(hash);
|
|
95
|
-
}
|
|
96
|
-
return finalHash.digest();
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region \0@oxc-project+runtime@0.108.0/helpers/defineProperty.js
|
|
76
|
+
function _defineProperty(e, r, t) {
|
|
77
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
78
|
+
value: t,
|
|
79
|
+
enumerable: !0,
|
|
80
|
+
configurable: !0,
|
|
81
|
+
writable: !0
|
|
82
|
+
}) : e[r] = t, e;
|
|
97
83
|
}
|
|
98
84
|
|
|
99
|
-
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/services/freeze.ts
|
|
100
87
|
var TokenFreezeService = class {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
} catch (error) {
|
|
145
|
-
throw new SparkRequestError(
|
|
146
|
-
`Failed to send a freeze/unfreeze operation to operator: ${operator.address}`,
|
|
147
|
-
{ operation: "freeze_tokens", error }
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
);
|
|
152
|
-
const successfulResponses = collectResponses(freezeResponses);
|
|
153
|
-
return successfulResponses[0].response;
|
|
154
|
-
}
|
|
88
|
+
constructor(config, connectionManager) {
|
|
89
|
+
_defineProperty(this, "config", void 0);
|
|
90
|
+
_defineProperty(this, "connectionManager", void 0);
|
|
91
|
+
this.config = config;
|
|
92
|
+
this.connectionManager = connectionManager;
|
|
93
|
+
}
|
|
94
|
+
async freezeTokens({ ownerPublicKey, tokenIdentifier }) {
|
|
95
|
+
return this.freezeOperation(ownerPublicKey, false, tokenIdentifier);
|
|
96
|
+
}
|
|
97
|
+
async unfreezeTokens({ ownerPublicKey, tokenIdentifier }) {
|
|
98
|
+
return this.freezeOperation(ownerPublicKey, true, tokenIdentifier);
|
|
99
|
+
}
|
|
100
|
+
async freezeOperation(ownerPublicKey, shouldUnfreeze, tokenIdentifier) {
|
|
101
|
+
const signingOperators = this.config.getSigningOperators();
|
|
102
|
+
const issuerProvidedTimestamp = Date.now();
|
|
103
|
+
return collectResponses(await Promise.allSettled(Object.entries(signingOperators).map(async ([identifier, operator]) => {
|
|
104
|
+
const sparkTokenClient = await this.connectionManager.createSparkTokenClient(operator.address);
|
|
105
|
+
const freezeTokensPayload = {
|
|
106
|
+
version: 1,
|
|
107
|
+
ownerPublicKey,
|
|
108
|
+
tokenIdentifier,
|
|
109
|
+
shouldUnfreeze,
|
|
110
|
+
issuerProvidedTimestamp,
|
|
111
|
+
operatorIdentityPublicKey: hexToBytes(operator.identityPublicKey)
|
|
112
|
+
};
|
|
113
|
+
const hashedPayload = hashFreezeTokensPayload(freezeTokensPayload);
|
|
114
|
+
const issuerSignature = await this.config.signer.signMessageWithIdentityKey(hashedPayload);
|
|
115
|
+
try {
|
|
116
|
+
return {
|
|
117
|
+
identifier,
|
|
118
|
+
response: await sparkTokenClient.freeze_tokens({
|
|
119
|
+
freezeTokensPayload,
|
|
120
|
+
issuerSignature
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw new SparkRequestError(`Failed to send a freeze/unfreeze operation to operator: ${operator.address}`, {
|
|
125
|
+
operation: "freeze_tokens",
|
|
126
|
+
error
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
})))[0].response;
|
|
130
|
+
}
|
|
155
131
|
};
|
|
156
132
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
TokenTransactionService
|
|
160
|
-
} from "@buildonspark/spark-sdk";
|
|
161
|
-
import { numberToBytesBE } from "@noble/curves/utils";
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/services/token-transactions.ts
|
|
162
135
|
var IssuerTokenTransactionService = class extends TokenTransactionService {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
},
|
|
263
|
-
partialTokenOutputs: []
|
|
264
|
-
};
|
|
265
|
-
}
|
|
136
|
+
constructor(config, connectionManager) {
|
|
137
|
+
super(config, connectionManager);
|
|
138
|
+
}
|
|
139
|
+
async constructMintTokenTransaction(rawTokenIdentifierBytes, issuerTokenPublicKey, tokenAmount) {
|
|
140
|
+
return {
|
|
141
|
+
version: 2,
|
|
142
|
+
network: this.config.getNetworkProto(),
|
|
143
|
+
tokenInputs: {
|
|
144
|
+
$case: "mintInput",
|
|
145
|
+
mintInput: {
|
|
146
|
+
issuerPublicKey: issuerTokenPublicKey,
|
|
147
|
+
tokenIdentifier: rawTokenIdentifierBytes
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
tokenOutputs: [{
|
|
151
|
+
ownerPublicKey: issuerTokenPublicKey,
|
|
152
|
+
tokenIdentifier: rawTokenIdentifierBytes,
|
|
153
|
+
tokenAmount: numberToBytesBE(tokenAmount, 16)
|
|
154
|
+
}],
|
|
155
|
+
clientCreatedTimestamp: /* @__PURE__ */ new Date(),
|
|
156
|
+
sparkOperatorIdentityPublicKeys: super.collectOperatorIdentityPublicKeys(),
|
|
157
|
+
expiryTime: void 0,
|
|
158
|
+
invoiceAttachments: []
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async constructPartialMintTokenTransaction(rawTokenIdentifierBytes, issuerTokenPublicKey, tokenAmount) {
|
|
162
|
+
return {
|
|
163
|
+
version: 3,
|
|
164
|
+
tokenTransactionMetadata: {
|
|
165
|
+
network: this.config.getNetworkProto(),
|
|
166
|
+
sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys(),
|
|
167
|
+
validityDurationSeconds: await this.config.getTokenValidityDurationSeconds(),
|
|
168
|
+
clientCreatedTimestamp: this.connectionManager.getCurrentServerTime(),
|
|
169
|
+
invoiceAttachments: []
|
|
170
|
+
},
|
|
171
|
+
tokenInputs: {
|
|
172
|
+
$case: "mintInput",
|
|
173
|
+
mintInput: {
|
|
174
|
+
issuerPublicKey: issuerTokenPublicKey,
|
|
175
|
+
tokenIdentifier: rawTokenIdentifierBytes
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
partialTokenOutputs: [{
|
|
179
|
+
ownerPublicKey: issuerTokenPublicKey,
|
|
180
|
+
tokenIdentifier: rawTokenIdentifierBytes,
|
|
181
|
+
withdrawBondSats: this.config.getExpectedWithdrawBondSats(),
|
|
182
|
+
withdrawRelativeBlockLocktime: this.config.getExpectedWithdrawRelativeBlockLocktime(),
|
|
183
|
+
tokenAmount: numberToBytesBE(tokenAmount, 16)
|
|
184
|
+
}]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async constructCreateTokenTransaction(tokenPublicKey, tokenName, tokenTicker, decimals, maxSupply, isFreezable, extraMetadata) {
|
|
188
|
+
return {
|
|
189
|
+
version: 2,
|
|
190
|
+
network: this.config.getNetworkProto(),
|
|
191
|
+
tokenInputs: {
|
|
192
|
+
$case: "createInput",
|
|
193
|
+
createInput: {
|
|
194
|
+
issuerPublicKey: tokenPublicKey,
|
|
195
|
+
tokenName,
|
|
196
|
+
tokenTicker,
|
|
197
|
+
decimals,
|
|
198
|
+
maxSupply: numberToBytesBE(maxSupply, 16),
|
|
199
|
+
isFreezable,
|
|
200
|
+
extraMetadata
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
tokenOutputs: [],
|
|
204
|
+
clientCreatedTimestamp: /* @__PURE__ */ new Date(),
|
|
205
|
+
sparkOperatorIdentityPublicKeys: super.collectOperatorIdentityPublicKeys(),
|
|
206
|
+
expiryTime: void 0,
|
|
207
|
+
invoiceAttachments: []
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
async constructPartialCreateTokenTransaction(tokenPublicKey, tokenName, tokenTicker, decimals, maxSupply, isFreezable, extraMetadata) {
|
|
211
|
+
return {
|
|
212
|
+
version: 3,
|
|
213
|
+
tokenTransactionMetadata: {
|
|
214
|
+
network: this.config.getNetworkProto(),
|
|
215
|
+
sparkOperatorIdentityPublicKeys: this.collectOperatorIdentityPublicKeys(),
|
|
216
|
+
validityDurationSeconds: await this.config.getTokenValidityDurationSeconds(),
|
|
217
|
+
clientCreatedTimestamp: this.connectionManager.getCurrentServerTime(),
|
|
218
|
+
invoiceAttachments: []
|
|
219
|
+
},
|
|
220
|
+
tokenInputs: {
|
|
221
|
+
$case: "createInput",
|
|
222
|
+
createInput: {
|
|
223
|
+
issuerPublicKey: tokenPublicKey,
|
|
224
|
+
tokenName,
|
|
225
|
+
tokenTicker,
|
|
226
|
+
decimals,
|
|
227
|
+
maxSupply: numberToBytesBE(maxSupply, 16),
|
|
228
|
+
isFreezable,
|
|
229
|
+
extraMetadata
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
partialTokenOutputs: []
|
|
233
|
+
};
|
|
234
|
+
}
|
|
266
235
|
};
|
|
267
236
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/utils/create-validation.ts
|
|
239
|
+
/**
|
|
240
|
+
* Returns true when the input is already in NFC normalisation form.
|
|
241
|
+
* JavaScript strings are UTF-16 encoded, so any JavaScript string is
|
|
242
|
+
* already valid Unicode. However, we still need to ensure canonical
|
|
243
|
+
* equivalence so that, for example, \u00E9 (é) and \u0065\u0301 (é)
|
|
244
|
+
* are treated identically. We do this by comparing the original
|
|
245
|
+
* string to its NFC-normalised representation.
|
|
246
|
+
*/
|
|
273
247
|
function isNfcNormalized(value) {
|
|
274
|
-
|
|
248
|
+
return value.normalize("NFC") === value;
|
|
275
249
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
250
|
+
const MIN_NAME_SIZE = 3;
|
|
251
|
+
const MAX_NAME_SIZE = 20;
|
|
252
|
+
const MIN_SYMBOL_SIZE = 3;
|
|
253
|
+
const MAX_SYMBOL_SIZE = 6;
|
|
254
|
+
const MAX_DECIMALS = 255;
|
|
255
|
+
const MAXIMUM_MAX_SUPPLY = (1n << 128n) - 1n;
|
|
256
|
+
const MAX_TOKEN_CONTENT_SIZE = 1024;
|
|
283
257
|
function validateTokenParameters(tokenName, tokenTicker, decimals, maxSupply, extraMetadata) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > MAX_DECIMALS) {
|
|
326
|
-
throw new SparkValidationError2(
|
|
327
|
-
`Decimals must be an integer between 0 and ${MAX_DECIMALS}`,
|
|
328
|
-
{
|
|
329
|
-
field: "decimals",
|
|
330
|
-
value: decimals,
|
|
331
|
-
expected: `>=0 and <=${MAX_DECIMALS}`
|
|
332
|
-
}
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
if (maxSupply < 0n || maxSupply > MAXIMUM_MAX_SUPPLY) {
|
|
336
|
-
throw new SparkValidationError2(`maxSupply must be between 0 and 2^128-1`, {
|
|
337
|
-
field: "maxSupply",
|
|
338
|
-
value: maxSupply.toString(),
|
|
339
|
-
expected: `>=0 and <=${MAXIMUM_MAX_SUPPLY.toString()}`
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
if (extraMetadata && extraMetadata.length > MAX_TOKEN_CONTENT_SIZE) {
|
|
343
|
-
throw new SparkValidationError2(
|
|
344
|
-
`Extra metadata must be less than ${MAX_TOKEN_CONTENT_SIZE} bytes`,
|
|
345
|
-
{
|
|
346
|
-
field: "extraMetadata",
|
|
347
|
-
value: extraMetadata.length,
|
|
348
|
-
expected: `<${MAX_TOKEN_CONTENT_SIZE}`
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
}
|
|
258
|
+
if (!isNfcNormalized(tokenName)) throw new SparkValidationError("Token name must be NFC-normalised UTF-8", {
|
|
259
|
+
field: "tokenName",
|
|
260
|
+
value: tokenName,
|
|
261
|
+
expected: "NFC normalised string"
|
|
262
|
+
});
|
|
263
|
+
if (!isNfcNormalized(tokenTicker)) throw new SparkValidationError("Token ticker must be NFC-normalised UTF-8", {
|
|
264
|
+
field: "tokenTicker",
|
|
265
|
+
value: tokenTicker,
|
|
266
|
+
expected: "NFC normalised string"
|
|
267
|
+
});
|
|
268
|
+
const nameBytes = Buffer.from(tokenName, "utf-8").length;
|
|
269
|
+
if (nameBytes < MIN_NAME_SIZE || nameBytes > MAX_NAME_SIZE) throw new SparkValidationError(`Token name must be between ${MIN_NAME_SIZE} and ${MAX_NAME_SIZE} bytes`, {
|
|
270
|
+
field: "tokenName",
|
|
271
|
+
value: tokenName,
|
|
272
|
+
actualLength: nameBytes,
|
|
273
|
+
expected: `>=${MIN_NAME_SIZE} and <=${MAX_NAME_SIZE}`
|
|
274
|
+
});
|
|
275
|
+
const tickerBytes = Buffer.from(tokenTicker, "utf-8").length;
|
|
276
|
+
if (tickerBytes < MIN_SYMBOL_SIZE || tickerBytes > MAX_SYMBOL_SIZE) throw new SparkValidationError(`Token ticker must be between ${MIN_SYMBOL_SIZE} and ${MAX_SYMBOL_SIZE} bytes`, {
|
|
277
|
+
field: "tokenTicker",
|
|
278
|
+
value: tokenTicker,
|
|
279
|
+
actualLength: tickerBytes,
|
|
280
|
+
expected: `>=${MIN_SYMBOL_SIZE} and <=${MAX_SYMBOL_SIZE}`
|
|
281
|
+
});
|
|
282
|
+
if (!Number.isSafeInteger(decimals) || decimals < 0 || decimals > MAX_DECIMALS) throw new SparkValidationError(`Decimals must be an integer between 0 and ${MAX_DECIMALS}`, {
|
|
283
|
+
field: "decimals",
|
|
284
|
+
value: decimals,
|
|
285
|
+
expected: `>=0 and <=${MAX_DECIMALS}`
|
|
286
|
+
});
|
|
287
|
+
if (maxSupply < 0n || maxSupply > MAXIMUM_MAX_SUPPLY) throw new SparkValidationError(`maxSupply must be between 0 and 2^128-1`, {
|
|
288
|
+
field: "maxSupply",
|
|
289
|
+
value: maxSupply.toString(),
|
|
290
|
+
expected: `>=0 and <=${MAXIMUM_MAX_SUPPLY.toString()}`
|
|
291
|
+
});
|
|
292
|
+
if (extraMetadata && extraMetadata.length > MAX_TOKEN_CONTENT_SIZE) throw new SparkValidationError(`Extra metadata must be less than ${MAX_TOKEN_CONTENT_SIZE} bytes`, {
|
|
293
|
+
field: "extraMetadata",
|
|
294
|
+
value: extraMetadata.length,
|
|
295
|
+
expected: `<${MAX_TOKEN_CONTENT_SIZE}`
|
|
296
|
+
});
|
|
352
297
|
}
|
|
353
298
|
|
|
354
|
-
|
|
355
|
-
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/issuer-wallet/issuer-spark-wallet.ts
|
|
301
|
+
const BURN_ADDRESS = "02".repeat(33);
|
|
302
|
+
/**
|
|
303
|
+
* Represents a Spark wallet with minting capabilities.
|
|
304
|
+
* This class extends the base SparkWallet with additional functionality for token minting,
|
|
305
|
+
* burning, and freezing operations.
|
|
306
|
+
*/
|
|
356
307
|
var IssuerSparkWallet = class extends SparkWallet {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
{
|
|
719
|
-
field: "tokenIdentifier",
|
|
720
|
-
availableTokens: tokenIdentifiers
|
|
721
|
-
}
|
|
722
|
-
);
|
|
723
|
-
}
|
|
724
|
-
burnTokenIdentifier = tokenIdentifiers[0];
|
|
725
|
-
} else {
|
|
726
|
-
tokenAmount = tokenAmountOrParams.tokenAmount;
|
|
727
|
-
outputs = tokenAmountOrParams.selectedOutputs;
|
|
728
|
-
await this.validateTokenIssuer(tokenAmountOrParams.tokenIdentifier);
|
|
729
|
-
burnTokenIdentifier = tokenAmountOrParams.tokenIdentifier;
|
|
730
|
-
}
|
|
731
|
-
const burnAddress = encodeSparkAddress({
|
|
732
|
-
identityPublicKey: BURN_ADDRESS,
|
|
733
|
-
network: this.config.getNetworkType()
|
|
734
|
-
});
|
|
735
|
-
return await this.transferTokens({
|
|
736
|
-
tokenIdentifier: burnTokenIdentifier,
|
|
737
|
-
tokenAmount,
|
|
738
|
-
receiverSparkAddress: burnAddress,
|
|
739
|
-
selectedOutputs: outputs
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
async freezeTokens(sparkAddressOrParams) {
|
|
743
|
-
let bech32mTokenIdentifier;
|
|
744
|
-
let sparkAddress;
|
|
745
|
-
if (typeof sparkAddressOrParams === "string") {
|
|
746
|
-
sparkAddress = sparkAddressOrParams;
|
|
747
|
-
bech32mTokenIdentifier = void 0;
|
|
748
|
-
} else {
|
|
749
|
-
sparkAddress = sparkAddressOrParams.sparkAddress;
|
|
750
|
-
bech32mTokenIdentifier = sparkAddressOrParams.tokenIdentifier;
|
|
751
|
-
}
|
|
752
|
-
const decodedOwnerPubkey = decodeSparkAddress(
|
|
753
|
-
sparkAddress,
|
|
754
|
-
this.config.getNetworkType()
|
|
755
|
-
);
|
|
756
|
-
if (bech32mTokenIdentifier === void 0) {
|
|
757
|
-
const tokenIdentifiers = await this.getIssuerTokenIdentifiers();
|
|
758
|
-
if (tokenIdentifiers.length === 0) {
|
|
759
|
-
throw new SparkValidationError3(
|
|
760
|
-
"No tokens found. Create a token first."
|
|
761
|
-
);
|
|
762
|
-
}
|
|
763
|
-
if (tokenIdentifiers.length > 1) {
|
|
764
|
-
throw new SparkValidationError3(
|
|
765
|
-
"Multiple tokens found. Use freezeTokens({ tokenIdentifier, sparkAddress }) instead.",
|
|
766
|
-
{
|
|
767
|
-
field: "tokenIdentifier",
|
|
768
|
-
availableTokens: tokenIdentifiers
|
|
769
|
-
}
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
bech32mTokenIdentifier = tokenIdentifiers[0];
|
|
773
|
-
} else {
|
|
774
|
-
await this.validateTokenIssuer(bech32mTokenIdentifier);
|
|
775
|
-
}
|
|
776
|
-
const rawTokenIdentifier = decodeBech32mTokenIdentifier(
|
|
777
|
-
bech32mTokenIdentifier,
|
|
778
|
-
this.config.getNetworkType()
|
|
779
|
-
).tokenIdentifier;
|
|
780
|
-
const response = await this.tokenFreezeService.freezeTokens({
|
|
781
|
-
ownerPublicKey: hexToBytes2(decodedOwnerPubkey.identityPublicKey),
|
|
782
|
-
tokenIdentifier: rawTokenIdentifier
|
|
783
|
-
});
|
|
784
|
-
const tokenAmount = bytesToNumberBE(response.impactedTokenAmount);
|
|
785
|
-
return {
|
|
786
|
-
impactedOutputIds: response.impactedOutputIds,
|
|
787
|
-
impactedTokenAmount: tokenAmount
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
async unfreezeTokens(sparkAddressOrParams) {
|
|
791
|
-
let bech32mTokenIdentifier;
|
|
792
|
-
let sparkAddress;
|
|
793
|
-
if (typeof sparkAddressOrParams === "string") {
|
|
794
|
-
sparkAddress = sparkAddressOrParams;
|
|
795
|
-
bech32mTokenIdentifier = void 0;
|
|
796
|
-
} else {
|
|
797
|
-
sparkAddress = sparkAddressOrParams.sparkAddress;
|
|
798
|
-
bech32mTokenIdentifier = sparkAddressOrParams.tokenIdentifier;
|
|
799
|
-
}
|
|
800
|
-
if (bech32mTokenIdentifier === void 0) {
|
|
801
|
-
const tokenIdentifiers = await this.getIssuerTokenIdentifiers();
|
|
802
|
-
if (tokenIdentifiers.length === 0) {
|
|
803
|
-
throw new SparkValidationError3(
|
|
804
|
-
"No tokens found. Create a token first."
|
|
805
|
-
);
|
|
806
|
-
}
|
|
807
|
-
if (tokenIdentifiers.length > 1) {
|
|
808
|
-
throw new SparkValidationError3(
|
|
809
|
-
"Multiple tokens found. Use unfreezeTokens({ tokenIdentifier, sparkAddress }) instead.",
|
|
810
|
-
{
|
|
811
|
-
field: "tokenIdentifier",
|
|
812
|
-
availableTokens: tokenIdentifiers
|
|
813
|
-
}
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
bech32mTokenIdentifier = tokenIdentifiers[0];
|
|
817
|
-
} else {
|
|
818
|
-
await this.validateTokenIssuer(bech32mTokenIdentifier);
|
|
819
|
-
}
|
|
820
|
-
const decodedOwnerPubkey = decodeSparkAddress(
|
|
821
|
-
sparkAddress,
|
|
822
|
-
this.config.getNetworkType()
|
|
823
|
-
);
|
|
824
|
-
const rawTokenIdentifier = decodeBech32mTokenIdentifier(
|
|
825
|
-
bech32mTokenIdentifier,
|
|
826
|
-
this.config.getNetworkType()
|
|
827
|
-
).tokenIdentifier;
|
|
828
|
-
const response = await this.tokenFreezeService.unfreezeTokens({
|
|
829
|
-
ownerPublicKey: hexToBytes2(decodedOwnerPubkey.identityPublicKey),
|
|
830
|
-
tokenIdentifier: rawTokenIdentifier
|
|
831
|
-
});
|
|
832
|
-
const tokenAmount = bytesToNumberBE(response.impactedTokenAmount);
|
|
833
|
-
return {
|
|
834
|
-
impactedOutputIds: response.impactedOutputIds,
|
|
835
|
-
impactedTokenAmount: tokenAmount
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
/**
|
|
839
|
-
* Retrieves the distribution information for the issuer's token.
|
|
840
|
-
* @throws {SparkError} This feature is not yet supported
|
|
841
|
-
*/
|
|
842
|
-
async getIssuerTokenDistribution() {
|
|
843
|
-
throw new SparkError("Token distribution is not yet supported");
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* This validates that the token belongs to this issuer.
|
|
847
|
-
* If a token is in the cache, it must belong to this issuer.
|
|
848
|
-
* @param tokenIdentifier - The bech32m encoded token identifier
|
|
849
|
-
* @throws {SparkValidationError} If the token is not found for this issuer
|
|
850
|
-
* @private
|
|
851
|
-
*/
|
|
852
|
-
async validateTokenIssuer(tokenIdentifier) {
|
|
853
|
-
const issuerPublicKey = await super.getIdentityPublicKey();
|
|
854
|
-
const cachedMetadata = this.tokenMetadata.get(tokenIdentifier);
|
|
855
|
-
if (cachedMetadata) {
|
|
856
|
-
if (bytesToHex(cachedMetadata.issuerPublicKey) !== issuerPublicKey) {
|
|
857
|
-
throw new SparkValidationError3("Token was not issued by this issuer", {
|
|
858
|
-
field: "issuerPublicKey",
|
|
859
|
-
tokenIdentifier,
|
|
860
|
-
expected: issuerPublicKey,
|
|
861
|
-
actual: bytesToHex(cachedMetadata.issuerPublicKey)
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
} else {
|
|
865
|
-
const tokensMetadata = await this.getIssuerTokensMetadata([
|
|
866
|
-
tokenIdentifier
|
|
867
|
-
]);
|
|
868
|
-
if (tokensMetadata.length === 0) {
|
|
869
|
-
throw new SparkValidationError3("Token not found for this issuer", {
|
|
870
|
-
field: "tokenIdentifier",
|
|
871
|
-
value: tokenIdentifier
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
if (tokensMetadata[0].tokenPublicKey !== issuerPublicKey) {
|
|
875
|
-
throw new SparkValidationError3("Token was not issued by this issuer", {
|
|
876
|
-
field: "issuerPublicKey",
|
|
877
|
-
tokenIdentifier,
|
|
878
|
-
expected: issuerPublicKey,
|
|
879
|
-
actual: tokensMetadata[0].tokenPublicKey
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
getTraceName(methodName) {
|
|
885
|
-
return `IssuerSparkWallet.${methodName}`;
|
|
886
|
-
}
|
|
887
|
-
wrapIssuerPublicMethod(methodName) {
|
|
888
|
-
const original = this[methodName];
|
|
889
|
-
if (typeof original !== "function") {
|
|
890
|
-
throw new Error(
|
|
891
|
-
`Method ${methodName} is not a function on IssuerSparkWallet.`
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
const originalFn = original;
|
|
895
|
-
const wrapped = SparkWallet.wrapMethod(
|
|
896
|
-
String(methodName),
|
|
897
|
-
originalFn,
|
|
898
|
-
this
|
|
899
|
-
);
|
|
900
|
-
this[methodName] = wrapped;
|
|
901
|
-
}
|
|
902
|
-
wrapIssuerSparkWalletMethods() {
|
|
903
|
-
PUBLIC_ISSUER_SPARK_WALLET_METHODS.forEach(
|
|
904
|
-
(m) => this.wrapIssuerPublicMethod(m)
|
|
905
|
-
);
|
|
906
|
-
}
|
|
308
|
+
/**
|
|
309
|
+
* Initializes a new IssuerSparkWallet instance.
|
|
310
|
+
* Inherits the generic static initialize from the base class.
|
|
311
|
+
*/
|
|
312
|
+
constructor(configOptions, signer) {
|
|
313
|
+
super(configOptions, signer);
|
|
314
|
+
_defineProperty(this, "issuerTokenTransactionService", void 0);
|
|
315
|
+
_defineProperty(this, "tokenFreezeService", void 0);
|
|
316
|
+
_defineProperty(this, "tracerId", "issuer-sdk");
|
|
317
|
+
this.issuerTokenTransactionService = new IssuerTokenTransactionService(this.config, this.connectionManager);
|
|
318
|
+
this.tokenFreezeService = new TokenFreezeService(this.config, this.connectionManager);
|
|
319
|
+
this.wrapIssuerSparkWalletMethods();
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Gets the token balance for the issuer's token.
|
|
323
|
+
* @deprecated Use getIssuerTokenBalances() instead. This method will be removed in a future version.
|
|
324
|
+
* @returns An object containing the token balance as a bigint
|
|
325
|
+
*
|
|
326
|
+
* @throws {SparkValidationError} If multiple tokens are found for this issuer
|
|
327
|
+
*/
|
|
328
|
+
async getIssuerTokenBalance() {
|
|
329
|
+
const publicKey = await super.getIdentityPublicKey();
|
|
330
|
+
const issuerBalance = [...(await this.getBalance()).tokenBalances.entries()].filter(([, info]) => info.tokenMetadata.tokenPublicKey === publicKey);
|
|
331
|
+
if (issuerBalance.length > 1) throw new SparkValidationError("Multiple tokens found for this issuer. Use getIssuerTokenBalances() instead.", {
|
|
332
|
+
field: "issuerTokenBalance",
|
|
333
|
+
expected: "single token",
|
|
334
|
+
actual: `${issuerBalance.length} tokens`
|
|
335
|
+
});
|
|
336
|
+
if (issuerBalance.length === 0) return {
|
|
337
|
+
tokenIdentifier: void 0,
|
|
338
|
+
balance: 0n
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
tokenIdentifier: issuerBalance[0][0],
|
|
342
|
+
balance: issuerBalance[0][1].balance
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Gets the token balances for the tokens that were issued by this user.
|
|
347
|
+
* @returns An array of objects containing the token identifier and balance
|
|
348
|
+
*/
|
|
349
|
+
async getIssuerTokenBalances() {
|
|
350
|
+
const publicKey = await super.getIdentityPublicKey();
|
|
351
|
+
const issuerBalance = [...(await this.getBalance()).tokenBalances.entries()].filter(([, info]) => info.tokenMetadata.tokenPublicKey === publicKey);
|
|
352
|
+
if (issuerBalance.length === 0) return [{
|
|
353
|
+
tokenIdentifier: void 0,
|
|
354
|
+
balance: 0n
|
|
355
|
+
}];
|
|
356
|
+
return issuerBalance.map(([tokenIdentifier, { balance }]) => ({
|
|
357
|
+
tokenIdentifier,
|
|
358
|
+
balance
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Retrieves information about the issuer's token.
|
|
363
|
+
* @deprecated Use getIssuerTokensMetadata() instead. This method will be removed in a future version.
|
|
364
|
+
* @returns An object containing token information including public key, name, symbol, decimals, max supply, freeze status, and extra metadata
|
|
365
|
+
* @throws {SparkRequestError} If the token metadata cannot be retrieved
|
|
366
|
+
* @throws {SparkValidationError} If multiple tokens are found for this issuer
|
|
367
|
+
*/
|
|
368
|
+
async getIssuerTokenMetadata() {
|
|
369
|
+
const tokensMetadata = await this.getIssuerTokensMetadata();
|
|
370
|
+
if (tokensMetadata.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
371
|
+
if (tokensMetadata.length > 1) throw new SparkValidationError("Multiple tokens found for this issuer. Please migrate to getIssuerTokensMetadata() instead.", {
|
|
372
|
+
field: "tokenMetadata",
|
|
373
|
+
value: tokensMetadata
|
|
374
|
+
});
|
|
375
|
+
return tokensMetadata[0];
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Retrieves information about the tokens that were issued by this user.
|
|
379
|
+
* @param tokenIdentifiers - Optional array of specific token identifiers to fetch.
|
|
380
|
+
* If omitted, all tokens for this issuer are fetched.
|
|
381
|
+
* @returns An array of objects containing token information including public key, name, symbol, decimals, max supply, freeze status, and extra metadata
|
|
382
|
+
* @throws {SparkRequestError} If the token metadata cannot be retrieved
|
|
383
|
+
*/
|
|
384
|
+
async getIssuerTokensMetadata(tokenIdentifiers) {
|
|
385
|
+
const issuerPublicKey = await super.getIdentityPublicKey();
|
|
386
|
+
const sparkTokenClient = await this.connectionManager.createSparkTokenClient(this.config.getCoordinatorAddress());
|
|
387
|
+
const filterByIdentifiers = Array.isArray(tokenIdentifiers) && tokenIdentifiers.length > 0;
|
|
388
|
+
const tokenIdentifierSet = filterByIdentifiers ? new Set(tokenIdentifiers) : void 0;
|
|
389
|
+
const request = {};
|
|
390
|
+
if (filterByIdentifiers) request.tokenIdentifiers = tokenIdentifiers.map((id) => decodeBech32mTokenIdentifier(id, this.config.getNetworkType()).tokenIdentifier);
|
|
391
|
+
else request.issuerPublicKeys = Array.of(hexToBytes(issuerPublicKey));
|
|
392
|
+
try {
|
|
393
|
+
const response = await sparkTokenClient.query_token_metadata(request);
|
|
394
|
+
const tokenMetadata = [];
|
|
395
|
+
for (const metadata of response.tokenMetadata) {
|
|
396
|
+
const bech32mTokenIdentifier = encodeBech32mTokenIdentifier({
|
|
397
|
+
tokenIdentifier: metadata.tokenIdentifier,
|
|
398
|
+
network: this.config.getNetworkType()
|
|
399
|
+
});
|
|
400
|
+
if (bytesToHex(metadata.issuerPublicKey) !== issuerPublicKey) continue;
|
|
401
|
+
if (filterByIdentifiers && !tokenIdentifierSet.has(bech32mTokenIdentifier)) continue;
|
|
402
|
+
this.tokenMetadata.set(bech32mTokenIdentifier, metadata);
|
|
403
|
+
tokenMetadata.push({
|
|
404
|
+
tokenPublicKey: bytesToHex(metadata.issuerPublicKey),
|
|
405
|
+
rawTokenIdentifier: metadata.tokenIdentifier,
|
|
406
|
+
tokenName: metadata.tokenName,
|
|
407
|
+
tokenTicker: metadata.tokenTicker,
|
|
408
|
+
decimals: metadata.decimals,
|
|
409
|
+
maxSupply: bytesToNumberBE(metadata.maxSupply),
|
|
410
|
+
isFreezable: metadata.isFreezable,
|
|
411
|
+
extraMetadata: metadata.extraMetadata ? new Uint8Array(metadata.extraMetadata) : void 0,
|
|
412
|
+
bech32mTokenIdentifier
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return tokenMetadata;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
if (error instanceof SparkError) throw error;
|
|
418
|
+
throw new SparkRequestError("Failed to fetch token metadata", { error });
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Retrieves the bech32m encoded token identifier for the issuer's token.
|
|
423
|
+
* @deprecated Use getIssuerTokenIdentifiers() instead. This method will be removed in a future version.
|
|
424
|
+
* @returns The bech32m encoded token identifier for the issuer's token
|
|
425
|
+
* @throws {SparkRequestError} If the token identifier cannot be retrieved
|
|
426
|
+
* @throws {SparkValidationError} If multiple tokens are found for this issuer
|
|
427
|
+
*/
|
|
428
|
+
async getIssuerTokenIdentifier() {
|
|
429
|
+
const tokensMetadata = await this.getIssuerTokensMetadata();
|
|
430
|
+
if (tokensMetadata.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
431
|
+
if (tokensMetadata.length > 1) throw new SparkValidationError("Multiple tokens found. Use getIssuerTokenIdentifiers() instead.", {
|
|
432
|
+
method: "getIssuerTokenIdentifier",
|
|
433
|
+
availableTokens: tokensMetadata.map((t) => ({
|
|
434
|
+
tokenName: t.tokenName,
|
|
435
|
+
tokenTicker: t.tokenTicker,
|
|
436
|
+
bech32mTokenIdentifier: encodeBech32mTokenIdentifier({
|
|
437
|
+
tokenIdentifier: t.rawTokenIdentifier,
|
|
438
|
+
network: this.config.getNetworkType()
|
|
439
|
+
})
|
|
440
|
+
}))
|
|
441
|
+
});
|
|
442
|
+
return tokensMetadata[0].bech32mTokenIdentifier;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Retrieves the bech32m encoded token identifier for the issuer's token.
|
|
446
|
+
* @returns The bech32m encoded token identifier for the issuer's token
|
|
447
|
+
* @throws {SparkRequestError} If the token identifier cannot be retrieved
|
|
448
|
+
*/
|
|
449
|
+
async getIssuerTokenIdentifiers() {
|
|
450
|
+
return (await this.getIssuerTokensMetadata()).map((metadata) => metadata.bech32mTokenIdentifier);
|
|
451
|
+
}
|
|
452
|
+
async createToken({ tokenName, tokenTicker, decimals, isFreezable, maxSupply = 0n, extraMetadata, returnIdentifierForCreate = false }) {
|
|
453
|
+
validateTokenParameters(tokenName, tokenTicker, decimals, maxSupply, extraMetadata);
|
|
454
|
+
const issuerPublicKey = await super.getIdentityPublicKey();
|
|
455
|
+
if (this.config.getTokenTransactionVersion() === "V2") {
|
|
456
|
+
const tokenTransaction = await this.issuerTokenTransactionService.constructCreateTokenTransaction(hexToBytes(issuerPublicKey), tokenName, tokenTicker, decimals, maxSupply, isFreezable, extraMetadata);
|
|
457
|
+
const { finalTokenTransactionHash, tokenIdentifier } = await this.issuerTokenTransactionService.broadcastTokenTransactionDetailed(tokenTransaction);
|
|
458
|
+
const txHash = bytesToHex(finalTokenTransactionHash);
|
|
459
|
+
if (returnIdentifierForCreate) {
|
|
460
|
+
if (!tokenIdentifier) throw new SparkRequestError("Server response missing expected field: tokenIdentifier", {
|
|
461
|
+
operation: "broadcast_transaction",
|
|
462
|
+
field: "tokenIdentifier"
|
|
463
|
+
});
|
|
464
|
+
return {
|
|
465
|
+
tokenIdentifier: encodeBech32mTokenIdentifier({
|
|
466
|
+
tokenIdentifier,
|
|
467
|
+
network: this.config.getNetworkType()
|
|
468
|
+
}),
|
|
469
|
+
transactionHash: txHash
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return txHash;
|
|
473
|
+
} else {
|
|
474
|
+
const partialTokenTransaction = await this.issuerTokenTransactionService.constructPartialCreateTokenTransaction(hexToBytes(issuerPublicKey), tokenName, tokenTicker, decimals, maxSupply, isFreezable, extraMetadata);
|
|
475
|
+
const broadcastResponse = await this.issuerTokenTransactionService.broadcastTokenTransactionV3Detailed(partialTokenTransaction);
|
|
476
|
+
const finalTransactionHash = bytesToHex(await hashFinalTokenTransaction(broadcastResponse.finalTokenTransaction));
|
|
477
|
+
if (returnIdentifierForCreate) {
|
|
478
|
+
if (!broadcastResponse.tokenIdentifier) throw new SparkRequestError("Server response missing expected field: tokenIdentifier", {
|
|
479
|
+
operation: "broadcast_transaction",
|
|
480
|
+
field: "tokenIdentifier"
|
|
481
|
+
});
|
|
482
|
+
return {
|
|
483
|
+
tokenIdentifier: encodeBech32mTokenIdentifier({
|
|
484
|
+
tokenIdentifier: broadcastResponse.tokenIdentifier,
|
|
485
|
+
network: this.config.getNetworkType()
|
|
486
|
+
}),
|
|
487
|
+
transactionHash: finalTransactionHash
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return finalTransactionHash;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async mintTokens(tokenAmountOrParams) {
|
|
494
|
+
let tokenAmount;
|
|
495
|
+
let bech32mTokenIdentifier;
|
|
496
|
+
if (typeof tokenAmountOrParams === "bigint") {
|
|
497
|
+
tokenAmount = tokenAmountOrParams;
|
|
498
|
+
bech32mTokenIdentifier = void 0;
|
|
499
|
+
} else {
|
|
500
|
+
tokenAmount = tokenAmountOrParams.tokenAmount;
|
|
501
|
+
bech32mTokenIdentifier = tokenAmountOrParams.tokenIdentifier;
|
|
502
|
+
}
|
|
503
|
+
const issuerTokenPublicKeyBytes = hexToBytes(await super.getIdentityPublicKey());
|
|
504
|
+
if (bech32mTokenIdentifier === void 0) {
|
|
505
|
+
const tokensMetadata = await this.getIssuerTokensMetadata();
|
|
506
|
+
if (tokensMetadata.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
507
|
+
if (tokensMetadata.length > 1) throw new SparkValidationError("Multiple tokens found. Please use mintTokens({ tokenAmount, tokenIdentifier }) instead.", {
|
|
508
|
+
field: "tokenIdentifier",
|
|
509
|
+
availableTokens: tokensMetadata.map((t) => ({
|
|
510
|
+
tokenName: t.tokenName,
|
|
511
|
+
tokenTicker: t.tokenTicker,
|
|
512
|
+
bech32mTokenIdentifier: t.bech32mTokenIdentifier
|
|
513
|
+
}))
|
|
514
|
+
});
|
|
515
|
+
bech32mTokenIdentifier = tokensMetadata[0].bech32mTokenIdentifier;
|
|
516
|
+
} else await this.validateTokenIssuer(bech32mTokenIdentifier);
|
|
517
|
+
const rawTokenIdentifier = decodeBech32mTokenIdentifier(bech32mTokenIdentifier, this.config.getNetworkType()).tokenIdentifier;
|
|
518
|
+
if (this.config.getTokenTransactionVersion() === "V2") {
|
|
519
|
+
const tokenTransaction = await this.issuerTokenTransactionService.constructMintTokenTransaction(rawTokenIdentifier, issuerTokenPublicKeyBytes, tokenAmount);
|
|
520
|
+
return await this.issuerTokenTransactionService.broadcastTokenTransaction(tokenTransaction);
|
|
521
|
+
} else {
|
|
522
|
+
const partialTokenTransaction = await this.issuerTokenTransactionService.constructPartialMintTokenTransaction(rawTokenIdentifier, issuerTokenPublicKeyBytes, tokenAmount);
|
|
523
|
+
return await this.issuerTokenTransactionService.broadcastTokenTransactionV3(partialTokenTransaction);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async burnTokens(tokenAmountOrParams, selectedOutputs) {
|
|
527
|
+
let burnTokenIdentifier;
|
|
528
|
+
let tokenAmount;
|
|
529
|
+
let outputs;
|
|
530
|
+
if (typeof tokenAmountOrParams === "bigint") {
|
|
531
|
+
tokenAmount = tokenAmountOrParams;
|
|
532
|
+
outputs = selectedOutputs;
|
|
533
|
+
const tokenIdentifiers = await this.getIssuerTokenIdentifiers();
|
|
534
|
+
if (tokenIdentifiers.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
535
|
+
if (tokenIdentifiers.length > 1) throw new SparkValidationError("Multiple tokens found. Use burnTokens({ tokenIdentifier, tokenAmount, selectedOutputs }) to specify which token to burn.", {
|
|
536
|
+
field: "tokenIdentifier",
|
|
537
|
+
availableTokens: tokenIdentifiers
|
|
538
|
+
});
|
|
539
|
+
burnTokenIdentifier = tokenIdentifiers[0];
|
|
540
|
+
} else {
|
|
541
|
+
tokenAmount = tokenAmountOrParams.tokenAmount;
|
|
542
|
+
outputs = tokenAmountOrParams.selectedOutputs;
|
|
543
|
+
await this.validateTokenIssuer(tokenAmountOrParams.tokenIdentifier);
|
|
544
|
+
burnTokenIdentifier = tokenAmountOrParams.tokenIdentifier;
|
|
545
|
+
}
|
|
546
|
+
const burnAddress = encodeSparkAddress({
|
|
547
|
+
identityPublicKey: BURN_ADDRESS,
|
|
548
|
+
network: this.config.getNetworkType()
|
|
549
|
+
});
|
|
550
|
+
return await this.transferTokens({
|
|
551
|
+
tokenIdentifier: burnTokenIdentifier,
|
|
552
|
+
tokenAmount,
|
|
553
|
+
receiverSparkAddress: burnAddress,
|
|
554
|
+
selectedOutputs: outputs
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async freezeTokens(sparkAddressOrParams) {
|
|
558
|
+
let bech32mTokenIdentifier;
|
|
559
|
+
let sparkAddress;
|
|
560
|
+
if (typeof sparkAddressOrParams === "string") {
|
|
561
|
+
sparkAddress = sparkAddressOrParams;
|
|
562
|
+
bech32mTokenIdentifier = void 0;
|
|
563
|
+
} else {
|
|
564
|
+
sparkAddress = sparkAddressOrParams.sparkAddress;
|
|
565
|
+
bech32mTokenIdentifier = sparkAddressOrParams.tokenIdentifier;
|
|
566
|
+
}
|
|
567
|
+
const decodedOwnerPubkey = decodeSparkAddress(sparkAddress, this.config.getNetworkType());
|
|
568
|
+
if (bech32mTokenIdentifier === void 0) {
|
|
569
|
+
const tokenIdentifiers = await this.getIssuerTokenIdentifiers();
|
|
570
|
+
if (tokenIdentifiers.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
571
|
+
if (tokenIdentifiers.length > 1) throw new SparkValidationError("Multiple tokens found. Use freezeTokens({ tokenIdentifier, sparkAddress }) instead.", {
|
|
572
|
+
field: "tokenIdentifier",
|
|
573
|
+
availableTokens: tokenIdentifiers
|
|
574
|
+
});
|
|
575
|
+
bech32mTokenIdentifier = tokenIdentifiers[0];
|
|
576
|
+
} else await this.validateTokenIssuer(bech32mTokenIdentifier);
|
|
577
|
+
const rawTokenIdentifier = decodeBech32mTokenIdentifier(bech32mTokenIdentifier, this.config.getNetworkType()).tokenIdentifier;
|
|
578
|
+
const response = await this.tokenFreezeService.freezeTokens({
|
|
579
|
+
ownerPublicKey: hexToBytes(decodedOwnerPubkey.identityPublicKey),
|
|
580
|
+
tokenIdentifier: rawTokenIdentifier
|
|
581
|
+
});
|
|
582
|
+
const tokenAmount = bytesToNumberBE(response.impactedTokenAmount);
|
|
583
|
+
return {
|
|
584
|
+
impactedOutputIds: response.impactedOutputIds,
|
|
585
|
+
impactedTokenAmount: tokenAmount
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
async unfreezeTokens(sparkAddressOrParams) {
|
|
589
|
+
let bech32mTokenIdentifier;
|
|
590
|
+
let sparkAddress;
|
|
591
|
+
if (typeof sparkAddressOrParams === "string") {
|
|
592
|
+
sparkAddress = sparkAddressOrParams;
|
|
593
|
+
bech32mTokenIdentifier = void 0;
|
|
594
|
+
} else {
|
|
595
|
+
sparkAddress = sparkAddressOrParams.sparkAddress;
|
|
596
|
+
bech32mTokenIdentifier = sparkAddressOrParams.tokenIdentifier;
|
|
597
|
+
}
|
|
598
|
+
if (bech32mTokenIdentifier === void 0) {
|
|
599
|
+
const tokenIdentifiers = await this.getIssuerTokenIdentifiers();
|
|
600
|
+
if (tokenIdentifiers.length === 0) throw new SparkValidationError("No tokens found. Create a token first.");
|
|
601
|
+
if (tokenIdentifiers.length > 1) throw new SparkValidationError("Multiple tokens found. Use unfreezeTokens({ tokenIdentifier, sparkAddress }) instead.", {
|
|
602
|
+
field: "tokenIdentifier",
|
|
603
|
+
availableTokens: tokenIdentifiers
|
|
604
|
+
});
|
|
605
|
+
bech32mTokenIdentifier = tokenIdentifiers[0];
|
|
606
|
+
} else await this.validateTokenIssuer(bech32mTokenIdentifier);
|
|
607
|
+
const decodedOwnerPubkey = decodeSparkAddress(sparkAddress, this.config.getNetworkType());
|
|
608
|
+
const rawTokenIdentifier = decodeBech32mTokenIdentifier(bech32mTokenIdentifier, this.config.getNetworkType()).tokenIdentifier;
|
|
609
|
+
const response = await this.tokenFreezeService.unfreezeTokens({
|
|
610
|
+
ownerPublicKey: hexToBytes(decodedOwnerPubkey.identityPublicKey),
|
|
611
|
+
tokenIdentifier: rawTokenIdentifier
|
|
612
|
+
});
|
|
613
|
+
const tokenAmount = bytesToNumberBE(response.impactedTokenAmount);
|
|
614
|
+
return {
|
|
615
|
+
impactedOutputIds: response.impactedOutputIds,
|
|
616
|
+
impactedTokenAmount: tokenAmount
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Retrieves the distribution information for the issuer's token.
|
|
621
|
+
* @throws {SparkError} This feature is not yet supported
|
|
622
|
+
*/
|
|
623
|
+
async getIssuerTokenDistribution() {
|
|
624
|
+
throw new SparkError("Token distribution is not yet supported");
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* This validates that the token belongs to this issuer.
|
|
628
|
+
* If a token is in the cache, it must belong to this issuer.
|
|
629
|
+
* @param tokenIdentifier - The bech32m encoded token identifier
|
|
630
|
+
* @throws {SparkValidationError} If the token is not found for this issuer
|
|
631
|
+
* @private
|
|
632
|
+
*/
|
|
633
|
+
async validateTokenIssuer(tokenIdentifier) {
|
|
634
|
+
const issuerPublicKey = await super.getIdentityPublicKey();
|
|
635
|
+
const cachedMetadata = this.tokenMetadata.get(tokenIdentifier);
|
|
636
|
+
if (cachedMetadata) {
|
|
637
|
+
if (bytesToHex(cachedMetadata.issuerPublicKey) !== issuerPublicKey) throw new SparkValidationError("Token was not issued by this issuer", {
|
|
638
|
+
field: "issuerPublicKey",
|
|
639
|
+
tokenIdentifier,
|
|
640
|
+
expected: issuerPublicKey,
|
|
641
|
+
actual: bytesToHex(cachedMetadata.issuerPublicKey)
|
|
642
|
+
});
|
|
643
|
+
} else {
|
|
644
|
+
const tokensMetadata = await this.getIssuerTokensMetadata([tokenIdentifier]);
|
|
645
|
+
if (tokensMetadata.length === 0) throw new SparkValidationError("Token not found for this issuer", {
|
|
646
|
+
field: "tokenIdentifier",
|
|
647
|
+
value: tokenIdentifier
|
|
648
|
+
});
|
|
649
|
+
if (tokensMetadata[0].tokenPublicKey !== issuerPublicKey) throw new SparkValidationError("Token was not issued by this issuer", {
|
|
650
|
+
field: "issuerPublicKey",
|
|
651
|
+
tokenIdentifier,
|
|
652
|
+
expected: issuerPublicKey,
|
|
653
|
+
actual: tokensMetadata[0].tokenPublicKey
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
getTraceName(methodName) {
|
|
658
|
+
return `IssuerSparkWallet.${methodName}`;
|
|
659
|
+
}
|
|
660
|
+
wrapIssuerPublicMethod(methodName) {
|
|
661
|
+
const original = this[methodName];
|
|
662
|
+
if (typeof original !== "function") throw new Error(`Method ${methodName} is not a function on IssuerSparkWallet.`);
|
|
663
|
+
const originalFn = original;
|
|
664
|
+
this[methodName] = SparkWallet.wrapMethod(String(methodName), originalFn, this);
|
|
665
|
+
}
|
|
666
|
+
wrapIssuerSparkWalletMethods() {
|
|
667
|
+
PUBLIC_ISSUER_SPARK_WALLET_METHODS.forEach((m) => this.wrapIssuerPublicMethod(m));
|
|
668
|
+
}
|
|
907
669
|
};
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
670
|
+
const PUBLIC_ISSUER_SPARK_WALLET_METHODS = [
|
|
671
|
+
"getIssuerTokenBalance",
|
|
672
|
+
"getIssuerTokenBalances",
|
|
673
|
+
"getIssuerTokenMetadata",
|
|
674
|
+
"getIssuerTokensMetadata",
|
|
675
|
+
"getIssuerTokenIdentifier",
|
|
676
|
+
"getIssuerTokenIdentifiers",
|
|
677
|
+
"createToken",
|
|
678
|
+
"mintTokens",
|
|
679
|
+
"burnTokens",
|
|
680
|
+
"freezeTokens",
|
|
681
|
+
"unfreezeTokens",
|
|
682
|
+
"getIssuerTokenDistribution"
|
|
921
683
|
];
|
|
922
684
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
};
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region src/issuer-wallet/issuer-spark-wallet.react-native.ts
|
|
687
|
+
var IssuerSparkWalletReactNative = class extends IssuerSparkWallet {};
|
|
926
688
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
DefaultSparkSigner,
|
|
930
|
-
UnsafeStatelessSparkSigner
|
|
931
|
-
} from "@buildonspark/spark-sdk";
|
|
932
|
-
import { WalletConfig } from "@buildonspark/spark-sdk";
|
|
933
|
-
export {
|
|
934
|
-
DefaultSparkSigner,
|
|
935
|
-
IssuerSparkWalletReactNative as IssuerSparkWallet,
|
|
936
|
-
UnsafeStatelessSparkSigner,
|
|
937
|
-
WalletConfig
|
|
938
|
-
};
|
|
689
|
+
//#endregion
|
|
690
|
+
export { DefaultSparkSigner, IssuerSparkWalletReactNative as IssuerSparkWallet, UnsafeStatelessSparkSigner, WalletConfig };
|