mainnet-js 0.4.34 → 0.4.35

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.
Files changed (42) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/main/transaction/Wif.d.ts +6 -4
  3. package/dist/main/transaction/Wif.js +14 -7
  4. package/dist/main/transaction/Wif.js.map +1 -1
  5. package/dist/main/transaction/allocateFee.d.ts +7 -0
  6. package/dist/main/transaction/allocateFee.js +118 -0
  7. package/dist/main/transaction/allocateFee.js.map +1 -0
  8. package/dist/main/wallet/Slp.js +4 -1
  9. package/dist/main/wallet/Slp.js.map +1 -1
  10. package/dist/main/wallet/Wif.js +20 -3
  11. package/dist/main/wallet/Wif.js.map +1 -1
  12. package/dist/main/wallet/enum.d.ts +9 -0
  13. package/dist/main/wallet/enum.js +11 -1
  14. package/dist/main/wallet/enum.js.map +1 -1
  15. package/dist/main/wallet/interface.d.ts +2 -1
  16. package/dist/mainnet-0.4.35.js +2 -0
  17. package/dist/{mainnet-0.4.34.js.LICENSE.txt → mainnet-0.4.35.js.LICENSE.txt} +0 -0
  18. package/dist/module/transaction/Wif.d.ts +6 -4
  19. package/dist/module/transaction/Wif.js +14 -7
  20. package/dist/module/transaction/Wif.js.map +1 -1
  21. package/dist/module/transaction/allocateFee.d.ts +7 -0
  22. package/dist/module/transaction/allocateFee.js +110 -0
  23. package/dist/module/transaction/allocateFee.js.map +1 -0
  24. package/dist/module/wallet/Slp.js +4 -1
  25. package/dist/module/wallet/Slp.js.map +1 -1
  26. package/dist/module/wallet/Wif.js +21 -4
  27. package/dist/module/wallet/Wif.js.map +1 -1
  28. package/dist/module/wallet/enum.d.ts +9 -0
  29. package/dist/module/wallet/enum.js +10 -0
  30. package/dist/module/wallet/enum.js.map +1 -1
  31. package/dist/module/wallet/interface.d.ts +2 -1
  32. package/dist/tsconfig.browser.tsbuildinfo +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +1 -1
  35. package/src/transaction/Wif.ts +24 -7
  36. package/src/transaction/allocateFee.test.ts +309 -0
  37. package/src/transaction/allocateFee.ts +132 -0
  38. package/src/wallet/Slp.ts +5 -1
  39. package/src/wallet/Wif.ts +29 -4
  40. package/src/wallet/enum.ts +10 -0
  41. package/src/wallet/interface.ts +2 -1
  42. package/dist/mainnet-0.4.34.js +0 -2
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "url": "https://github.com/mainnet-cash/mainnet-js/issues"
9
9
  },
10
10
  "name": "mainnet-js",
11
- "version": "0.4.34",
11
+ "version": "0.4.35",
12
12
  "homepage": "https://mainnet.cash",
13
13
  "main": "dist/main/index.js",
14
14
  "module": "dist/module/index.js",
@@ -13,12 +13,14 @@ import {
13
13
  AuthenticationProgramStateBCH,
14
14
  } from "@bitauth/libauth";
15
15
  import { UtxoI } from "../interface";
16
+ import { allocateFee } from "./allocateFee";
16
17
 
17
18
  import { DUST_UTXO_THRESHOLD } from "../constant";
18
19
  import { OpReturnData, SendRequest } from "../wallet/model";
19
20
  import { amountInSatoshi } from "../util/amountInSatoshi";
20
21
  import { sumSendRequestAmounts } from "../util/sumSendRequestAmounts";
21
22
  import { sumUtxoValue } from "../util/sumUtxoValue";
23
+ import { FeePaidByEnum } from "../wallet/enum";
22
24
 
23
25
  // Build a transaction for a p2pkh transaction for a non HD wallet
24
26
  export async function buildP2pkhNonHdTransaction(
@@ -27,7 +29,8 @@ export async function buildP2pkhNonHdTransaction(
27
29
  signingKey: Uint8Array,
28
30
  fee: number = 0,
29
31
  discardChange = false,
30
- slpOutputs: any[] = []
32
+ slpOutputs: any[] = [],
33
+ feePaidBy: FeePaidByEnum = FeePaidByEnum.change
31
34
  ) {
32
35
  if (!signingKey) {
33
36
  throw new Error("Missing signing key when building transaction");
@@ -42,6 +45,7 @@ export async function buildP2pkhNonHdTransaction(
42
45
 
43
46
  const compiler = await authenticationTemplateToCompilerBCH(template);
44
47
  const inputAmount = await sumUtxoValue(inputs);
48
+
45
49
  const sendAmount = await sumSendRequestAmounts(outputs);
46
50
 
47
51
  // Get the change locking bytecode
@@ -53,11 +57,13 @@ export async function buildP2pkhNonHdTransaction(
53
57
  }
54
58
 
55
59
  try {
60
+ const changeAmount = BigInt(inputAmount) - BigInt(sendAmount) - BigInt(fee);
61
+
62
+ outputs = allocateFee(outputs, fee, feePaidBy, changeAmount);
63
+
56
64
  let lockedOutputs = await prepareOutputs(outputs);
57
65
 
58
66
  if (discardChange !== true) {
59
- const changeAmount =
60
- BigInt(inputAmount) - BigInt(sendAmount) - BigInt(fee);
61
67
  if (changeAmount > DUST_UTXO_THRESHOLD) {
62
68
  lockedOutputs.push({
63
69
  lockingBytecode: changeLockingBytecode.bytecode,
@@ -188,7 +194,8 @@ export function prepareOpReturnOutput(request: OpReturnData) {
188
194
  export async function getSuitableUtxos(
189
195
  unspentOutputs: UtxoI[],
190
196
  amountRequired: BigInt | undefined,
191
- bestHeight: number
197
+ bestHeight: number,
198
+ feePaidBy: FeePaidByEnum
192
199
  ): Promise<UtxoI[]> {
193
200
  let suitableUtxos: UtxoI[] = [];
194
201
  let amountAvailable = BigInt(0);
@@ -209,6 +216,11 @@ export async function getSuitableUtxos(
209
216
  }
210
217
  }
211
218
 
219
+ // if the fee is split with a feePaidBy option, skip checking change.
220
+ if (feePaidBy && feePaidBy != FeePaidByEnum.change) {
221
+ return suitableUtxos;
222
+ }
223
+
212
224
  // If the amount needed is met, or no amount is given, return
213
225
  if (typeof amountRequired === "undefined") {
214
226
  return suitableUtxos;
@@ -232,12 +244,14 @@ export async function getFeeAmount({
232
244
  privateKey,
233
245
  relayFeePerByteInSatoshi,
234
246
  slpOutputs,
247
+ feePaidBy,
235
248
  }: {
236
249
  utxos: UtxoI[];
237
250
  sendRequests: Array<SendRequest | OpReturnData>;
238
251
  privateKey: Uint8Array;
239
252
  relayFeePerByteInSatoshi: number;
240
253
  slpOutputs: any[];
254
+ feePaidBy: FeePaidByEnum;
241
255
  }) {
242
256
  // build transaction
243
257
  if (utxos) {
@@ -248,7 +262,8 @@ export async function getFeeAmount({
248
262
  privateKey,
249
263
  0, //DUST_UTXO_THRESHOLD
250
264
  false,
251
- slpOutputs
265
+ slpOutputs,
266
+ feePaidBy
252
267
  );
253
268
 
254
269
  return draftTransaction.length * relayFeePerByteInSatoshi + 1;
@@ -266,7 +281,8 @@ export async function buildEncodedTransaction(
266
281
  privateKey: Uint8Array,
267
282
  fee: number = 0,
268
283
  discardChange = false,
269
- slpOutputs: any[] = []
284
+ slpOutputs: any[] = [],
285
+ feePaidBy: FeePaidByEnum = FeePaidByEnum.change
270
286
  ) {
271
287
  let txn = await buildP2pkhNonHdTransaction(
272
288
  fundingUtxos,
@@ -274,7 +290,8 @@ export async function buildEncodedTransaction(
274
290
  privateKey,
275
291
  fee,
276
292
  discardChange,
277
- slpOutputs
293
+ slpOutputs,
294
+ feePaidBy
278
295
  );
279
296
  // submit transaction
280
297
  if (txn.success) {
@@ -0,0 +1,309 @@
1
+ import { allocateFee, sortSendRequests } from "./allocateFee";
2
+ import { FeePaidByEnum } from "../wallet/enum";
3
+ import { asSendRequestObject } from "../util/asSendRequestObject";
4
+ import { SendRequest } from "..";
5
+ import { RegTestWallet } from "../wallet/Wif";
6
+
7
+ test("Should get the regtest wallet balance", async () => {
8
+ // Build Alice's wallet from Wallet Import Format string, send some sats
9
+ if (!process.env.ADDRESS) {
10
+ throw Error("Attempted to pass an empty address");
11
+ } else {
12
+ const funder = await RegTestWallet.fromId(
13
+ `wif:regtest:${process.env.PRIVATE_WIF!}`
14
+ );
15
+ const alice = await RegTestWallet.newRandom();
16
+ const bob = await RegTestWallet.newRandom();
17
+ const charlie = await RegTestWallet.newRandom();
18
+ const dave = await RegTestWallet.newRandom();
19
+ const edward = await RegTestWallet.newRandom();
20
+ await funder.send([
21
+ {
22
+ cashaddr: alice.cashaddr!,
23
+ value: 4500,
24
+ unit: "satoshis",
25
+ },
26
+ ]);
27
+ await alice.send(
28
+ [
29
+ {
30
+ cashaddr: bob.cashaddr!,
31
+ unit: "sat",
32
+ value: 549,
33
+ },
34
+ {
35
+ cashaddr: charlie.cashaddr!,
36
+ unit: "sat",
37
+ value: 550,
38
+ },
39
+ {
40
+ cashaddr: dave.cashaddr!,
41
+ unit: "sat",
42
+ value: 551,
43
+ },
44
+ {
45
+ cashaddr: edward.cashaddr!,
46
+ unit: "sat",
47
+ value: 2552,
48
+ },
49
+ ],
50
+ { feePaidBy: FeePaidByEnum.changeThenAny }
51
+ );
52
+ expect(await alice.getBalance("sat")).toBe(0);
53
+ expect(await bob.getBalance("sat")).toBe(0);
54
+ expect(await charlie.getBalance("sat")).toBe(550);
55
+ expect(await dave.getBalance("sat")).toBe(551);
56
+ expect(await edward.getBalance("sat")).toBe(2552);
57
+ }
58
+ });
59
+
60
+ test("Expect first in to subtract fee from first", async () => {
61
+ let to = [
62
+ ["alice", 2000, "sat"],
63
+ ["bob", 2000, "sat"],
64
+ ];
65
+ let fee = 1;
66
+ let requests = asSendRequestObject(to) as SendRequest[];
67
+ let allocatedInputs = await allocateFee(
68
+ requests,
69
+ fee,
70
+ FeePaidByEnum.first,
71
+ BigInt(0)
72
+ );
73
+ expect(allocatedInputs[0].value).toBeLessThan(2000);
74
+ expect(allocatedInputs[1].value).toBe(2000);
75
+ });
76
+
77
+ test("Expect last to subtract fee from last", async () => {
78
+ let to = [
79
+ ["alice", 2000, "sat"],
80
+ ["bob", 2000, "sat"],
81
+ ];
82
+ let fee = 1;
83
+ let requests = asSendRequestObject(to) as SendRequest[];
84
+ let allocatedInputs = allocateFee(
85
+ requests,
86
+ fee,
87
+ FeePaidByEnum.last,
88
+ BigInt(0)
89
+ );
90
+
91
+ expect(allocatedInputs[0].value).toBe(2000);
92
+ expect(allocatedInputs[1].value).toBeLessThan(2000);
93
+ });
94
+
95
+ test("Expect all to allocate fees equally", async () => {
96
+ let to = [
97
+ ["alice", 2000, "sat"],
98
+ ["bob", 2000, "sat"],
99
+ ["charlie", 2000, "sat"],
100
+ ];
101
+ let fee = 3;
102
+ let requests = asSendRequestObject(to) as SendRequest[];
103
+
104
+ let allocatedInputs = allocateFee(
105
+ requests,
106
+ fee,
107
+ FeePaidByEnum.any,
108
+ BigInt(0)
109
+ );
110
+
111
+ expect(allocatedInputs[0].value).toBe(1999);
112
+ expect(allocatedInputs[1].value).toBe(1999);
113
+ expect(allocatedInputs[2].value).toBe(1999);
114
+ });
115
+
116
+ test("Expect all to allocate fees equally, taking dust result", async () => {
117
+ let to = [
118
+ ["alice", 2000, "sat"],
119
+ ["bob", 547, "sat"],
120
+ ["charlie", 2000, "sat"],
121
+ ];
122
+ let fee = 300;
123
+ let requests = asSendRequestObject(to) as SendRequest[];
124
+
125
+ let allocatedInputs = allocateFee(
126
+ requests,
127
+ fee,
128
+ FeePaidByEnum.any,
129
+ BigInt(0)
130
+ );
131
+
132
+ expect(allocatedInputs[0].value).toBe(2000);
133
+ expect(allocatedInputs[1].value).toBe(2000);
134
+ expect(allocatedInputs.length).toBe(2);
135
+ });
136
+
137
+ test("Expect all to allocate fees equally, taking dust result output, dividing remainder", async () => {
138
+ let to = [
139
+ ["alice", 2000, "sat"],
140
+ ["bob", 547, "sat"],
141
+ ["charlie", 2000, "sat"],
142
+ ];
143
+ let fee = 647;
144
+ let requests = asSendRequestObject(to) as SendRequest[];
145
+
146
+ let allocatedInputs = allocateFee(
147
+ requests,
148
+ fee,
149
+ FeePaidByEnum.any,
150
+ BigInt(0)
151
+ );
152
+
153
+ expect(allocatedInputs[0].value).toBe(1950);
154
+ expect(allocatedInputs[1].value).toBe(1950);
155
+ expect(allocatedInputs.length).toBe(2);
156
+ });
157
+
158
+ test("Expect an odd fee to be applied to have remainder applied to first receipt", async () => {
159
+ let to = [
160
+ ["alice", 2000, "sat"],
161
+ ["bob", 2000, "sat"],
162
+ ["charlie", 2000, "sat"],
163
+ ];
164
+ let fee = 301;
165
+ let requests = asSendRequestObject(to) as SendRequest[];
166
+
167
+ let allocatedInputs = allocateFee(
168
+ requests,
169
+ fee,
170
+ FeePaidByEnum.any,
171
+ BigInt(0)
172
+ );
173
+
174
+ expect(allocatedInputs[0].value).toBe(1899);
175
+ expect(allocatedInputs[1].value).toBe(1900);
176
+ expect(allocatedInputs[2].value).toBe(1900);
177
+ });
178
+
179
+ test("Expect insufficient funds to error", async () => {
180
+ expect.assertions(1);
181
+ try {
182
+ let to = [
183
+ ["alice", 2000, "sat"],
184
+ ["bob", 2000, "sat"],
185
+ ["charlie", 2000, "sat"],
186
+ ];
187
+ let fee = 7000;
188
+ let requests = asSendRequestObject(to) as SendRequest[];
189
+ let allocatedInputs = allocateFee(
190
+ requests,
191
+ fee,
192
+ FeePaidByEnum.changeThenAny,
193
+ BigInt(999)
194
+ );
195
+ } catch (e: any) {
196
+ expect(e.message).toBe("Insufficient funds for transaction given fee");
197
+ }
198
+ });
199
+
200
+ test("Expect dust amounts to error", async () => {
201
+ expect.assertions(1);
202
+ try {
203
+ let to = [
204
+ ["alice", 2000, "sat"],
205
+ ["bob", 2000, "sat"],
206
+ ["charlie", 2000, "sat"],
207
+ ];
208
+ let fee = 1500;
209
+ let requests = asSendRequestObject(to) as SendRequest[];
210
+ let allocatedInputs = allocateFee(
211
+ requests,
212
+ fee,
213
+ FeePaidByEnum.first,
214
+ BigInt(0)
215
+ );
216
+ } catch (e: any) {
217
+ expect(e.message).toBe("Fee strategy would result in dust output");
218
+ }
219
+ });
220
+
221
+ test("Expect near-dust amounts not to error", async () => {
222
+ let to = [
223
+ ["alice", 1000, "sat"],
224
+ ["bob", 1000, "sat"],
225
+ ["charlie", 1000, "sat"],
226
+ ];
227
+ let fee = 1362;
228
+ let requests = asSendRequestObject(to) as SendRequest[];
229
+ let result = allocateFee(requests, fee, FeePaidByEnum.any, BigInt(0));
230
+ expect(result[0].value).toBe(546);
231
+ expect(result[1].value).toBe(546);
232
+ expect(result[2].value).toBe(546);
233
+ });
234
+
235
+ test("Expect `any` to not consume change", async () => {
236
+ let to = [
237
+ ["alice", 1000, "sat"],
238
+ ["bob", 1000, "sat"],
239
+ ["charlie", 1000, "sat"],
240
+ ];
241
+ let fee = 1362;
242
+ let requests = asSendRequestObject(to) as SendRequest[];
243
+ let result = allocateFee(requests, fee, FeePaidByEnum.any, BigInt(1362));
244
+ expect(result[0].value).toBe(546);
245
+ expect(result[1].value).toBe(546);
246
+ expect(result[2].value).toBe(546);
247
+ });
248
+
249
+ test("Expect `change,any` to consume only change", async () => {
250
+ let to = [
251
+ ["alice", 1000, "sat"],
252
+ ["bob", 1000, "sat"],
253
+ ["charlie", 1000, "sat"],
254
+ ];
255
+ let fee = 1362;
256
+ let requests = asSendRequestObject(to) as SendRequest[];
257
+ let result = allocateFee(
258
+ requests,
259
+ fee,
260
+ FeePaidByEnum.changeThenAny,
261
+ BigInt(1362)
262
+ );
263
+ expect(result[0].value).toBe(1000);
264
+ expect(result[1].value).toBe(1000);
265
+ expect(result[2].value).toBe(1000);
266
+ });
267
+
268
+ test("Expect `change,any` to use both", async () => {
269
+ let to = [
270
+ ["alice", 1000, "sat"],
271
+ ["bob", 1000, "sat"],
272
+ ["charlie", 1000, "sat"],
273
+ ];
274
+ let fee = 1362 * 2;
275
+ let requests = asSendRequestObject(to) as SendRequest[];
276
+ let result = allocateFee(
277
+ requests,
278
+ fee,
279
+ FeePaidByEnum.changeThenAny,
280
+ BigInt(1362)
281
+ );
282
+ expect(result[0].value).toBe(546);
283
+ expect(result[1].value).toBe(546);
284
+ expect(result[2].value).toBe(546);
285
+ });
286
+
287
+ test("Expect sortSendRequests to sort by lowest value first", async () => {
288
+ let to = [
289
+ ["alice", 2000, "sat"],
290
+ ["bob", 547, "sat"],
291
+ ["charlie", 1, "sat"],
292
+ ["dave", 4, "sat"],
293
+ ["edward", 6, "sat"],
294
+ ["fred", 2000, "sat"],
295
+ ["greg", 2000, "sat"],
296
+ ["harry", 2000, "sat"],
297
+ ];
298
+ let fee = 1;
299
+ let requests = asSendRequestObject(to) as SendRequest[];
300
+
301
+ let result = sortSendRequests(requests);
302
+ expect(result[0].value).toBe(1);
303
+ expect(result[1].value).toBe(4);
304
+ expect(result[2].value).toBe(6);
305
+ expect(result[3].value).toBe(547);
306
+ expect(result[4].value).toBe(2000);
307
+ expect(result[5].value).toBe(2000);
308
+ expect(result.length).toBe(8);
309
+ });
@@ -0,0 +1,132 @@
1
+ import { OpReturnData, SendRequest } from "../wallet/model";
2
+ import { FeePaidByEnum } from "../wallet/enum";
3
+ import { DUST_UTXO_THRESHOLD } from "../constant";
4
+
5
+ export function checkFeeForDust(value: number) {
6
+ if (value < DUST_UTXO_THRESHOLD) {
7
+ throw Error("Fee strategy would result in dust output");
8
+ }
9
+ }
10
+
11
+ export function checkSatsAvailable(
12
+ sendRequestArray: Array<SendRequest>,
13
+ fee: number
14
+ ) {
15
+ let amountAvailable = sendRequestArray.reduce(function (sum, r) {
16
+ return sum + (r.value - DUST_UTXO_THRESHOLD);
17
+ }, 0);
18
+ if (amountAvailable < fee) {
19
+ throw Error("Insufficient funds for transaction given fee");
20
+ }
21
+ }
22
+
23
+ export function checkForOpReturn(
24
+ output: SendRequest | OpReturnData
25
+ ): SendRequest {
26
+ if (output instanceof OpReturnData) {
27
+ throw Error("Cannot specify fee to be paid by OpReturnData");
28
+ }
29
+ return output;
30
+ }
31
+
32
+ export function sortSendRequests(sendRequestArray: Array<SendRequest>) {
33
+ return sendRequestArray.sort(
34
+ (a: SendRequest, b: SendRequest) => a.value - b.value
35
+ );
36
+ }
37
+
38
+ function distributeFees(requests: Array<SendRequest>, fee: number) {
39
+ checkSatsAvailable(requests, fee);
40
+ fee = Number(fee);
41
+ for (let r = 0; r < requests.length; r++) {
42
+ if (fee > 0) {
43
+ checkForOpReturn(requests[r]);
44
+ let perRequestFee = Math.floor(fee / (requests.length - r));
45
+ perRequestFee += fee % (requests.length - r);
46
+ if (requests[r].value - perRequestFee < DUST_UTXO_THRESHOLD) {
47
+ fee -= requests[r].value;
48
+ requests[r].value = 0;
49
+ } else {
50
+ fee -= perRequestFee;
51
+ requests[r].value -= perRequestFee;
52
+ }
53
+ }
54
+ }
55
+ return requests.filter((r) => r.value >= DUST_UTXO_THRESHOLD);
56
+ }
57
+
58
+ function firstPays(requests: Array<SendRequest | OpReturnData>, fee: number) {
59
+ let payer = requests.shift()!;
60
+ payer = checkForOpReturn(payer);
61
+ payer.value = payer.value! - fee;
62
+ checkFeeForDust(payer.value);
63
+ requests.unshift(payer);
64
+ return requests;
65
+ }
66
+ function lastPays(requests: Array<SendRequest | OpReturnData>, fee: number) {
67
+ let payer = requests.pop()!;
68
+ payer = checkForOpReturn(payer);
69
+ payer.value = payer.value! - fee;
70
+ checkFeeForDust(payer.value);
71
+ requests.push(payer);
72
+ return requests;
73
+ }
74
+ function anyPays(requests: Array<SendRequest | OpReturnData>, fee: number) {
75
+ for (let r of requests) {
76
+ checkForOpReturn(r);
77
+ }
78
+ requests = sortSendRequests(requests as Array<SendRequest>);
79
+ requests = distributeFees(requests as Array<SendRequest>, fee);
80
+ return requests;
81
+ }
82
+
83
+ function changeThenFallback(
84
+ requests: Array<SendRequest | OpReturnData>,
85
+ fee: number,
86
+ change: bigint,
87
+ fallbackFn: Function
88
+ ) {
89
+ if (BigInt(fee) > change) {
90
+ let outstandingFee = BigInt(fee) - change;
91
+ requests = fallbackFn(requests, outstandingFee);
92
+ }
93
+ return requests;
94
+ }
95
+
96
+ export function allocateFee(
97
+ requests: Array<SendRequest | OpReturnData>,
98
+ fee: number,
99
+ feePaidBy: FeePaidByEnum,
100
+ change: bigint
101
+ ): Array<SendRequest> {
102
+ if (requests.length > 0) {
103
+ switch (feePaidBy) {
104
+ case FeePaidByEnum.change:
105
+ // handled by default
106
+ break;
107
+ case FeePaidByEnum.changeThenFirst:
108
+ requests = changeThenFallback(requests, fee, change, firstPays);
109
+ break;
110
+ case FeePaidByEnum.changeThenLast:
111
+ requests = changeThenFallback(requests, fee, change, lastPays);
112
+ break;
113
+ case FeePaidByEnum.changeThenAny:
114
+ requests = changeThenFallback(requests, fee, change, anyPays);
115
+ break;
116
+ case FeePaidByEnum.first:
117
+ requests = firstPays(requests, fee);
118
+ break;
119
+ case FeePaidByEnum.last:
120
+ requests = lastPays(requests, fee);
121
+ break;
122
+ case FeePaidByEnum.any:
123
+ requests = anyPays(requests, fee);
124
+ break;
125
+ default:
126
+ throw Error("FeePaidBy option not recognized");
127
+ }
128
+ return requests as Array<SendRequest>;
129
+ } else {
130
+ throw Error("Attempted to specify feePaidBy on zero length SendRequest");
131
+ }
132
+ }
package/src/wallet/Slp.ts CHANGED
@@ -53,6 +53,7 @@ import { GsppProvider } from "../slp/GsppProvider";
53
53
  import { delay } from "../util/delay";
54
54
  import { Util } from "./Util";
55
55
  import { Mainnet } from "../index";
56
+ import { FeePaidByEnum } from "./enum";
56
57
 
57
58
  /**
58
59
  * Class to manage an slp enabled wallet.
@@ -680,6 +681,7 @@ export class Slp {
680
681
  privateKey: this.wallet.privateKey,
681
682
  relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
682
683
  slpOutputs: slpOutputsResult.SlpOutputs,
684
+ feePaidBy: FeePaidByEnum.change,
683
685
  });
684
686
 
685
687
  const bchSpendAmount = slpOutputsResult.BchSendRequests.map(
@@ -689,7 +691,8 @@ export class Slp {
689
691
  let fundingUtxos = await getSuitableUtxos(
690
692
  fundingBchUtxos,
691
693
  BigInt(bchSpendAmount) + BigInt(feeEstimate),
692
- bestHeight
694
+ bestHeight,
695
+ FeePaidByEnum.change
693
696
  );
694
697
 
695
698
  if (fundingUtxos.length === 0) {
@@ -704,6 +707,7 @@ export class Slp {
704
707
  privateKey: this.wallet.privateKey,
705
708
  relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
706
709
  slpOutputs: slpOutputsResult.SlpOutputs,
710
+ feePaidBy: FeePaidByEnum.change,
707
711
  });
708
712
 
709
713
  const encodedTransaction = await buildEncodedTransaction(
package/src/wallet/Wif.ts CHANGED
@@ -31,7 +31,7 @@ import { networkPrefixMap } from "../enum";
31
31
  import { PrivateKeyI, UtxoI } from "../interface";
32
32
 
33
33
  import { BaseWallet } from "./Base";
34
- import { WalletTypeEnum } from "./enum";
34
+ import { FeePaidByEnum, WalletTypeEnum } from "./enum";
35
35
  import {
36
36
  CancelWatchFn,
37
37
  SendRequestOptionsI,
@@ -764,6 +764,13 @@ export class Wallet extends BaseWallet {
764
764
  this._slpSemiAware = true;
765
765
  }
766
766
 
767
+ let feePaidBy;
768
+ if (params.options && params.options.feePaidBy) {
769
+ feePaidBy = params.options.feePaidBy;
770
+ } else {
771
+ feePaidBy = FeePaidByEnum.change;
772
+ }
773
+
767
774
  // get inputs
768
775
  let utxos: UtxoI[];
769
776
  if (params.options && params.options.utxoIds) {
@@ -787,7 +794,12 @@ export class Wallet extends BaseWallet {
787
794
  .fill(0)
788
795
  .map(() => sendRequest);
789
796
 
790
- const fundingUtxos = await getSuitableUtxos(utxos, undefined, bestHeight);
797
+ const fundingUtxos = await getSuitableUtxos(
798
+ utxos,
799
+ undefined,
800
+ bestHeight,
801
+ feePaidBy
802
+ );
791
803
  const relayFeePerByteInSatoshi = await getRelayFeeCache(this.provider!);
792
804
  const fee = await getFeeAmount({
793
805
  utxos: fundingUtxos,
@@ -795,6 +807,7 @@ export class Wallet extends BaseWallet {
795
807
  privateKey: this.privateKey,
796
808
  relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
797
809
  slpOutputs: [],
810
+ feePaidBy: feePaidBy,
798
811
  });
799
812
  const spendableAmount = await sumUtxoValue(fundingUtxos);
800
813
 
@@ -950,6 +963,13 @@ export class Wallet extends BaseWallet {
950
963
  this._slpSemiAware = true;
951
964
  }
952
965
 
966
+ let feePaidBy;
967
+ if (options && options.feePaidBy) {
968
+ feePaidBy = options.feePaidBy;
969
+ } else {
970
+ feePaidBy = FeePaidByEnum.change;
971
+ }
972
+
953
973
  // get inputs from options or query all inputs
954
974
  let utxos: UtxoI[];
955
975
  if (options && options.utxoIds) {
@@ -977,12 +997,14 @@ export class Wallet extends BaseWallet {
977
997
  privateKey: this.privateKey,
978
998
  relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
979
999
  slpOutputs: [],
1000
+ feePaidBy: feePaidBy,
980
1001
  });
981
1002
 
982
1003
  const fundingUtxos = await getSuitableUtxos(
983
1004
  utxos,
984
1005
  BigInt(spendAmount) + BigInt(feeEstimate),
985
- bestHeight
1006
+ bestHeight,
1007
+ feePaidBy
986
1008
  );
987
1009
  if (fundingUtxos.length === 0) {
988
1010
  throw Error(
@@ -995,13 +1017,16 @@ export class Wallet extends BaseWallet {
995
1017
  privateKey: this.privateKey,
996
1018
  relayFeePerByteInSatoshi: relayFeePerByteInSatoshi,
997
1019
  slpOutputs: [],
1020
+ feePaidBy: feePaidBy,
998
1021
  });
999
1022
  const encodedTransaction = await buildEncodedTransaction(
1000
1023
  fundingUtxos,
1001
1024
  sendRequests,
1002
1025
  this.privateKey,
1003
1026
  fee,
1004
- discardChange
1027
+ discardChange,
1028
+ [],
1029
+ feePaidBy
1005
1030
  );
1006
1031
 
1007
1032
  return encodedTransaction;
@@ -5,3 +5,13 @@ export enum WalletTypeEnum {
5
5
  Watch = "watch",
6
6
  PrivateKey = "privkey",
7
7
  }
8
+
9
+ export enum FeePaidByEnum {
10
+ change = "change",
11
+ first = "firstOutput",
12
+ any = "anyOutputs",
13
+ last = "lastOutput",
14
+ changeThenFirst = "changeThenFirst",
15
+ changeThenAny = "changeThenAny",
16
+ changeThenLast = "changeThenLast",
17
+ }