mainnet-js 1.1.0 → 1.1.2
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/index.html +1 -1
- package/dist/{mainnet-1.1.0.js → mainnet-1.1.2.js} +4 -4
- package/dist/module/libauth.d.ts +1 -1
- package/dist/module/libauth.d.ts.map +1 -1
- package/dist/module/libauth.js +1 -1
- package/dist/module/libauth.js.map +1 -1
- package/dist/module/transaction/Wif.d.ts +42 -7
- package/dist/module/transaction/Wif.d.ts.map +1 -1
- package/dist/module/transaction/Wif.js +127 -106
- package/dist/module/transaction/Wif.js.map +1 -1
- package/dist/module/wallet/Slp.d.ts.map +1 -1
- package/dist/module/wallet/Slp.js +11 -1
- package/dist/module/wallet/Slp.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +14 -6
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +131 -59
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/module/wallet/interface.d.ts +2 -0
- package/dist/module/wallet/interface.d.ts.map +1 -1
- package/dist/module/wallet/model.d.ts +4 -0
- package/dist/module/wallet/model.d.ts.map +1 -1
- package/dist/module/wallet/model.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/libauth.ts +1 -0
- package/src/transaction/Wif.ts +200 -154
- package/src/wallet/Cashtokens.test.headless.js +162 -1
- package/src/wallet/Cashtokens.test.ts +338 -2
- package/src/wallet/Slp.ts +10 -7
- package/src/wallet/Wif.test.ts +101 -1
- package/src/wallet/Wif.ts +185 -98
- package/src/wallet/interface.ts +2 -0
- package/src/wallet/model.ts +6 -0
package/src/wallet/Wif.test.ts
CHANGED
|
@@ -7,7 +7,13 @@ import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD as DUST } from "../constant";
|
|
|
7
7
|
import { delay } from "../util/delay";
|
|
8
8
|
import { OpReturnData, SendResponse } from "./model";
|
|
9
9
|
import { ElectrumRawTransaction } from "../network/interface";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
binToHex,
|
|
12
|
+
binsAreEqual,
|
|
13
|
+
decodeTransaction,
|
|
14
|
+
hexToBin,
|
|
15
|
+
utf8ToBin,
|
|
16
|
+
} from "@bitauth/libauth";
|
|
11
17
|
import { mine } from "../mine";
|
|
12
18
|
import ElectrumNetworkProvider from "../network/ElectrumNetworkProvider";
|
|
13
19
|
|
|
@@ -1114,4 +1120,98 @@ describe(`Wallet extrema behavior regression testing`, () => {
|
|
|
1114
1120
|
(await bob.getMaxAmountToSend({ options: { slpSemiAware: false } })).sat
|
|
1115
1121
|
).toBeLessThanOrEqual(546);
|
|
1116
1122
|
});
|
|
1123
|
+
|
|
1124
|
+
test("Should encode unsigned transactions", async () => {
|
|
1125
|
+
const aliceWif = `wif:regtest:${process.env.PRIVATE_WIF!}`;
|
|
1126
|
+
const aliceWallet = await RegTestWallet.fromId(aliceWif);
|
|
1127
|
+
const bobWallet = await RegTestWallet.newRandom();
|
|
1128
|
+
aliceWallet.privateKey = undefined;
|
|
1129
|
+
|
|
1130
|
+
const aliceUtxos = await aliceWallet.getAddressUtxos();
|
|
1131
|
+
|
|
1132
|
+
{
|
|
1133
|
+
const { encodedTransaction, sourceOutputs } =
|
|
1134
|
+
await aliceWallet.encodeTransaction(
|
|
1135
|
+
[
|
|
1136
|
+
{
|
|
1137
|
+
cashaddr: bobWallet.cashaddr!,
|
|
1138
|
+
value: 2000,
|
|
1139
|
+
unit: "satoshis",
|
|
1140
|
+
},
|
|
1141
|
+
],
|
|
1142
|
+
false,
|
|
1143
|
+
{ buildUnsigned: true }
|
|
1144
|
+
);
|
|
1145
|
+
expect(encodedTransaction.length).toBeGreaterThan(0);
|
|
1146
|
+
|
|
1147
|
+
// check transaction was not submitted
|
|
1148
|
+
expect(JSON.stringify(aliceUtxos)).toBe(
|
|
1149
|
+
JSON.stringify(await aliceWallet.getAddressUtxos())
|
|
1150
|
+
);
|
|
1151
|
+
|
|
1152
|
+
const decoded = decodeTransaction(encodedTransaction);
|
|
1153
|
+
if (typeof decoded === "string") {
|
|
1154
|
+
throw decoded;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
expect(
|
|
1158
|
+
binsAreEqual(decoded.inputs[0].unlockingBytecode, Uint8Array.from([]))
|
|
1159
|
+
).toBe(true);
|
|
1160
|
+
expect(sourceOutputs.length).toBe(decoded.inputs.length);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
{
|
|
1164
|
+
const { unsignedTransaction, sourceOutputs } = await aliceWallet.send(
|
|
1165
|
+
[
|
|
1166
|
+
{
|
|
1167
|
+
cashaddr: bobWallet.cashaddr!,
|
|
1168
|
+
value: 2000,
|
|
1169
|
+
unit: "satoshis",
|
|
1170
|
+
},
|
|
1171
|
+
],
|
|
1172
|
+
{ buildUnsigned: true }
|
|
1173
|
+
);
|
|
1174
|
+
const encodedTransaction = hexToBin(unsignedTransaction!);
|
|
1175
|
+
expect(encodedTransaction.length).toBeGreaterThan(0);
|
|
1176
|
+
|
|
1177
|
+
// check transaction was not submitted
|
|
1178
|
+
expect(JSON.stringify(aliceUtxos)).toBe(
|
|
1179
|
+
JSON.stringify(await aliceWallet.getAddressUtxos())
|
|
1180
|
+
);
|
|
1181
|
+
|
|
1182
|
+
const decoded = decodeTransaction(encodedTransaction);
|
|
1183
|
+
if (typeof decoded === "string") {
|
|
1184
|
+
throw decoded;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
expect(
|
|
1188
|
+
binsAreEqual(decoded.inputs[0].unlockingBytecode, Uint8Array.from([]))
|
|
1189
|
+
).toBe(true);
|
|
1190
|
+
expect(sourceOutputs!.length).toBe(decoded.inputs.length);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
{
|
|
1194
|
+
const { unsignedTransaction, sourceOutputs } = await aliceWallet.sendMax(
|
|
1195
|
+
bobWallet.cashaddr!,
|
|
1196
|
+
{ buildUnsigned: true }
|
|
1197
|
+
);
|
|
1198
|
+
const encodedTransaction = hexToBin(unsignedTransaction!);
|
|
1199
|
+
expect(encodedTransaction.length).toBeGreaterThan(0);
|
|
1200
|
+
|
|
1201
|
+
// check transaction was not submitted
|
|
1202
|
+
expect(JSON.stringify(aliceUtxos)).toBe(
|
|
1203
|
+
JSON.stringify(await aliceWallet.getAddressUtxos())
|
|
1204
|
+
);
|
|
1205
|
+
|
|
1206
|
+
const decoded = decodeTransaction(encodedTransaction);
|
|
1207
|
+
if (typeof decoded === "string") {
|
|
1208
|
+
throw decoded;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
expect(
|
|
1212
|
+
binsAreEqual(decoded.inputs[0].unlockingBytecode, Uint8Array.from([]))
|
|
1213
|
+
).toBe(true);
|
|
1214
|
+
expect(sourceOutputs!.length).toBe(decoded.inputs.length);
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1117
1217
|
});
|
package/src/wallet/Wif.ts
CHANGED
|
@@ -849,7 +849,7 @@ export class Wallet extends BaseWallet {
|
|
|
849
849
|
options: {},
|
|
850
850
|
}
|
|
851
851
|
): Promise<{ value: number; utxos: UtxoI[] }> {
|
|
852
|
-
if (!this.privateKey) {
|
|
852
|
+
if (!this.privateKey && params.options?.buildUnsigned !== true) {
|
|
853
853
|
throw Error("Couldn't get network or private key for wallet.");
|
|
854
854
|
}
|
|
855
855
|
if (!this.cashaddr) {
|
|
@@ -907,7 +907,8 @@ export class Wallet extends BaseWallet {
|
|
|
907
907
|
const fee = await getFeeAmount({
|
|
908
908
|
utxos: fundingUtxos,
|
|
909
909
|
sendRequests: sendRequests,
|
|
910
|
-
privateKey: this.privateKey,
|
|
910
|
+
privateKey: this.privateKey ?? Uint8Array.from([]),
|
|
911
|
+
sourceAddress: this.cashaddr!,
|
|
911
912
|
relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
|
|
912
913
|
slpOutputs: [],
|
|
913
914
|
feePaidBy: feePaidBy,
|
|
@@ -953,31 +954,33 @@ export class Wallet extends BaseWallet {
|
|
|
953
954
|
| SendRequestArray[],
|
|
954
955
|
options?: SendRequestOptionsI
|
|
955
956
|
): Promise<SendResponse> {
|
|
956
|
-
|
|
957
|
-
requests,
|
|
958
|
-
undefined,
|
|
959
|
-
options
|
|
960
|
-
);
|
|
957
|
+
const { encodedTransaction, tokenIds, sourceOutputs } =
|
|
958
|
+
await this.encodeTransaction(requests, undefined, options);
|
|
961
959
|
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
options.awaitTransactionPropagation === undefined ||
|
|
965
|
-
options.awaitTransactionPropagation;
|
|
960
|
+
const resp = new SendResponse({});
|
|
961
|
+
resp.tokenIds = tokenIds;
|
|
966
962
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
963
|
+
if (options?.buildUnsigned !== true) {
|
|
964
|
+
const txId = await this.submitTransaction(
|
|
965
|
+
encodedTransaction,
|
|
966
|
+
options?.awaitTransactionPropagation === undefined ||
|
|
967
|
+
options?.awaitTransactionPropagation === true
|
|
968
|
+
);
|
|
971
969
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
970
|
+
resp.txId = txId;
|
|
971
|
+
resp.explorerUrl = this.explorerUrl(resp.txId);
|
|
972
|
+
|
|
973
|
+
if (
|
|
974
|
+
options?.queryBalance === undefined ||
|
|
975
|
+
options?.queryBalance === true
|
|
976
|
+
) {
|
|
977
|
+
resp.balance = (await this.getBalance()) as BalanceResponse;
|
|
978
|
+
}
|
|
979
|
+
} else {
|
|
980
|
+
resp.unsignedTransaction = binToHex(encodedTransaction);
|
|
981
|
+
resp.sourceOutputs = sourceOutputs;
|
|
978
982
|
}
|
|
979
|
-
|
|
980
|
-
resp.tokenIds = tokenIds;
|
|
983
|
+
|
|
981
984
|
return resp;
|
|
982
985
|
}
|
|
983
986
|
|
|
@@ -993,16 +996,7 @@ export class Wallet extends BaseWallet {
|
|
|
993
996
|
cashaddr: string,
|
|
994
997
|
options?: SendRequestOptionsI
|
|
995
998
|
): Promise<SendResponse> {
|
|
996
|
-
|
|
997
|
-
const queryBalance =
|
|
998
|
-
!options || options.queryBalance === undefined || options.queryBalance;
|
|
999
|
-
return {
|
|
1000
|
-
txId: txId,
|
|
1001
|
-
balance: queryBalance
|
|
1002
|
-
? ((await this.getBalance()) as BalanceResponse)
|
|
1003
|
-
: undefined,
|
|
1004
|
-
explorerUrl: this.explorerUrl(txId),
|
|
1005
|
-
};
|
|
999
|
+
return await this.sendMaxRaw(cashaddr, options);
|
|
1006
1000
|
}
|
|
1007
1001
|
|
|
1008
1002
|
/**
|
|
@@ -1016,11 +1010,13 @@ export class Wallet extends BaseWallet {
|
|
|
1016
1010
|
private async sendMaxRaw(
|
|
1017
1011
|
cashaddr: string,
|
|
1018
1012
|
options?: SendRequestOptionsI
|
|
1019
|
-
): Promise<
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1013
|
+
): Promise<SendResponse> {
|
|
1014
|
+
const { value: maxSpendableAmount, utxos } = await this._getMaxAmountToSend(
|
|
1015
|
+
{
|
|
1016
|
+
outputCount: 1,
|
|
1017
|
+
options: options,
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1024
1020
|
|
|
1025
1021
|
if (!options) {
|
|
1026
1022
|
options = {};
|
|
@@ -1028,28 +1024,40 @@ export class Wallet extends BaseWallet {
|
|
|
1028
1024
|
|
|
1029
1025
|
options.utxoIds = utxos;
|
|
1030
1026
|
|
|
1031
|
-
|
|
1027
|
+
const sendRequest = new SendRequest({
|
|
1032
1028
|
cashaddr: cashaddr,
|
|
1033
1029
|
value: maxSpendableAmount,
|
|
1034
1030
|
unit: "sat",
|
|
1035
1031
|
});
|
|
1036
1032
|
|
|
1037
|
-
const { encodedTransaction } =
|
|
1038
|
-
[sendRequest],
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1033
|
+
const { encodedTransaction, tokenIds, sourceOutputs } =
|
|
1034
|
+
await this.encodeTransaction([sendRequest], true, options);
|
|
1035
|
+
|
|
1036
|
+
const resp = new SendResponse({});
|
|
1037
|
+
resp.tokenIds = tokenIds;
|
|
1038
|
+
|
|
1039
|
+
if (options?.buildUnsigned !== true) {
|
|
1040
|
+
const txId = await this.submitTransaction(
|
|
1041
|
+
encodedTransaction,
|
|
1042
|
+
options?.awaitTransactionPropagation === undefined ||
|
|
1043
|
+
options?.awaitTransactionPropagation === true
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
resp.txId = txId;
|
|
1047
|
+
resp.explorerUrl = this.explorerUrl(resp.txId);
|
|
1051
1048
|
|
|
1052
|
-
|
|
1049
|
+
if (
|
|
1050
|
+
options?.queryBalance === undefined ||
|
|
1051
|
+
options?.queryBalance === true
|
|
1052
|
+
) {
|
|
1053
|
+
resp.balance = (await this.getBalance()) as BalanceResponse;
|
|
1054
|
+
}
|
|
1055
|
+
} else {
|
|
1056
|
+
resp.unsignedTransaction = binToHex(encodedTransaction);
|
|
1057
|
+
resp.sourceOutputs = sourceOutputs;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return resp;
|
|
1053
1061
|
}
|
|
1054
1062
|
|
|
1055
1063
|
/**
|
|
@@ -1070,7 +1078,7 @@ export class Wallet extends BaseWallet {
|
|
|
1070
1078
|
) {
|
|
1071
1079
|
let sendRequests = asSendRequestObject(requests);
|
|
1072
1080
|
|
|
1073
|
-
if (!this.privateKey) {
|
|
1081
|
+
if (!this.privateKey && options?.buildUnsigned !== true) {
|
|
1074
1082
|
throw new Error(
|
|
1075
1083
|
`Wallet ${this.name} is missing either a network or private key`
|
|
1076
1084
|
);
|
|
@@ -1124,6 +1132,13 @@ export class Wallet extends BaseWallet {
|
|
|
1124
1132
|
utxos = utxos.filter((val) => !val.token);
|
|
1125
1133
|
}
|
|
1126
1134
|
|
|
1135
|
+
// let tokenOp: "send" | "genesis" | "mint" | "burn" | undefined = undefined;
|
|
1136
|
+
// if (options?.ensureUtxos?.every(val => !val.token) && sendRequests.some(val => (val as TokenSendRequest).tokenId)) {
|
|
1137
|
+
// tokenOp = "genesis";
|
|
1138
|
+
// } else if (options?.ensureUtxos?.length === 1 && options?.ensureUtxos?.[0].token?.capability === NFTCapability.minting && ) {
|
|
1139
|
+
|
|
1140
|
+
// }
|
|
1141
|
+
|
|
1127
1142
|
const addTokenChangeOutputs = (
|
|
1128
1143
|
inputs: UtxoI[],
|
|
1129
1144
|
outputs: SendRequestType[]
|
|
@@ -1189,7 +1204,8 @@ export class Wallet extends BaseWallet {
|
|
|
1189
1204
|
const feeEstimate = await getFeeAmount({
|
|
1190
1205
|
utxos: utxos,
|
|
1191
1206
|
sendRequests: sendRequests,
|
|
1192
|
-
privateKey: this.privateKey,
|
|
1207
|
+
privateKey: this.privateKey ?? Uint8Array.from([]),
|
|
1208
|
+
sourceAddress: this.cashaddr!,
|
|
1193
1209
|
relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
|
|
1194
1210
|
slpOutputs: [],
|
|
1195
1211
|
feePaidBy: feePaidBy,
|
|
@@ -1201,7 +1217,8 @@ export class Wallet extends BaseWallet {
|
|
|
1201
1217
|
bestHeight,
|
|
1202
1218
|
feePaidBy,
|
|
1203
1219
|
sendRequests,
|
|
1204
|
-
options?.ensureUtxos || []
|
|
1220
|
+
options?.ensureUtxos || [],
|
|
1221
|
+
options?.tokenOperation
|
|
1205
1222
|
);
|
|
1206
1223
|
if (fundingUtxos.length === 0) {
|
|
1207
1224
|
throw Error(
|
|
@@ -1211,20 +1228,24 @@ export class Wallet extends BaseWallet {
|
|
|
1211
1228
|
const fee = await getFeeAmount({
|
|
1212
1229
|
utxos: fundingUtxos,
|
|
1213
1230
|
sendRequests: sendRequests,
|
|
1214
|
-
privateKey: this.privateKey,
|
|
1231
|
+
privateKey: this.privateKey ?? Uint8Array.from([]),
|
|
1232
|
+
sourceAddress: this.cashaddr!,
|
|
1215
1233
|
relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
|
|
1216
1234
|
slpOutputs: [],
|
|
1217
1235
|
feePaidBy: feePaidBy,
|
|
1218
1236
|
});
|
|
1219
|
-
const encodedTransaction = await buildEncodedTransaction(
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1237
|
+
const { encodedTransaction, sourceOutputs } = await buildEncodedTransaction(
|
|
1238
|
+
{
|
|
1239
|
+
inputs: fundingUtxos,
|
|
1240
|
+
outputs: sendRequests,
|
|
1241
|
+
signingKey: this.privateKey ?? Uint8Array.from([]),
|
|
1242
|
+
sourceAddress: this.cashaddr!,
|
|
1243
|
+
fee,
|
|
1244
|
+
discardChange,
|
|
1245
|
+
slpOutputs: [],
|
|
1246
|
+
feePaidBy,
|
|
1247
|
+
changeAddress,
|
|
1248
|
+
}
|
|
1228
1249
|
);
|
|
1229
1250
|
|
|
1230
1251
|
const tokenIds = [
|
|
@@ -1236,7 +1257,7 @@ export class Wallet extends BaseWallet {
|
|
|
1236
1257
|
.map((val) => (val as TokenSendRequest).tokenId),
|
|
1237
1258
|
].filter((value, index, array) => array.indexOf(value) === index);
|
|
1238
1259
|
|
|
1239
|
-
return { encodedTransaction, tokenIds
|
|
1260
|
+
return { encodedTransaction, tokenIds, sourceOutputs };
|
|
1240
1261
|
}
|
|
1241
1262
|
|
|
1242
1263
|
// Submit a raw transaction
|
|
@@ -1437,32 +1458,52 @@ export class Wallet extends BaseWallet {
|
|
|
1437
1458
|
* @param {string?} genesisRequest.commitment NFT commitment message
|
|
1438
1459
|
* @param {string?} genesisRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
|
|
1439
1460
|
* @param {number?} genesisRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
|
|
1440
|
-
* @param {SendRequestType} sendRequests single or an array of extra send requests (OP_RETURN, value transfer, etc.) to include in genesis transaction
|
|
1461
|
+
* @param {SendRequestType | SendRequestType[]} sendRequests single or an array of extra send requests (OP_RETURN, value transfer, etc.) to include in genesis transaction
|
|
1462
|
+
* @param {SendRequestOptionsI} options Options of the send requests
|
|
1441
1463
|
*/
|
|
1442
1464
|
public async tokenGenesis(
|
|
1443
1465
|
genesisRequest: TokenGenesisRequest,
|
|
1444
|
-
sendRequests: SendRequestType | SendRequestType[] = []
|
|
1466
|
+
sendRequests: SendRequestType | SendRequestType[] = [],
|
|
1467
|
+
options?: SendRequestOptionsI
|
|
1445
1468
|
): Promise<SendResponse> {
|
|
1446
1469
|
if (!Array.isArray(sendRequests)) {
|
|
1447
1470
|
sendRequests = [sendRequests];
|
|
1448
1471
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1472
|
+
|
|
1473
|
+
let utxos: UtxoI[];
|
|
1474
|
+
if (options && options.utxoIds) {
|
|
1475
|
+
utxos = options.utxoIds.map((utxoId: UtxoI | string) =>
|
|
1476
|
+
typeof utxoId === "string" ? fromUtxoId(utxoId) : utxoId
|
|
1477
|
+
);
|
|
1478
|
+
} else {
|
|
1479
|
+
utxos = await this.getAddressUtxos(this.cashaddr);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const genesisInputs = utxos.filter((val) => val.vout === 0);
|
|
1483
|
+
if (genesisInputs.length === 0) {
|
|
1484
|
+
throw new Error(
|
|
1485
|
+
"No suitable inputs with vout=0 available for new token genesis"
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
const genesisSendRequest = new TokenSendRequest({
|
|
1490
|
+
cashaddr: genesisRequest.cashaddr || this.tokenaddr!,
|
|
1491
|
+
amount: genesisRequest.amount,
|
|
1492
|
+
value: genesisRequest.value || 1000,
|
|
1493
|
+
capability: genesisRequest.capability,
|
|
1494
|
+
commitment: genesisRequest.commitment,
|
|
1495
|
+
tokenId: utxos[0].txid,
|
|
1496
|
+
});
|
|
1497
|
+
// TODO: remove
|
|
1498
|
+
(genesisSendRequest as any)._isGenesis = true;
|
|
1499
|
+
return this.send([genesisSendRequest, ...(sendRequests as any)], {
|
|
1500
|
+
...options,
|
|
1501
|
+
utxoIds: utxos,
|
|
1502
|
+
ensureUtxos: [utxos[0]],
|
|
1503
|
+
checkTokenQuantities: false,
|
|
1504
|
+
queryBalance: false,
|
|
1505
|
+
tokenOperation: "genesis",
|
|
1506
|
+
});
|
|
1466
1507
|
}
|
|
1467
1508
|
|
|
1468
1509
|
/**
|
|
@@ -1475,12 +1516,18 @@ export class Wallet extends BaseWallet {
|
|
|
1475
1516
|
* @param {string?} mintRequest.cashaddr cash address to send the created token UTXO to; if undefined will default to your address
|
|
1476
1517
|
* @param {number?} mintRequest.value satoshi value to send alongside with tokens; if undefined will default to 1000 satoshi
|
|
1477
1518
|
* @param {boolean?} deductTokenAmount if minting token contains fungible amount, deduct from it by amount of minted tokens
|
|
1519
|
+
* @param {SendRequestOptionsI} options Options of the send requests
|
|
1478
1520
|
*/
|
|
1479
1521
|
public async tokenMint(
|
|
1480
1522
|
tokenId: string,
|
|
1481
1523
|
mintRequests: TokenMintRequest | Array<TokenMintRequest>,
|
|
1482
|
-
deductTokenAmount: boolean = false
|
|
1524
|
+
deductTokenAmount: boolean = false,
|
|
1525
|
+
options?: SendRequestOptionsI
|
|
1483
1526
|
): Promise<SendResponse> {
|
|
1527
|
+
if (tokenId?.length !== 64) {
|
|
1528
|
+
throw Error(`Invalid tokenId supplied: ${tokenId}`);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1484
1531
|
if (!Array.isArray(mintRequests)) {
|
|
1485
1532
|
mintRequests = [mintRequests];
|
|
1486
1533
|
}
|
|
@@ -1489,7 +1536,7 @@ export class Wallet extends BaseWallet {
|
|
|
1489
1536
|
const nftUtxos = utxos.filter(
|
|
1490
1537
|
(val) =>
|
|
1491
1538
|
val.token?.tokenId === tokenId &&
|
|
1492
|
-
val.token?.capability
|
|
1539
|
+
val.token?.capability === NFTCapability.minting
|
|
1493
1540
|
);
|
|
1494
1541
|
if (!nftUtxos.length) {
|
|
1495
1542
|
throw new Error(
|
|
@@ -1525,8 +1572,11 @@ export class Wallet extends BaseWallet {
|
|
|
1525
1572
|
),
|
|
1526
1573
|
],
|
|
1527
1574
|
{
|
|
1575
|
+
...options,
|
|
1576
|
+
ensureUtxos: [nftUtxos[0]],
|
|
1528
1577
|
checkTokenQuantities: false,
|
|
1529
1578
|
queryBalance: false,
|
|
1579
|
+
tokenOperation: "mint",
|
|
1530
1580
|
}
|
|
1531
1581
|
);
|
|
1532
1582
|
}
|
|
@@ -1545,18 +1595,23 @@ export class Wallet extends BaseWallet {
|
|
|
1545
1595
|
* @param {number?} burnRequest.amount amount of fungible tokens to burn, optional
|
|
1546
1596
|
* @param {string?} burnRequest.cashaddr address to return token and satoshi change to
|
|
1547
1597
|
* @param {string?} message optional message to include in OP_RETURN
|
|
1598
|
+
* @param {SendRequestOptionsI} options Options of the send requests
|
|
1548
1599
|
*/
|
|
1549
1600
|
public async tokenBurn(
|
|
1550
1601
|
burnRequest: TokenBurnRequest,
|
|
1551
|
-
message?: string
|
|
1602
|
+
message?: string,
|
|
1603
|
+
options?: SendRequestOptionsI
|
|
1552
1604
|
): Promise<SendResponse> {
|
|
1605
|
+
if (burnRequest.tokenId?.length !== 64) {
|
|
1606
|
+
throw Error(`Invalid tokenId supplied: ${burnRequest.tokenId}`);
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1553
1609
|
const utxos = await this.getAddressUtxos(this.cashaddr!);
|
|
1554
1610
|
const tokenUtxos = utxos.filter(
|
|
1555
1611
|
(val) =>
|
|
1556
1612
|
val.token?.tokenId === burnRequest.tokenId &&
|
|
1557
1613
|
val.token?.capability === burnRequest.capability &&
|
|
1558
|
-
val.token?.commitment === burnRequest.commitment
|
|
1559
|
-
val.token?.capability === burnRequest.capability
|
|
1614
|
+
val.token?.commitment === burnRequest.commitment
|
|
1560
1615
|
);
|
|
1561
1616
|
|
|
1562
1617
|
if (!tokenUtxos.length) {
|
|
@@ -1579,6 +1634,16 @@ export class Wallet extends BaseWallet {
|
|
|
1579
1634
|
changeSendRequests = [];
|
|
1580
1635
|
utxoIds.push(tokenUtxos[0]);
|
|
1581
1636
|
} else {
|
|
1637
|
+
// add utxos to spend from
|
|
1638
|
+
let available = 0;
|
|
1639
|
+
for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
|
|
1640
|
+
utxoIds.push(token);
|
|
1641
|
+
available += token.token?.amount!;
|
|
1642
|
+
if (available >= fungibleBurnAmount) {
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1582
1647
|
// if there are FT, reduce their amount
|
|
1583
1648
|
const newAmount = totalFungibleAmount - fungibleBurnAmount;
|
|
1584
1649
|
const safeNewAmount = Math.max(0, newAmount);
|
|
@@ -1599,6 +1664,16 @@ export class Wallet extends BaseWallet {
|
|
|
1599
1664
|
changeSendRequests = [];
|
|
1600
1665
|
utxoIds.push(tokenUtxos[0]);
|
|
1601
1666
|
} else {
|
|
1667
|
+
// add utxos to spend from
|
|
1668
|
+
let available = 0;
|
|
1669
|
+
for (const token of tokenUtxos.filter((val) => val.token?.amount)) {
|
|
1670
|
+
utxoIds.push(token);
|
|
1671
|
+
available += token.token?.amount!;
|
|
1672
|
+
if (available >= fungibleBurnAmount) {
|
|
1673
|
+
break;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1602
1677
|
// reduce the FT amount
|
|
1603
1678
|
const newAmount = totalFungibleAmount - fungibleBurnAmount;
|
|
1604
1679
|
const safeNewAmount = Math.max(0, newAmount);
|
|
@@ -1615,9 +1690,11 @@ export class Wallet extends BaseWallet {
|
|
|
1615
1690
|
|
|
1616
1691
|
const opReturn = OpReturnData.fromString(message || "");
|
|
1617
1692
|
return this.send([opReturn, ...changeSendRequests], {
|
|
1693
|
+
...options,
|
|
1618
1694
|
checkTokenQuantities: false,
|
|
1619
1695
|
queryBalance: false,
|
|
1620
1696
|
ensureUtxos: utxoIds.length > 0 ? utxoIds : undefined,
|
|
1697
|
+
tokenOperation: "burn",
|
|
1621
1698
|
});
|
|
1622
1699
|
}
|
|
1623
1700
|
|
|
@@ -1641,7 +1718,9 @@ export class Wallet extends BaseWallet {
|
|
|
1641
1718
|
* @returns {number} fungible token balance
|
|
1642
1719
|
*/
|
|
1643
1720
|
public async getTokenBalance(tokenId: string): Promise<number> {
|
|
1644
|
-
const utxos = await this.
|
|
1721
|
+
const utxos = (await this.getTokenUtxos(tokenId)).filter(
|
|
1722
|
+
(val) => val.token?.amount
|
|
1723
|
+
);
|
|
1645
1724
|
return sumTokenAmounts(utxos, tokenId);
|
|
1646
1725
|
}
|
|
1647
1726
|
|
|
@@ -1653,7 +1732,9 @@ export class Wallet extends BaseWallet {
|
|
|
1653
1732
|
* @returns {number} non-fungible token balance
|
|
1654
1733
|
*/
|
|
1655
1734
|
public async getNftTokenBalance(tokenId: string): Promise<number> {
|
|
1656
|
-
const utxos = await this.getTokenUtxos(tokenId)
|
|
1735
|
+
const utxos = (await this.getTokenUtxos(tokenId)).filter(
|
|
1736
|
+
(val) => val.token?.commitment !== undefined
|
|
1737
|
+
);
|
|
1657
1738
|
return utxos.length;
|
|
1658
1739
|
}
|
|
1659
1740
|
|
|
@@ -1661,9 +1742,11 @@ export class Wallet extends BaseWallet {
|
|
|
1661
1742
|
* getAllTokenBalances Gets all fungible token balances in this wallet
|
|
1662
1743
|
* @returns {Object} a map [tokenId => balance] for all tokens in this wallet
|
|
1663
1744
|
*/
|
|
1664
|
-
public async getAllTokenBalances(): Promise<
|
|
1745
|
+
public async getAllTokenBalances(): Promise<{ [tokenId: string]: number }> {
|
|
1665
1746
|
const result = {};
|
|
1666
|
-
const utxos = await this.getTokenUtxos()
|
|
1747
|
+
const utxos = (await this.getTokenUtxos()).filter(
|
|
1748
|
+
(val) => val.token?.amount
|
|
1749
|
+
);
|
|
1667
1750
|
for (const utxo of utxos) {
|
|
1668
1751
|
if (!result[utxo.token!.tokenId]) {
|
|
1669
1752
|
result[utxo.token!.tokenId] = 0;
|
|
@@ -1677,9 +1760,13 @@ export class Wallet extends BaseWallet {
|
|
|
1677
1760
|
* getAllNftTokenBalances Gets all non-fungible token (NFT) balances in this wallet
|
|
1678
1761
|
* @returns {Object} a map [tokenId => balance] for all NFTs in this wallet
|
|
1679
1762
|
*/
|
|
1680
|
-
public async getAllNftTokenBalances(): Promise<
|
|
1763
|
+
public async getAllNftTokenBalances(): Promise<{
|
|
1764
|
+
[tokenId: string]: number;
|
|
1765
|
+
}> {
|
|
1681
1766
|
const result = {};
|
|
1682
|
-
const utxos = await this.getTokenUtxos()
|
|
1767
|
+
const utxos = (await this.getTokenUtxos()).filter(
|
|
1768
|
+
(val) => val.token?.commitment !== undefined
|
|
1769
|
+
);
|
|
1683
1770
|
for (const utxo of utxos) {
|
|
1684
1771
|
if (!result[utxo.token!.tokenId]) {
|
|
1685
1772
|
result[utxo.token!.tokenId] = 0;
|
package/src/wallet/interface.ts
CHANGED
|
@@ -54,7 +54,9 @@ export interface SendRequestOptionsI {
|
|
|
54
54
|
awaitTransactionPropagation?: boolean;
|
|
55
55
|
feePaidBy?: FeePaidByEnum;
|
|
56
56
|
checkTokenQuantities?: boolean; // true
|
|
57
|
+
tokenOperation?: "send" | "genesis" | "mint" | "burn"; // undefined. internal use only
|
|
57
58
|
ensureUtxos?: UtxoI[]; // ensure these inputs will be consumed in the transaction
|
|
59
|
+
buildUnsigned?: boolean; // false
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export interface MnemonicI {
|
package/src/wallet/model.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { UnitEnum } from "../enum.js";
|
|
|
4
4
|
import { NFTCapability, UtxoI } from "../interface.js";
|
|
5
5
|
import { DELIMITER } from "../constant.js";
|
|
6
6
|
import {
|
|
7
|
+
Input,
|
|
8
|
+
Output,
|
|
7
9
|
binToNumberUint16LE,
|
|
8
10
|
binToUtf8,
|
|
9
11
|
hexToBin,
|
|
@@ -312,11 +314,15 @@ export class OpReturnData {
|
|
|
312
314
|
|
|
313
315
|
export type SendRequestArray = Array<string | number | UnitEnum | Buffer>;
|
|
314
316
|
|
|
317
|
+
export type SourceOutput = Input & Output;
|
|
318
|
+
|
|
315
319
|
export class SendResponse {
|
|
316
320
|
txId?: string;
|
|
317
321
|
balance?: BalanceResponse;
|
|
318
322
|
explorerUrl?: string;
|
|
319
323
|
tokenIds?: string[];
|
|
324
|
+
unsignedTransaction?: string; // unsigned transaction hex
|
|
325
|
+
sourceOutputs?: SourceOutput[]; // source outputs for signing unsigned transactions
|
|
320
326
|
|
|
321
327
|
constructor({
|
|
322
328
|
txId,
|