@psf/bch-js 5.4.0 → 6.2.0
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/.on-save.json +8 -0
- package/LICENSE.md +1 -2
- package/package.json +6 -6
- package/src/slp/utils.js +0 -1762
- package/src/transaction.js +1 -498
- package/src/utxo.js +0 -270
- package/test/integration/chains/bchn/utxo-integration.js +0 -107
- package/test/integration/slp.js +0 -470
- package/test/unit/slp-utils.js +0 -2105
- package/test/unit/transaction-unit.js +4 -688
- package/test/unit/utxo-unit.js +0 -131
- package/test/integration/chains/abc/slp.js +0 -226
- package/test/integration/chains/bchn-no-wl-slpdb/rawtransaction.js +0 -71
- package/test/integration/chains/bchn-no-wl-slpdb/slp.js +0 -273
- package/test/integration/chains/bchn-no-wl-slpdb/utxo-integration.js +0 -105
package/test/unit/slp-utils.js
CHANGED
|
@@ -45,216 +45,6 @@ describe('#SLP Utils', () => {
|
|
|
45
45
|
sandbox.restore()
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
describe('#list', () => {
|
|
49
|
-
it('should list single SLP token by id', async () => {
|
|
50
|
-
sandbox
|
|
51
|
-
.stub(uut.Utils.axios, 'get')
|
|
52
|
-
.resolves({ data: mockData.mockToken })
|
|
53
|
-
|
|
54
|
-
const tokenId =
|
|
55
|
-
'4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84'
|
|
56
|
-
|
|
57
|
-
const list = await uut.Utils.list(tokenId)
|
|
58
|
-
// console.log(`list: ${JSON.stringify(list, null, 2)}`)
|
|
59
|
-
|
|
60
|
-
assert.equal(list.id, tokenId)
|
|
61
|
-
assert.property(list, 'decimals')
|
|
62
|
-
assert.property(list, 'symbol')
|
|
63
|
-
assert.property(list, 'documentUri')
|
|
64
|
-
assert.property(list, 'name')
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should list multiple SLP tokens by array of ids', async () => {
|
|
68
|
-
// Mock the call to the REST API
|
|
69
|
-
sandbox
|
|
70
|
-
.stub(uut.Utils.axios, 'post')
|
|
71
|
-
.resolves({ data: mockData.mockList })
|
|
72
|
-
|
|
73
|
-
const tokenIds = [
|
|
74
|
-
'4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84',
|
|
75
|
-
'8fc284dcbc922f7bb7e2a443dc3af792f52923bba403fcf67ca028c88e89da0e'
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
const list = await uut.Utils.list(tokenIds)
|
|
79
|
-
// console.log(`list: ${JSON.stringify(list, null, 2)}`)
|
|
80
|
-
|
|
81
|
-
assert.isArray(list)
|
|
82
|
-
assert.property(list[0], 'symbol')
|
|
83
|
-
assert.property(list[1], 'symbol')
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
describe('#balancesForAddress', () => {
|
|
88
|
-
it('should throw an error if input is not a string or array of strings', async () => {
|
|
89
|
-
try {
|
|
90
|
-
const address = 1234
|
|
91
|
-
|
|
92
|
-
await uut.Utils.balancesForAddress(address)
|
|
93
|
-
|
|
94
|
-
assert.equal(true, false, 'Uh oh. Code path should not end here.')
|
|
95
|
-
} catch (err) {
|
|
96
|
-
// console.log(`Error: `, err)
|
|
97
|
-
assert.include(
|
|
98
|
-
err.message,
|
|
99
|
-
'Input address must be a string or array of strings'
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9', async () => {
|
|
105
|
-
// Mock the call to the REST API
|
|
106
|
-
if (process.env.TEST === 'unit') {
|
|
107
|
-
sandbox
|
|
108
|
-
.stub(uut.Utils.axios, 'get')
|
|
109
|
-
.resolves({ data: mockData.balancesForAddress })
|
|
110
|
-
}
|
|
111
|
-
// sandbox
|
|
112
|
-
// .stub(uut.Utils, "balancesForAddress")
|
|
113
|
-
// .resolves(mockData.balancesForAddress)
|
|
114
|
-
|
|
115
|
-
const balances = await uut.Utils.balancesForAddress(
|
|
116
|
-
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
|
117
|
-
)
|
|
118
|
-
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
|
119
|
-
|
|
120
|
-
assert.isArray(balances)
|
|
121
|
-
assert.hasAllKeys(balances[0], [
|
|
122
|
-
'tokenId',
|
|
123
|
-
'balanceString',
|
|
124
|
-
'balance',
|
|
125
|
-
'decimalCount',
|
|
126
|
-
'slpAddress'
|
|
127
|
-
])
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('should fetch balances for multiple addresses.', async () => {
|
|
131
|
-
const addresses = [
|
|
132
|
-
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
|
133
|
-
'simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57'
|
|
134
|
-
]
|
|
135
|
-
|
|
136
|
-
// Mock the call to the REST API
|
|
137
|
-
if (process.env.TEST === 'unit') {
|
|
138
|
-
sandbox
|
|
139
|
-
.stub(uut.Utils.axios, 'post')
|
|
140
|
-
.resolves({ data: mockData.balancesForAddresses })
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const balances = await uut.Utils.balancesForAddress(addresses)
|
|
144
|
-
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
|
145
|
-
|
|
146
|
-
assert.isArray(balances)
|
|
147
|
-
assert.isArray(balances[0])
|
|
148
|
-
assert.hasAllKeys(balances[0][0], [
|
|
149
|
-
'tokenId',
|
|
150
|
-
'balanceString',
|
|
151
|
-
'balance',
|
|
152
|
-
'decimalCount',
|
|
153
|
-
'slpAddress'
|
|
154
|
-
])
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
describe('#validateTxid', () => {
|
|
159
|
-
it('should validate slp txid', async () => {
|
|
160
|
-
// Mock the call to the REST API
|
|
161
|
-
sandbox
|
|
162
|
-
.stub(uut.Utils.axios, 'post')
|
|
163
|
-
.resolves({ data: mockData.mockIsValidTxid })
|
|
164
|
-
|
|
165
|
-
const isValid = await uut.Utils.validateTxid(
|
|
166
|
-
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
|
|
167
|
-
)
|
|
168
|
-
assert.deepEqual(isValid, [
|
|
169
|
-
{
|
|
170
|
-
txid:
|
|
171
|
-
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
|
172
|
-
valid: true
|
|
173
|
-
}
|
|
174
|
-
])
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
describe('#balancesForToken', () => {
|
|
179
|
-
it('should retrieve token balances for a given tokenId', async () => {
|
|
180
|
-
// Mock the call to the REST API
|
|
181
|
-
sandbox
|
|
182
|
-
.stub(uut.Utils.axios, 'get')
|
|
183
|
-
.resolves({ data: mockData.mockBalancesForToken })
|
|
184
|
-
|
|
185
|
-
const balances = await uut.Utils.balancesForToken(
|
|
186
|
-
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
assert.hasAnyKeys(balances[0], ['tokenBalance', 'slpAddress'])
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
describe('#tokenStats', () => {
|
|
194
|
-
it('should retrieve stats for a given tokenId', async () => {
|
|
195
|
-
// Mock the call to the REST API
|
|
196
|
-
sandbox
|
|
197
|
-
.stub(uut.Utils.axios, 'get')
|
|
198
|
-
.resolves({ data: mockData.mockTokenStats })
|
|
199
|
-
|
|
200
|
-
const tokenStats = await uut.Utils.tokenStats(
|
|
201
|
-
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
assert.hasAnyKeys(tokenStats, [
|
|
205
|
-
'circulatingSupply',
|
|
206
|
-
'decimals',
|
|
207
|
-
'documentUri',
|
|
208
|
-
'name',
|
|
209
|
-
'satoshisLockedUp',
|
|
210
|
-
'symbol',
|
|
211
|
-
'tokenId',
|
|
212
|
-
'totalBurned',
|
|
213
|
-
'totalMinted',
|
|
214
|
-
'txnsSinceGenesis',
|
|
215
|
-
'validAddresses',
|
|
216
|
-
'validUtxos'
|
|
217
|
-
])
|
|
218
|
-
})
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
describe('#transactions', () => {
|
|
222
|
-
it('should retrieve transactions for a given tokenId and address', async () => {
|
|
223
|
-
// Mock the call to the REST API
|
|
224
|
-
sandbox
|
|
225
|
-
.stub(uut.Utils.axios, 'get')
|
|
226
|
-
.resolves({ data: mockData.mockTransactions })
|
|
227
|
-
|
|
228
|
-
const transactions = await uut.Utils.transactions(
|
|
229
|
-
'495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a',
|
|
230
|
-
'simpleledger:qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqnt0wauwu'
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
assert.hasAnyKeys(transactions[0], ['txid', 'tokenDetails'])
|
|
234
|
-
})
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
describe('#burnTotal', () => {
|
|
238
|
-
it('should retrieve input, output and burn totals', async () => {
|
|
239
|
-
// Mock the call to the REST API
|
|
240
|
-
sandbox
|
|
241
|
-
.stub(uut.Utils.axios, 'get')
|
|
242
|
-
.resolves({ data: mockData.mockBurnTotal })
|
|
243
|
-
|
|
244
|
-
const burnTotal = await uut.Utils.burnTotal(
|
|
245
|
-
'c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de'
|
|
246
|
-
)
|
|
247
|
-
// console.log(`burnTotal: ${JSON.stringify(burnTotal, null, 2)}`)
|
|
248
|
-
|
|
249
|
-
assert.hasAnyKeys(burnTotal, [
|
|
250
|
-
'transactionId',
|
|
251
|
-
'inputTotal',
|
|
252
|
-
'outputTotal',
|
|
253
|
-
'burnTotal'
|
|
254
|
-
])
|
|
255
|
-
})
|
|
256
|
-
})
|
|
257
|
-
|
|
258
48
|
describe('#decodeOpReturn', () => {
|
|
259
49
|
it('should throw an error for a non-string input', async () => {
|
|
260
50
|
try {
|
|
@@ -529,1899 +319,4 @@ describe('#SLP Utils', () => {
|
|
|
529
319
|
}
|
|
530
320
|
})
|
|
531
321
|
})
|
|
532
|
-
|
|
533
|
-
describe('#_hydrateUtxo', () => {
|
|
534
|
-
// // This captures an important corner-case. When an SLP token is created, the
|
|
535
|
-
// // change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
|
|
536
|
-
it('should return details on minting baton from genesis transaction', async () => {
|
|
537
|
-
// Mock the call to REST API
|
|
538
|
-
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
539
|
-
|
|
540
|
-
// Stub the calls to decodeOpReturn.
|
|
541
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').resolves({
|
|
542
|
-
tokenType: 1,
|
|
543
|
-
txType: 'GENESIS',
|
|
544
|
-
ticker: 'SLPSDK',
|
|
545
|
-
name: 'SLP SDK example using BITBOX',
|
|
546
|
-
tokenId:
|
|
547
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
548
|
-
documentUri: 'developer.bitcoin.com',
|
|
549
|
-
documentHash: '',
|
|
550
|
-
decimals: 8,
|
|
551
|
-
mintBatonVout: 2,
|
|
552
|
-
qty: '50700000000'
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
const utxos = [
|
|
556
|
-
{
|
|
557
|
-
txid:
|
|
558
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
559
|
-
vout: 3,
|
|
560
|
-
amount: 0.00002015,
|
|
561
|
-
satoshis: 2015,
|
|
562
|
-
height: 594892,
|
|
563
|
-
confirmations: 5
|
|
564
|
-
},
|
|
565
|
-
{
|
|
566
|
-
txid:
|
|
567
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
568
|
-
vout: 2,
|
|
569
|
-
amount: 0.00000546,
|
|
570
|
-
satoshis: 546,
|
|
571
|
-
height: 594892,
|
|
572
|
-
confirmations: 5
|
|
573
|
-
}
|
|
574
|
-
]
|
|
575
|
-
|
|
576
|
-
const data = await uut.Utils._hydrateUtxo(utxos)
|
|
577
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
578
|
-
|
|
579
|
-
// assert.equal(data[0], false, "Change UTXO marked as false.")
|
|
580
|
-
assert.property(data[0], 'txid')
|
|
581
|
-
assert.property(data[0], 'vout')
|
|
582
|
-
assert.property(data[0], 'amount')
|
|
583
|
-
assert.property(data[0], 'satoshis')
|
|
584
|
-
assert.property(data[0], 'height')
|
|
585
|
-
assert.property(data[0], 'confirmations')
|
|
586
|
-
assert.property(data[0], 'isValid')
|
|
587
|
-
assert.equal(data[0].isValid, false)
|
|
588
|
-
|
|
589
|
-
assert.property(data[1], 'txid')
|
|
590
|
-
assert.property(data[1], 'vout')
|
|
591
|
-
assert.property(data[1], 'amount')
|
|
592
|
-
assert.property(data[1], 'satoshis')
|
|
593
|
-
assert.property(data[1], 'height')
|
|
594
|
-
assert.property(data[1], 'confirmations')
|
|
595
|
-
assert.property(data[1], 'utxoType')
|
|
596
|
-
assert.property(data[1], 'tokenId')
|
|
597
|
-
assert.property(data[1], 'tokenTicker')
|
|
598
|
-
assert.property(data[1], 'tokenName')
|
|
599
|
-
assert.property(data[1], 'tokenDocumentUrl')
|
|
600
|
-
assert.property(data[1], 'tokenDocumentHash')
|
|
601
|
-
assert.property(data[1], 'decimals')
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
// 429 means the user is exceeding the rate limits.
|
|
605
|
-
it('should return isValid=null for 429 rate limit error', async () => {
|
|
606
|
-
// Force decodeOpReturn() to throw a 429 error.
|
|
607
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').rejects(mockData.mock429Error)
|
|
608
|
-
|
|
609
|
-
const utxos = [
|
|
610
|
-
{
|
|
611
|
-
txid:
|
|
612
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
613
|
-
vout: 3,
|
|
614
|
-
amount: 0.00002015,
|
|
615
|
-
satoshis: 2015,
|
|
616
|
-
height: 594892,
|
|
617
|
-
confirmations: 5
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
txid:
|
|
621
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
622
|
-
vout: 2,
|
|
623
|
-
amount: 0.00000546,
|
|
624
|
-
satoshis: 546,
|
|
625
|
-
height: 594892,
|
|
626
|
-
confirmations: 5
|
|
627
|
-
}
|
|
628
|
-
]
|
|
629
|
-
|
|
630
|
-
const result = await uut.Utils._hydrateUtxo(utxos)
|
|
631
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
632
|
-
|
|
633
|
-
assert.equal(result[0].isValid, null)
|
|
634
|
-
assert.equal(result[1].isValid, null)
|
|
635
|
-
})
|
|
636
|
-
|
|
637
|
-
// 503 means the server is down or is not responding.
|
|
638
|
-
it('should return isValid=null for 503 rate limit error', async () => {
|
|
639
|
-
// Force decodeOpReturn() to throw a 429 error.
|
|
640
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').rejects(mockData.mock503Error)
|
|
641
|
-
|
|
642
|
-
const utxos = [
|
|
643
|
-
{
|
|
644
|
-
txid:
|
|
645
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
646
|
-
vout: 3,
|
|
647
|
-
amount: 0.00002015,
|
|
648
|
-
satoshis: 2015,
|
|
649
|
-
height: 594892,
|
|
650
|
-
confirmations: 5
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
txid:
|
|
654
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
655
|
-
vout: 2,
|
|
656
|
-
amount: 0.00000546,
|
|
657
|
-
satoshis: 546,
|
|
658
|
-
height: 594892,
|
|
659
|
-
confirmations: 5
|
|
660
|
-
}
|
|
661
|
-
]
|
|
662
|
-
|
|
663
|
-
const result = await uut.Utils._hydrateUtxo(utxos)
|
|
664
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
665
|
-
|
|
666
|
-
assert.equal(result[0].isValid, null)
|
|
667
|
-
assert.equal(result[1].isValid, null)
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
it('should return details for a simple SEND SLP token utxo', async () => {
|
|
671
|
-
// Mock the call to REST API
|
|
672
|
-
// Stub the calls to decodeOpReturn.
|
|
673
|
-
sandbox
|
|
674
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
675
|
-
.onCall(0)
|
|
676
|
-
.resolves({
|
|
677
|
-
tokenType: 1,
|
|
678
|
-
txType: 'SEND',
|
|
679
|
-
tokenId:
|
|
680
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
681
|
-
amounts: ['200000000', '99887500000000']
|
|
682
|
-
})
|
|
683
|
-
.onCall(1)
|
|
684
|
-
.resolves({
|
|
685
|
-
tokenType: 1,
|
|
686
|
-
txType: 'GENESIS',
|
|
687
|
-
ticker: 'TOK-CH',
|
|
688
|
-
name: 'TokyoCash',
|
|
689
|
-
tokenId:
|
|
690
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
691
|
-
documentUri: '',
|
|
692
|
-
documentHash: '',
|
|
693
|
-
decimals: 8,
|
|
694
|
-
mintBatonVout: 0,
|
|
695
|
-
qty: '2100000000000000'
|
|
696
|
-
})
|
|
697
|
-
|
|
698
|
-
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
699
|
-
|
|
700
|
-
const utxos = [
|
|
701
|
-
{
|
|
702
|
-
txid:
|
|
703
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
704
|
-
vout: 1,
|
|
705
|
-
amount: 0.00000546,
|
|
706
|
-
satoshis: 546,
|
|
707
|
-
height: 596089,
|
|
708
|
-
confirmations: 748
|
|
709
|
-
}
|
|
710
|
-
]
|
|
711
|
-
|
|
712
|
-
const data = await uut.Utils._hydrateUtxo(utxos)
|
|
713
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
714
|
-
|
|
715
|
-
assert.property(data[0], 'txid')
|
|
716
|
-
assert.property(data[0], 'vout')
|
|
717
|
-
assert.property(data[0], 'amount')
|
|
718
|
-
assert.property(data[0], 'satoshis')
|
|
719
|
-
assert.property(data[0], 'height')
|
|
720
|
-
assert.property(data[0], 'confirmations')
|
|
721
|
-
assert.property(data[0], 'utxoType')
|
|
722
|
-
assert.property(data[0], 'tokenId')
|
|
723
|
-
assert.property(data[0], 'tokenTicker')
|
|
724
|
-
assert.property(data[0], 'tokenName')
|
|
725
|
-
assert.property(data[0], 'tokenDocumentUrl')
|
|
726
|
-
assert.property(data[0], 'tokenDocumentHash')
|
|
727
|
-
assert.property(data[0], 'decimals')
|
|
728
|
-
assert.property(data[0], 'tokenQty')
|
|
729
|
-
assert.property(data[0], 'isValid')
|
|
730
|
-
assert.equal(data[0].isValid, null)
|
|
731
|
-
})
|
|
732
|
-
|
|
733
|
-
// I don't know if this is the ideal behavior, but this is the behavior
|
|
734
|
-
// that is in production, so I wanted to capture it in a test case. This
|
|
735
|
-
// behavior can always be changed in the future.
|
|
736
|
-
it('should throw error if 429 when querying Genesis transaction', async () => {
|
|
737
|
-
try {
|
|
738
|
-
// Mock the call to REST API
|
|
739
|
-
// Stub the calls to decodeOpReturn.
|
|
740
|
-
sandbox
|
|
741
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
742
|
-
.onCall(0)
|
|
743
|
-
.resolves({
|
|
744
|
-
tokenType: 1,
|
|
745
|
-
txType: 'SEND',
|
|
746
|
-
tokenId:
|
|
747
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
748
|
-
amounts: ['200000000', '99887500000000']
|
|
749
|
-
})
|
|
750
|
-
.onCall(1)
|
|
751
|
-
.rejects(mockData.mock429Error)
|
|
752
|
-
|
|
753
|
-
const utxos = [
|
|
754
|
-
{
|
|
755
|
-
txid:
|
|
756
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
757
|
-
vout: 1,
|
|
758
|
-
amount: 0.00000546,
|
|
759
|
-
satoshis: 546,
|
|
760
|
-
height: 596089,
|
|
761
|
-
confirmations: 748
|
|
762
|
-
}
|
|
763
|
-
]
|
|
764
|
-
|
|
765
|
-
await uut.Utils._hydrateUtxo(utxos)
|
|
766
|
-
|
|
767
|
-
assert.fail('Unexpected result')
|
|
768
|
-
} catch (err) {
|
|
769
|
-
// console.log('error: ', err)
|
|
770
|
-
|
|
771
|
-
assert.property(err, 'message')
|
|
772
|
-
assert.property(err, 'response')
|
|
773
|
-
assert.equal(err.response.status, 429)
|
|
774
|
-
assert.equal(err.response.statusText, 'Too Many Requests')
|
|
775
|
-
assert.include(err.response.data.error, 'Too many requests')
|
|
776
|
-
}
|
|
777
|
-
})
|
|
778
|
-
|
|
779
|
-
it('should add delay if delay is specified', async () => {
|
|
780
|
-
// Mock the call to REST API
|
|
781
|
-
// Stub the calls to decodeOpReturn.
|
|
782
|
-
sandbox
|
|
783
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
784
|
-
.onCall(0)
|
|
785
|
-
.resolves({
|
|
786
|
-
tokenType: 1,
|
|
787
|
-
txType: 'SEND',
|
|
788
|
-
tokenId:
|
|
789
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
790
|
-
amounts: ['200000000', '99887500000000']
|
|
791
|
-
})
|
|
792
|
-
.onCall(1)
|
|
793
|
-
.resolves({
|
|
794
|
-
tokenType: 1,
|
|
795
|
-
txType: 'GENESIS',
|
|
796
|
-
ticker: 'TOK-CH',
|
|
797
|
-
name: 'TokyoCash',
|
|
798
|
-
tokenId:
|
|
799
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
800
|
-
documentUri: '',
|
|
801
|
-
documentHash: '',
|
|
802
|
-
decimals: 8,
|
|
803
|
-
mintBatonVout: 0,
|
|
804
|
-
qty: '2100000000000000'
|
|
805
|
-
})
|
|
806
|
-
|
|
807
|
-
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
808
|
-
|
|
809
|
-
const utxos = [
|
|
810
|
-
{
|
|
811
|
-
txid:
|
|
812
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
813
|
-
vout: 1,
|
|
814
|
-
amount: 0.00000546,
|
|
815
|
-
satoshis: 546,
|
|
816
|
-
height: 596089,
|
|
817
|
-
confirmations: 748
|
|
818
|
-
}
|
|
819
|
-
]
|
|
820
|
-
|
|
821
|
-
const usrObj = {
|
|
822
|
-
utxoDelay: 100
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
await uut.Utils._hydrateUtxo(utxos, usrObj)
|
|
826
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
827
|
-
|
|
828
|
-
// TODO: This test should realy assert that the test took at least 100mS
|
|
829
|
-
// to complete. However, as-is, it exercises the code path, so not
|
|
830
|
-
// throwing an error can be considered a pass.
|
|
831
|
-
assert.equal(true, true)
|
|
832
|
-
})
|
|
833
|
-
})
|
|
834
|
-
|
|
835
|
-
describe('#tokenUtxoDetails', () => {
|
|
836
|
-
it('should throw error if input is not an array.', async () => {
|
|
837
|
-
try {
|
|
838
|
-
await uut.Utils.tokenUtxoDetails('test')
|
|
839
|
-
|
|
840
|
-
assert.equal(true, false, 'Unexpected result.')
|
|
841
|
-
} catch (err) {
|
|
842
|
-
assert.include(
|
|
843
|
-
err.message,
|
|
844
|
-
'Input must be an array',
|
|
845
|
-
'Expected error message.'
|
|
846
|
-
)
|
|
847
|
-
}
|
|
848
|
-
})
|
|
849
|
-
|
|
850
|
-
it('should throw error if utxo does not have txid or tx_hash property.', async () => {
|
|
851
|
-
try {
|
|
852
|
-
const utxos = [
|
|
853
|
-
{
|
|
854
|
-
txid:
|
|
855
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
856
|
-
vout: 3,
|
|
857
|
-
amount: 0.00002015,
|
|
858
|
-
satoshis: 2015,
|
|
859
|
-
height: 594892,
|
|
860
|
-
confirmations: 5
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
vout: 2,
|
|
864
|
-
amount: 0.00000546,
|
|
865
|
-
satoshis: 546,
|
|
866
|
-
height: 594892,
|
|
867
|
-
confirmations: 5
|
|
868
|
-
}
|
|
869
|
-
]
|
|
870
|
-
|
|
871
|
-
await uut.Utils.tokenUtxoDetails(utxos)
|
|
872
|
-
|
|
873
|
-
assert.equal(true, false, 'Unexpected result.')
|
|
874
|
-
} catch (err) {
|
|
875
|
-
assert.include(
|
|
876
|
-
err.message,
|
|
877
|
-
'utxo 1 does not have a txid or tx_hash property',
|
|
878
|
-
'Expected error message.'
|
|
879
|
-
)
|
|
880
|
-
}
|
|
881
|
-
})
|
|
882
|
-
|
|
883
|
-
// // This captures an important corner-case. When an SLP token is created, the
|
|
884
|
-
// // change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
|
|
885
|
-
it('should return details on minting baton from genesis transaction', async () => {
|
|
886
|
-
// Mock the call to REST API
|
|
887
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
888
|
-
|
|
889
|
-
// Stub the calls to decodeOpReturn.
|
|
890
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').resolves({
|
|
891
|
-
tokenType: 1,
|
|
892
|
-
txType: 'GENESIS',
|
|
893
|
-
ticker: 'SLPSDK',
|
|
894
|
-
name: 'SLP SDK example using BITBOX',
|
|
895
|
-
tokenId:
|
|
896
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
897
|
-
documentUri: 'developer.bitcoin.com',
|
|
898
|
-
documentHash: '',
|
|
899
|
-
decimals: 8,
|
|
900
|
-
mintBatonVout: 2,
|
|
901
|
-
qty: '50700000000'
|
|
902
|
-
})
|
|
903
|
-
|
|
904
|
-
const utxos = [
|
|
905
|
-
{
|
|
906
|
-
txid:
|
|
907
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
908
|
-
vout: 3,
|
|
909
|
-
amount: 0.00002015,
|
|
910
|
-
satoshis: 2015,
|
|
911
|
-
height: 594892,
|
|
912
|
-
confirmations: 5
|
|
913
|
-
},
|
|
914
|
-
{
|
|
915
|
-
txid:
|
|
916
|
-
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
|
|
917
|
-
vout: 2,
|
|
918
|
-
amount: 0.00000546,
|
|
919
|
-
satoshis: 546,
|
|
920
|
-
height: 594892,
|
|
921
|
-
confirmations: 5
|
|
922
|
-
}
|
|
923
|
-
]
|
|
924
|
-
|
|
925
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
926
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
927
|
-
|
|
928
|
-
// assert.equal(data[0], false, "Change UTXO marked as false.")
|
|
929
|
-
assert.property(data[0], 'txid')
|
|
930
|
-
assert.property(data[0], 'vout')
|
|
931
|
-
assert.property(data[0], 'amount')
|
|
932
|
-
assert.property(data[0], 'satoshis')
|
|
933
|
-
assert.property(data[0], 'height')
|
|
934
|
-
assert.property(data[0], 'confirmations')
|
|
935
|
-
assert.property(data[0], 'isValid')
|
|
936
|
-
assert.equal(data[0].isValid, false)
|
|
937
|
-
|
|
938
|
-
assert.property(data[1], 'txid')
|
|
939
|
-
assert.property(data[1], 'vout')
|
|
940
|
-
assert.property(data[1], 'amount')
|
|
941
|
-
assert.property(data[1], 'satoshis')
|
|
942
|
-
assert.property(data[1], 'height')
|
|
943
|
-
assert.property(data[1], 'confirmations')
|
|
944
|
-
assert.property(data[1], 'utxoType')
|
|
945
|
-
assert.property(data[1], 'tokenId')
|
|
946
|
-
assert.property(data[1], 'tokenTicker')
|
|
947
|
-
assert.property(data[1], 'tokenName')
|
|
948
|
-
assert.property(data[1], 'tokenDocumentUrl')
|
|
949
|
-
assert.property(data[1], 'tokenDocumentHash')
|
|
950
|
-
assert.property(data[1], 'decimals')
|
|
951
|
-
assert.property(data[1], 'isValid')
|
|
952
|
-
assert.equal(data[1].isValid, true)
|
|
953
|
-
})
|
|
954
|
-
|
|
955
|
-
it('should return details for a MINT token utxo', async () => {
|
|
956
|
-
// Mock the call to REST API
|
|
957
|
-
|
|
958
|
-
// Stub the calls to decodeOpReturn.
|
|
959
|
-
sandbox
|
|
960
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
961
|
-
.onCall(0)
|
|
962
|
-
.resolves({
|
|
963
|
-
tokenType: 1,
|
|
964
|
-
txType: 'MINT',
|
|
965
|
-
tokenId:
|
|
966
|
-
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
|
967
|
-
mintBatonVout: 2,
|
|
968
|
-
qty: '1000000000000'
|
|
969
|
-
})
|
|
970
|
-
.onCall(1)
|
|
971
|
-
.resolves({
|
|
972
|
-
tokenType: 1,
|
|
973
|
-
txType: 'GENESIS',
|
|
974
|
-
ticker: 'PSF',
|
|
975
|
-
name: 'Permissionless Software Foundation',
|
|
976
|
-
tokenId:
|
|
977
|
-
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
|
978
|
-
documentUri: 'psfoundation.cash',
|
|
979
|
-
documentHash: '',
|
|
980
|
-
decimals: 8,
|
|
981
|
-
mintBatonVout: 2,
|
|
982
|
-
qty: '1988209163133'
|
|
983
|
-
})
|
|
984
|
-
|
|
985
|
-
// Stub the call to validateTxid
|
|
986
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
987
|
-
|
|
988
|
-
const utxos = [
|
|
989
|
-
{
|
|
990
|
-
txid:
|
|
991
|
-
'cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76',
|
|
992
|
-
vout: 1,
|
|
993
|
-
amount: 0.00000546,
|
|
994
|
-
satoshis: 546,
|
|
995
|
-
height: 600297,
|
|
996
|
-
confirmations: 76
|
|
997
|
-
}
|
|
998
|
-
]
|
|
999
|
-
|
|
1000
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1001
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1002
|
-
|
|
1003
|
-
assert.property(data[0], 'txid')
|
|
1004
|
-
assert.property(data[0], 'vout')
|
|
1005
|
-
assert.property(data[0], 'amount')
|
|
1006
|
-
assert.property(data[0], 'satoshis')
|
|
1007
|
-
assert.property(data[0], 'height')
|
|
1008
|
-
assert.property(data[0], 'confirmations')
|
|
1009
|
-
assert.property(data[0], 'utxoType')
|
|
1010
|
-
assert.property(data[0], 'transactionType')
|
|
1011
|
-
assert.property(data[0], 'tokenId')
|
|
1012
|
-
assert.property(data[0], 'tokenTicker')
|
|
1013
|
-
assert.property(data[0], 'tokenName')
|
|
1014
|
-
assert.property(data[0], 'tokenDocumentUrl')
|
|
1015
|
-
assert.property(data[0], 'tokenDocumentHash')
|
|
1016
|
-
assert.property(data[0], 'decimals')
|
|
1017
|
-
assert.property(data[0], 'mintBatonVout')
|
|
1018
|
-
assert.property(data[0], 'tokenQty')
|
|
1019
|
-
assert.property(data[0], 'isValid')
|
|
1020
|
-
assert.equal(data[0].isValid, true)
|
|
1021
|
-
})
|
|
1022
|
-
|
|
1023
|
-
it('should return details for a simple SEND SLP token utxo', async () => {
|
|
1024
|
-
// Mock the call to REST API
|
|
1025
|
-
// Stub the calls to decodeOpReturn.
|
|
1026
|
-
sandbox
|
|
1027
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1028
|
-
.onCall(0)
|
|
1029
|
-
.resolves({
|
|
1030
|
-
tokenType: 1,
|
|
1031
|
-
txType: 'SEND',
|
|
1032
|
-
tokenId:
|
|
1033
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
1034
|
-
amounts: ['200000000', '99887500000000']
|
|
1035
|
-
})
|
|
1036
|
-
.onCall(1)
|
|
1037
|
-
.resolves({
|
|
1038
|
-
tokenType: 1,
|
|
1039
|
-
txType: 'GENESIS',
|
|
1040
|
-
ticker: 'TOK-CH',
|
|
1041
|
-
name: 'TokyoCash',
|
|
1042
|
-
tokenId:
|
|
1043
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
1044
|
-
documentUri: '',
|
|
1045
|
-
documentHash: '',
|
|
1046
|
-
decimals: 8,
|
|
1047
|
-
mintBatonVout: 0,
|
|
1048
|
-
qty: '2100000000000000'
|
|
1049
|
-
})
|
|
1050
|
-
|
|
1051
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1052
|
-
|
|
1053
|
-
const utxos = [
|
|
1054
|
-
{
|
|
1055
|
-
txid:
|
|
1056
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
1057
|
-
vout: 1,
|
|
1058
|
-
amount: 0.00000546,
|
|
1059
|
-
satoshis: 546,
|
|
1060
|
-
height: 596089,
|
|
1061
|
-
confirmations: 748
|
|
1062
|
-
}
|
|
1063
|
-
]
|
|
1064
|
-
|
|
1065
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1066
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1067
|
-
|
|
1068
|
-
assert.property(data[0], 'txid')
|
|
1069
|
-
assert.property(data[0], 'vout')
|
|
1070
|
-
assert.property(data[0], 'amount')
|
|
1071
|
-
assert.property(data[0], 'satoshis')
|
|
1072
|
-
assert.property(data[0], 'height')
|
|
1073
|
-
assert.property(data[0], 'confirmations')
|
|
1074
|
-
assert.property(data[0], 'utxoType')
|
|
1075
|
-
assert.property(data[0], 'tokenId')
|
|
1076
|
-
assert.property(data[0], 'tokenTicker')
|
|
1077
|
-
assert.property(data[0], 'tokenName')
|
|
1078
|
-
assert.property(data[0], 'tokenDocumentUrl')
|
|
1079
|
-
assert.property(data[0], 'tokenDocumentHash')
|
|
1080
|
-
assert.property(data[0], 'decimals')
|
|
1081
|
-
assert.property(data[0], 'tokenQty')
|
|
1082
|
-
assert.property(data[0], 'isValid')
|
|
1083
|
-
assert.equal(data[0].isValid, true)
|
|
1084
|
-
})
|
|
1085
|
-
|
|
1086
|
-
it('should handle BCH and SLP utxos in the same TX', async () => {
|
|
1087
|
-
// Mock external dependencies.
|
|
1088
|
-
// sandbox
|
|
1089
|
-
// .stub(uut.Utils, 'validateTxid')
|
|
1090
|
-
// .resolves(mockData.mockDualValidation)
|
|
1091
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1092
|
-
|
|
1093
|
-
sandbox
|
|
1094
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1095
|
-
.onCall(0)
|
|
1096
|
-
.resolves({
|
|
1097
|
-
tokenType: 1,
|
|
1098
|
-
txType: 'SEND',
|
|
1099
|
-
tokenId:
|
|
1100
|
-
'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
|
|
1101
|
-
amounts: ['1', '5']
|
|
1102
|
-
})
|
|
1103
|
-
.onCall(1)
|
|
1104
|
-
.resolves({
|
|
1105
|
-
tokenType: 1,
|
|
1106
|
-
txType: 'SEND',
|
|
1107
|
-
tokenId:
|
|
1108
|
-
'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
|
|
1109
|
-
amounts: ['1', '5']
|
|
1110
|
-
})
|
|
1111
|
-
.onCall(2)
|
|
1112
|
-
.resolves({
|
|
1113
|
-
tokenType: 1,
|
|
1114
|
-
txType: 'GENESIS',
|
|
1115
|
-
ticker: 'TAP',
|
|
1116
|
-
name: 'Thoughts and Prayers',
|
|
1117
|
-
tokenId:
|
|
1118
|
-
'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d',
|
|
1119
|
-
documentUri: '',
|
|
1120
|
-
documentHash: '',
|
|
1121
|
-
decimals: 0,
|
|
1122
|
-
mintBatonVout: 2,
|
|
1123
|
-
qty: '1000000'
|
|
1124
|
-
})
|
|
1125
|
-
|
|
1126
|
-
const utxos = [
|
|
1127
|
-
{
|
|
1128
|
-
txid:
|
|
1129
|
-
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
|
1130
|
-
vout: 3,
|
|
1131
|
-
value: '6816',
|
|
1132
|
-
height: 606848,
|
|
1133
|
-
confirmations: 13,
|
|
1134
|
-
satoshis: 6816
|
|
1135
|
-
},
|
|
1136
|
-
{
|
|
1137
|
-
txid:
|
|
1138
|
-
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
|
|
1139
|
-
vout: 2,
|
|
1140
|
-
value: '546',
|
|
1141
|
-
height: 606848,
|
|
1142
|
-
confirmations: 13,
|
|
1143
|
-
satoshis: 546
|
|
1144
|
-
}
|
|
1145
|
-
]
|
|
1146
|
-
|
|
1147
|
-
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1148
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
1149
|
-
|
|
1150
|
-
assert.isArray(result)
|
|
1151
|
-
assert.equal(result.length, 2)
|
|
1152
|
-
|
|
1153
|
-
assert.property(result[0], 'txid')
|
|
1154
|
-
assert.property(result[0], 'vout')
|
|
1155
|
-
assert.property(result[0], 'value')
|
|
1156
|
-
assert.property(result[0], 'satoshis')
|
|
1157
|
-
assert.property(result[0], 'height')
|
|
1158
|
-
assert.property(result[0], 'confirmations')
|
|
1159
|
-
assert.property(result[0], 'isValid')
|
|
1160
|
-
assert.equal(result[0].isValid, false)
|
|
1161
|
-
|
|
1162
|
-
assert.equal(result[1].isValid, true)
|
|
1163
|
-
assert.equal(result[1].utxoType, 'token')
|
|
1164
|
-
assert.equal(result[1].transactionType, 'send')
|
|
1165
|
-
})
|
|
1166
|
-
|
|
1167
|
-
it('should handle problematic utxos', async () => {
|
|
1168
|
-
// Mock external dependencies.
|
|
1169
|
-
// Stub the calls to decodeOpReturn.
|
|
1170
|
-
sandbox
|
|
1171
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1172
|
-
.onCall(0)
|
|
1173
|
-
.throws({ message: 'scriptpubkey not op_return' })
|
|
1174
|
-
.onCall(1)
|
|
1175
|
-
.resolves({
|
|
1176
|
-
tokenType: 1,
|
|
1177
|
-
txType: 'SEND',
|
|
1178
|
-
tokenId:
|
|
1179
|
-
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
1180
|
-
amounts: ['5000000', '395010942']
|
|
1181
|
-
})
|
|
1182
|
-
.onCall(2)
|
|
1183
|
-
.resolves({
|
|
1184
|
-
tokenType: 1,
|
|
1185
|
-
txType: 'GENESIS',
|
|
1186
|
-
ticker: 'AUDC',
|
|
1187
|
-
name: 'AUD Coin',
|
|
1188
|
-
tokenId:
|
|
1189
|
-
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
1190
|
-
documentUri: 'audcoino@gmail.com',
|
|
1191
|
-
documentHash: '',
|
|
1192
|
-
decimals: 6,
|
|
1193
|
-
mintBatonVout: 0,
|
|
1194
|
-
qty: '2000000000000000000'
|
|
1195
|
-
})
|
|
1196
|
-
|
|
1197
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1198
|
-
|
|
1199
|
-
const utxos = [
|
|
1200
|
-
{
|
|
1201
|
-
txid:
|
|
1202
|
-
'0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959',
|
|
1203
|
-
vout: 0,
|
|
1204
|
-
amount: 0.00018362,
|
|
1205
|
-
satoshis: 18362,
|
|
1206
|
-
height: 613483,
|
|
1207
|
-
confirmations: 124
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
txid:
|
|
1211
|
-
'67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19',
|
|
1212
|
-
vout: 1,
|
|
1213
|
-
amount: 0.00000546,
|
|
1214
|
-
satoshis: 546,
|
|
1215
|
-
height: 612075,
|
|
1216
|
-
confirmations: 1532
|
|
1217
|
-
}
|
|
1218
|
-
]
|
|
1219
|
-
|
|
1220
|
-
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1221
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
1222
|
-
|
|
1223
|
-
assert.isArray(result)
|
|
1224
|
-
assert.equal(result.length, 2)
|
|
1225
|
-
|
|
1226
|
-
assert.property(result[0], 'txid')
|
|
1227
|
-
assert.property(result[0], 'vout')
|
|
1228
|
-
assert.property(result[0], 'amount')
|
|
1229
|
-
assert.property(result[0], 'satoshis')
|
|
1230
|
-
assert.property(result[0], 'height')
|
|
1231
|
-
assert.property(result[0], 'confirmations')
|
|
1232
|
-
assert.property(result[0], 'isValid')
|
|
1233
|
-
assert.equal(result[0].isValid, false)
|
|
1234
|
-
|
|
1235
|
-
assert.equal(result[1].isValid, true)
|
|
1236
|
-
assert.equal(result[1].utxoType, 'token')
|
|
1237
|
-
assert.equal(result[1].transactionType, 'send')
|
|
1238
|
-
})
|
|
1239
|
-
|
|
1240
|
-
it('should return isValid=false for BCH-only UTXOs', async () => {
|
|
1241
|
-
// Mock live network calls
|
|
1242
|
-
|
|
1243
|
-
sandbox
|
|
1244
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1245
|
-
.throws(new Error('scriptpubkey not op_return'))
|
|
1246
|
-
|
|
1247
|
-
const utxos = [
|
|
1248
|
-
{
|
|
1249
|
-
txid:
|
|
1250
|
-
'a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b',
|
|
1251
|
-
vout: 0,
|
|
1252
|
-
amount: 0.00001,
|
|
1253
|
-
satoshis: 1000,
|
|
1254
|
-
confirmations: 0,
|
|
1255
|
-
ts: 1578424704
|
|
1256
|
-
},
|
|
1257
|
-
{
|
|
1258
|
-
txid:
|
|
1259
|
-
'53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44',
|
|
1260
|
-
vout: 0,
|
|
1261
|
-
amount: 0.00001,
|
|
1262
|
-
satoshis: 1000,
|
|
1263
|
-
confirmations: 0,
|
|
1264
|
-
ts: 1578424634
|
|
1265
|
-
}
|
|
1266
|
-
]
|
|
1267
|
-
|
|
1268
|
-
const result = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1269
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
1270
|
-
|
|
1271
|
-
assert.isArray(result)
|
|
1272
|
-
|
|
1273
|
-
assert.property(result[0], 'txid')
|
|
1274
|
-
assert.property(result[0], 'vout')
|
|
1275
|
-
assert.property(result[0], 'amount')
|
|
1276
|
-
assert.property(result[0], 'satoshis')
|
|
1277
|
-
assert.property(result[0], 'confirmations')
|
|
1278
|
-
assert.property(result[0], 'isValid')
|
|
1279
|
-
assert.equal(result[0].isValid, false)
|
|
1280
|
-
|
|
1281
|
-
assert.property(result[1], 'txid')
|
|
1282
|
-
assert.property(result[1], 'vout')
|
|
1283
|
-
assert.property(result[1], 'amount')
|
|
1284
|
-
assert.property(result[1], 'satoshis')
|
|
1285
|
-
assert.property(result[1], 'confirmations')
|
|
1286
|
-
assert.property(result[1], 'isValid')
|
|
1287
|
-
assert.equal(result[1].isValid, false)
|
|
1288
|
-
})
|
|
1289
|
-
|
|
1290
|
-
it('should decode a Genesis transaction', async () => {
|
|
1291
|
-
const slpData = {
|
|
1292
|
-
tokenType: 1,
|
|
1293
|
-
txType: 'GENESIS',
|
|
1294
|
-
ticker: 'SLPTEST',
|
|
1295
|
-
name: 'SLP Test Token',
|
|
1296
|
-
tokenId:
|
|
1297
|
-
'd2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954',
|
|
1298
|
-
documentUri: 'https://FullStack.cash',
|
|
1299
|
-
documentHash: '',
|
|
1300
|
-
decimals: 8,
|
|
1301
|
-
mintBatonVout: 2,
|
|
1302
|
-
qty: '10000000000'
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// Mock external dependencies.
|
|
1306
|
-
// Stub the calls to decodeOpReturn.
|
|
1307
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').resolves(slpData)
|
|
1308
|
-
|
|
1309
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1310
|
-
|
|
1311
|
-
const utxos = [
|
|
1312
|
-
{
|
|
1313
|
-
txid:
|
|
1314
|
-
'd2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954',
|
|
1315
|
-
vout: 1,
|
|
1316
|
-
value: '546',
|
|
1317
|
-
confirmations: 0,
|
|
1318
|
-
satoshis: 546
|
|
1319
|
-
},
|
|
1320
|
-
{
|
|
1321
|
-
txid:
|
|
1322
|
-
'd2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954',
|
|
1323
|
-
vout: 2,
|
|
1324
|
-
value: '546',
|
|
1325
|
-
confirmations: 0,
|
|
1326
|
-
satoshis: 546
|
|
1327
|
-
},
|
|
1328
|
-
{
|
|
1329
|
-
txid:
|
|
1330
|
-
'd2ec6abff5d1c8ed9ab5db6d140dcaebb813463e42933a4a4db171e7222a0954',
|
|
1331
|
-
vout: 3,
|
|
1332
|
-
value: '12178',
|
|
1333
|
-
confirmations: 0,
|
|
1334
|
-
satoshis: 12178
|
|
1335
|
-
}
|
|
1336
|
-
]
|
|
1337
|
-
|
|
1338
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1339
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1340
|
-
|
|
1341
|
-
assert.isArray(data)
|
|
1342
|
-
|
|
1343
|
-
assert.equal(data[0].utxoType, 'token')
|
|
1344
|
-
assert.equal(data[0].tokenQty, 100)
|
|
1345
|
-
assert.equal(data[0].isValid, true)
|
|
1346
|
-
assert.equal(data[0].tokenType, 1)
|
|
1347
|
-
|
|
1348
|
-
assert.equal(data[1].utxoType, 'minting-baton')
|
|
1349
|
-
assert.equal(data[1].isValid, true)
|
|
1350
|
-
assert.equal(data[1].tokenType, 1)
|
|
1351
|
-
|
|
1352
|
-
assert.equal(data[2].isValid, false)
|
|
1353
|
-
})
|
|
1354
|
-
|
|
1355
|
-
it('should decode a Mint transaction', async () => {
|
|
1356
|
-
// Define stubbed data.
|
|
1357
|
-
const slpData = {
|
|
1358
|
-
tokenType: 1,
|
|
1359
|
-
txType: 'MINT',
|
|
1360
|
-
tokenId:
|
|
1361
|
-
'9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136',
|
|
1362
|
-
mintBatonVout: 2,
|
|
1363
|
-
qty: '10000000000'
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
const genesisData = {
|
|
1367
|
-
tokenType: 1,
|
|
1368
|
-
txType: 'GENESIS',
|
|
1369
|
-
ticker: 'SLPTEST',
|
|
1370
|
-
name: 'SLP Test Token',
|
|
1371
|
-
tokenId:
|
|
1372
|
-
'9d35c1803ed3ab8bd23c198b027f7b3b530586494dc265de6391b74a6b090136',
|
|
1373
|
-
documentUri: 'https://FullStack.cash',
|
|
1374
|
-
documentHash: '',
|
|
1375
|
-
decimals: 8,
|
|
1376
|
-
mintBatonVout: 2,
|
|
1377
|
-
qty: '10000000000'
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
// Mock external dependencies.
|
|
1381
|
-
// Stub the calls to decodeOpReturn.
|
|
1382
|
-
sandbox
|
|
1383
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1384
|
-
.resolves(slpData)
|
|
1385
|
-
.onCall(1)
|
|
1386
|
-
.resolves(genesisData)
|
|
1387
|
-
.onCall(2)
|
|
1388
|
-
.resolves(slpData)
|
|
1389
|
-
.onCall(3)
|
|
1390
|
-
.resolves(genesisData)
|
|
1391
|
-
.onCall(4)
|
|
1392
|
-
.resolves(slpData)
|
|
1393
|
-
|
|
1394
|
-
// Stub the call to validateTxid
|
|
1395
|
-
// sandbox
|
|
1396
|
-
// .stub(uut.Utils, 'validateTxid')
|
|
1397
|
-
// .resolves(stubValid)
|
|
1398
|
-
// .onCall(1)
|
|
1399
|
-
// .resolves(stubValid)
|
|
1400
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1401
|
-
|
|
1402
|
-
const utxos = [
|
|
1403
|
-
{
|
|
1404
|
-
txid:
|
|
1405
|
-
'880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04',
|
|
1406
|
-
vout: 1,
|
|
1407
|
-
value: '546',
|
|
1408
|
-
confirmations: 0,
|
|
1409
|
-
satoshis: 546
|
|
1410
|
-
},
|
|
1411
|
-
{
|
|
1412
|
-
txid:
|
|
1413
|
-
'880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04',
|
|
1414
|
-
vout: 2,
|
|
1415
|
-
value: '546',
|
|
1416
|
-
confirmations: 0,
|
|
1417
|
-
satoshis: 546
|
|
1418
|
-
},
|
|
1419
|
-
{
|
|
1420
|
-
txid:
|
|
1421
|
-
'880587f01e3112e779c0fdf1b9b859c242a28e56ead85483eeedcaa52f051a04',
|
|
1422
|
-
vout: 3,
|
|
1423
|
-
value: '10552',
|
|
1424
|
-
confirmations: 0,
|
|
1425
|
-
satoshis: 10552
|
|
1426
|
-
}
|
|
1427
|
-
]
|
|
1428
|
-
|
|
1429
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1430
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1431
|
-
|
|
1432
|
-
assert.isArray(data)
|
|
1433
|
-
|
|
1434
|
-
assert.equal(data[0].utxoType, 'token')
|
|
1435
|
-
assert.equal(data[0].tokenQty, 100)
|
|
1436
|
-
assert.equal(data[0].isValid, true)
|
|
1437
|
-
assert.equal(data[0].tokenType, 1)
|
|
1438
|
-
|
|
1439
|
-
assert.equal(data[1].utxoType, 'minting-baton')
|
|
1440
|
-
assert.equal(data[1].isValid, true)
|
|
1441
|
-
assert.equal(data[1].tokenType, 1)
|
|
1442
|
-
|
|
1443
|
-
assert.equal(data[2].isValid, false)
|
|
1444
|
-
})
|
|
1445
|
-
|
|
1446
|
-
it('should decode a NFT Group Genesis transaction', async () => {
|
|
1447
|
-
// Define stubbed data.
|
|
1448
|
-
const slpData = {
|
|
1449
|
-
tokenType: 129,
|
|
1450
|
-
txType: 'GENESIS',
|
|
1451
|
-
ticker: 'NFTTT',
|
|
1452
|
-
name: 'NFT Test Token',
|
|
1453
|
-
tokenId:
|
|
1454
|
-
'4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4',
|
|
1455
|
-
documentUri: 'https://FullStack.cash',
|
|
1456
|
-
documentHash: '',
|
|
1457
|
-
decimals: 0,
|
|
1458
|
-
mintBatonVout: 2,
|
|
1459
|
-
qty: '1'
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// Mock external dependencies.
|
|
1463
|
-
// Stub the calls to decodeOpReturn.
|
|
1464
|
-
sandbox
|
|
1465
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1466
|
-
.resolves(slpData)
|
|
1467
|
-
.onCall(1)
|
|
1468
|
-
.resolves(slpData)
|
|
1469
|
-
.onCall(2)
|
|
1470
|
-
.resolves(slpData)
|
|
1471
|
-
.onCall(3)
|
|
1472
|
-
.resolves(slpData)
|
|
1473
|
-
.onCall(4)
|
|
1474
|
-
.resolves(slpData)
|
|
1475
|
-
|
|
1476
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1477
|
-
|
|
1478
|
-
const utxos = [
|
|
1479
|
-
{
|
|
1480
|
-
txid:
|
|
1481
|
-
'4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4',
|
|
1482
|
-
vout: 3,
|
|
1483
|
-
value: '15620',
|
|
1484
|
-
height: 638207,
|
|
1485
|
-
confirmations: 3,
|
|
1486
|
-
satoshis: 15620
|
|
1487
|
-
},
|
|
1488
|
-
{
|
|
1489
|
-
txid:
|
|
1490
|
-
'4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4',
|
|
1491
|
-
vout: 2,
|
|
1492
|
-
value: '546',
|
|
1493
|
-
height: 638207,
|
|
1494
|
-
confirmations: 3,
|
|
1495
|
-
satoshis: 546
|
|
1496
|
-
},
|
|
1497
|
-
{
|
|
1498
|
-
txid:
|
|
1499
|
-
'4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4',
|
|
1500
|
-
vout: 1,
|
|
1501
|
-
value: '546',
|
|
1502
|
-
height: 638207,
|
|
1503
|
-
confirmations: 3,
|
|
1504
|
-
satoshis: 546
|
|
1505
|
-
}
|
|
1506
|
-
]
|
|
1507
|
-
|
|
1508
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1509
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1510
|
-
|
|
1511
|
-
assert.isArray(data)
|
|
1512
|
-
|
|
1513
|
-
assert.equal(data[0].isValid, false)
|
|
1514
|
-
|
|
1515
|
-
assert.equal(data[1].utxoType, 'minting-baton')
|
|
1516
|
-
assert.equal(data[1].isValid, true)
|
|
1517
|
-
assert.equal(data[1].tokenType, 129)
|
|
1518
|
-
|
|
1519
|
-
assert.equal(data[2].utxoType, 'token')
|
|
1520
|
-
assert.equal(data[2].tokenType, 129)
|
|
1521
|
-
assert.equal(data[1].isValid, true)
|
|
1522
|
-
})
|
|
1523
|
-
|
|
1524
|
-
it('should decode a NFT Group Mint transaction', async () => {
|
|
1525
|
-
// Define stubbed data.
|
|
1526
|
-
const slpData = {
|
|
1527
|
-
tokenType: 129,
|
|
1528
|
-
txType: 'MINT',
|
|
1529
|
-
tokenId:
|
|
1530
|
-
'eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981',
|
|
1531
|
-
mintBatonVout: 2,
|
|
1532
|
-
qty: '10'
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
const genesisData = {
|
|
1536
|
-
tokenType: 129,
|
|
1537
|
-
txType: 'GENESIS',
|
|
1538
|
-
ticker: 'NFTTT',
|
|
1539
|
-
name: 'NFT Test Token',
|
|
1540
|
-
tokenId:
|
|
1541
|
-
'eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981',
|
|
1542
|
-
documentUri: 'https://FullStack.cash',
|
|
1543
|
-
documentHash: '',
|
|
1544
|
-
decimals: 0,
|
|
1545
|
-
mintBatonVout: 2,
|
|
1546
|
-
qty: '1'
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
// Mock external dependencies.
|
|
1550
|
-
// Stub the calls to decodeOpReturn.
|
|
1551
|
-
sandbox
|
|
1552
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1553
|
-
.resolves(slpData)
|
|
1554
|
-
.onCall(1)
|
|
1555
|
-
.resolves(slpData)
|
|
1556
|
-
.onCall(2)
|
|
1557
|
-
.resolves(genesisData)
|
|
1558
|
-
.onCall(3)
|
|
1559
|
-
.resolves(slpData)
|
|
1560
|
-
.onCall(4)
|
|
1561
|
-
.resolves(genesisData)
|
|
1562
|
-
|
|
1563
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1564
|
-
|
|
1565
|
-
const utxos = [
|
|
1566
|
-
{
|
|
1567
|
-
txid:
|
|
1568
|
-
'35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919',
|
|
1569
|
-
vout: 3,
|
|
1570
|
-
value: '15620',
|
|
1571
|
-
height: 638207,
|
|
1572
|
-
confirmations: 3,
|
|
1573
|
-
satoshis: 15620
|
|
1574
|
-
},
|
|
1575
|
-
{
|
|
1576
|
-
txid:
|
|
1577
|
-
'35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919',
|
|
1578
|
-
vout: 2,
|
|
1579
|
-
value: '546',
|
|
1580
|
-
height: 638207,
|
|
1581
|
-
confirmations: 3,
|
|
1582
|
-
satoshis: 546
|
|
1583
|
-
},
|
|
1584
|
-
{
|
|
1585
|
-
txid:
|
|
1586
|
-
'35846676e7514658bbd2fd60b1f0d4d86195908f6b2de5328d54c8e4a2d05919',
|
|
1587
|
-
vout: 1,
|
|
1588
|
-
value: '546',
|
|
1589
|
-
height: 638207,
|
|
1590
|
-
confirmations: 3,
|
|
1591
|
-
satoshis: 546
|
|
1592
|
-
}
|
|
1593
|
-
]
|
|
1594
|
-
|
|
1595
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1596
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1597
|
-
|
|
1598
|
-
assert.isArray(data)
|
|
1599
|
-
|
|
1600
|
-
assert.equal(data[0].isValid, false)
|
|
1601
|
-
|
|
1602
|
-
assert.equal(data[1].utxoType, 'minting-baton')
|
|
1603
|
-
assert.equal(data[1].tokenType, 129)
|
|
1604
|
-
assert.equal(data[1].isValid, true)
|
|
1605
|
-
|
|
1606
|
-
assert.equal(data[2].utxoType, 'token')
|
|
1607
|
-
assert.equal(data[2].tokenType, 129)
|
|
1608
|
-
assert.equal(data[2].isValid, true)
|
|
1609
|
-
})
|
|
1610
|
-
|
|
1611
|
-
it('should decode a NFT Child Genesis transaction', async () => {
|
|
1612
|
-
// Define stubbed data.
|
|
1613
|
-
const slpData = {
|
|
1614
|
-
tokenType: 65,
|
|
1615
|
-
txType: 'GENESIS',
|
|
1616
|
-
ticker: 'NFTC',
|
|
1617
|
-
name: 'NFT Child',
|
|
1618
|
-
tokenId:
|
|
1619
|
-
'9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683',
|
|
1620
|
-
documentUri: 'https://FullStack.cash',
|
|
1621
|
-
documentHash: '',
|
|
1622
|
-
decimals: 0,
|
|
1623
|
-
mintBatonVout: 0,
|
|
1624
|
-
qty: '1'
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
// Mock external dependencies.
|
|
1628
|
-
// Stub the calls to decodeOpReturn.
|
|
1629
|
-
sandbox
|
|
1630
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1631
|
-
.resolves(slpData)
|
|
1632
|
-
.onCall(1)
|
|
1633
|
-
.resolves(slpData)
|
|
1634
|
-
|
|
1635
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1636
|
-
|
|
1637
|
-
const utxos = [
|
|
1638
|
-
{
|
|
1639
|
-
txid:
|
|
1640
|
-
'9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683',
|
|
1641
|
-
vout: 1,
|
|
1642
|
-
value: '546',
|
|
1643
|
-
confirmations: 0,
|
|
1644
|
-
satoshis: 546
|
|
1645
|
-
},
|
|
1646
|
-
{
|
|
1647
|
-
txid:
|
|
1648
|
-
'9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683',
|
|
1649
|
-
vout: 2,
|
|
1650
|
-
value: '13478',
|
|
1651
|
-
confirmations: 0,
|
|
1652
|
-
satoshis: 13478
|
|
1653
|
-
}
|
|
1654
|
-
]
|
|
1655
|
-
|
|
1656
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1657
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1658
|
-
|
|
1659
|
-
assert.isArray(data)
|
|
1660
|
-
|
|
1661
|
-
assert.equal(data[0].utxoType, 'token')
|
|
1662
|
-
assert.equal(data[0].tokenType, 65)
|
|
1663
|
-
assert.equal(data[0].isValid, true)
|
|
1664
|
-
|
|
1665
|
-
assert.equal(data[1].isValid, false)
|
|
1666
|
-
})
|
|
1667
|
-
|
|
1668
|
-
it('should decode an NFT Child Send transaction', async () => {
|
|
1669
|
-
// Define stubbed data.
|
|
1670
|
-
const slpData = {
|
|
1671
|
-
tokenType: 65,
|
|
1672
|
-
txType: 'SEND',
|
|
1673
|
-
tokenId:
|
|
1674
|
-
'9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683',
|
|
1675
|
-
amounts: ['1']
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
const genesisData = {
|
|
1679
|
-
tokenType: 65,
|
|
1680
|
-
txType: 'GENESIS',
|
|
1681
|
-
ticker: 'NFTC',
|
|
1682
|
-
name: 'NFT Child',
|
|
1683
|
-
tokenId:
|
|
1684
|
-
'9b6db26b64aedcedc0bd9a3037b29b3598573ec5cea99eec03faa838616cd683',
|
|
1685
|
-
documentUri: 'https://FullStack.cash',
|
|
1686
|
-
documentHash: '',
|
|
1687
|
-
decimals: 0,
|
|
1688
|
-
mintBatonVout: 0,
|
|
1689
|
-
qty: '1'
|
|
1690
|
-
}
|
|
1691
|
-
|
|
1692
|
-
// Mock external dependencies.
|
|
1693
|
-
// Stub the calls to decodeOpReturn.
|
|
1694
|
-
sandbox
|
|
1695
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1696
|
-
.resolves(slpData)
|
|
1697
|
-
.onCall(1)
|
|
1698
|
-
.resolves(genesisData)
|
|
1699
|
-
.onCall(2)
|
|
1700
|
-
.resolves(slpData)
|
|
1701
|
-
|
|
1702
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1703
|
-
|
|
1704
|
-
const utxos = [
|
|
1705
|
-
{
|
|
1706
|
-
txid:
|
|
1707
|
-
'6d68a7ffbb63ef851c43025f801a1d365cddda50b00741bca022c743d74cd61a',
|
|
1708
|
-
vout: 1,
|
|
1709
|
-
value: '546',
|
|
1710
|
-
confirmations: 0,
|
|
1711
|
-
satoshis: 546
|
|
1712
|
-
},
|
|
1713
|
-
{
|
|
1714
|
-
txid:
|
|
1715
|
-
'6d68a7ffbb63ef851c43025f801a1d365cddda50b00741bca022c743d74cd61a',
|
|
1716
|
-
vout: 2,
|
|
1717
|
-
value: '12136',
|
|
1718
|
-
confirmations: 0,
|
|
1719
|
-
satoshis: 12136
|
|
1720
|
-
}
|
|
1721
|
-
]
|
|
1722
|
-
|
|
1723
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1724
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1725
|
-
|
|
1726
|
-
assert.isArray(data)
|
|
1727
|
-
|
|
1728
|
-
assert.equal(data[0].utxoType, 'token')
|
|
1729
|
-
assert.equal(data[0].transactionType, 'send')
|
|
1730
|
-
assert.equal(data[0].tokenType, 65)
|
|
1731
|
-
assert.equal(data[0].isValid, true)
|
|
1732
|
-
|
|
1733
|
-
assert.equal(data[1].isValid, false)
|
|
1734
|
-
})
|
|
1735
|
-
|
|
1736
|
-
it('should decode an NFT Group Send transaction', async () => {
|
|
1737
|
-
// Define stubbed data.
|
|
1738
|
-
const slpData = {
|
|
1739
|
-
tokenType: 129,
|
|
1740
|
-
txType: 'SEND',
|
|
1741
|
-
tokenId:
|
|
1742
|
-
'eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981',
|
|
1743
|
-
amounts: ['1', '8']
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
const genesisData = {
|
|
1747
|
-
tokenType: 129,
|
|
1748
|
-
txType: 'GENESIS',
|
|
1749
|
-
ticker: 'NFTTT',
|
|
1750
|
-
name: 'NFT Test Token',
|
|
1751
|
-
tokenId:
|
|
1752
|
-
'eee4b82e4bb7113eca433829144363fc45f110693c286494fbf5b5c8043cc981',
|
|
1753
|
-
documentUri: 'https://FullStack.cash',
|
|
1754
|
-
documentHash: '',
|
|
1755
|
-
decimals: 0,
|
|
1756
|
-
mintBatonVout: 2,
|
|
1757
|
-
qty: '1'
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// Mock external dependencies.
|
|
1761
|
-
// Stub the calls to decodeOpReturn.
|
|
1762
|
-
sandbox
|
|
1763
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1764
|
-
.resolves(slpData)
|
|
1765
|
-
.onCall(1)
|
|
1766
|
-
.resolves(genesisData)
|
|
1767
|
-
.onCall(2)
|
|
1768
|
-
.resolves(slpData)
|
|
1769
|
-
.onCall(3)
|
|
1770
|
-
.resolves(genesisData)
|
|
1771
|
-
.onCall(4)
|
|
1772
|
-
.resolves(slpData)
|
|
1773
|
-
|
|
1774
|
-
sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
|
|
1775
|
-
|
|
1776
|
-
const utxos = [
|
|
1777
|
-
{
|
|
1778
|
-
txid:
|
|
1779
|
-
'57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766',
|
|
1780
|
-
vout: 1,
|
|
1781
|
-
value: '546',
|
|
1782
|
-
confirmations: 0,
|
|
1783
|
-
satoshis: 546
|
|
1784
|
-
},
|
|
1785
|
-
{
|
|
1786
|
-
txid:
|
|
1787
|
-
'57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766',
|
|
1788
|
-
vout: 2,
|
|
1789
|
-
value: '546',
|
|
1790
|
-
confirmations: 0,
|
|
1791
|
-
satoshis: 546
|
|
1792
|
-
},
|
|
1793
|
-
{
|
|
1794
|
-
txid:
|
|
1795
|
-
'57cc47c265ce878679e95e2cec510d8a1a9840f5c62feb4743cc5947d57d9766',
|
|
1796
|
-
vout: 3,
|
|
1797
|
-
value: '10794',
|
|
1798
|
-
confirmations: 0,
|
|
1799
|
-
satoshis: 10794
|
|
1800
|
-
}
|
|
1801
|
-
]
|
|
1802
|
-
|
|
1803
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1804
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1805
|
-
|
|
1806
|
-
assert.isArray(data)
|
|
1807
|
-
|
|
1808
|
-
assert.equal(data[0].utxoType, 'token')
|
|
1809
|
-
assert.equal(data[0].transactionType, 'send')
|
|
1810
|
-
assert.equal(data[0].tokenType, 129)
|
|
1811
|
-
assert.equal(data[0].isValid, true)
|
|
1812
|
-
|
|
1813
|
-
assert.equal(data[1].utxoType, 'token')
|
|
1814
|
-
assert.equal(data[1].transactionType, 'send')
|
|
1815
|
-
assert.equal(data[1].tokenType, 129)
|
|
1816
|
-
assert.equal(data[1].isValid, true)
|
|
1817
|
-
|
|
1818
|
-
assert.equal(data[2].isValid, false)
|
|
1819
|
-
})
|
|
1820
|
-
|
|
1821
|
-
it('should return null value when 429 recieved', async () => {
|
|
1822
|
-
const utxos = [
|
|
1823
|
-
{
|
|
1824
|
-
height: 654522,
|
|
1825
|
-
tx_hash:
|
|
1826
|
-
'072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1',
|
|
1827
|
-
tx_pos: 0,
|
|
1828
|
-
value: 6999,
|
|
1829
|
-
satoshis: 6999,
|
|
1830
|
-
txid:
|
|
1831
|
-
'072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1',
|
|
1832
|
-
vout: 0
|
|
1833
|
-
},
|
|
1834
|
-
{
|
|
1835
|
-
height: 654522,
|
|
1836
|
-
tx_hash:
|
|
1837
|
-
'a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca',
|
|
1838
|
-
tx_pos: 1,
|
|
1839
|
-
value: 546,
|
|
1840
|
-
satoshis: 546,
|
|
1841
|
-
txid:
|
|
1842
|
-
'a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca',
|
|
1843
|
-
vout: 1
|
|
1844
|
-
}
|
|
1845
|
-
]
|
|
1846
|
-
|
|
1847
|
-
sandbox.stub(uut.Utils, 'decodeOpReturn').rejects({
|
|
1848
|
-
error:
|
|
1849
|
-
'Too many requests. Your limits are currently 3 requests per minute. Increase rate limits at https://fullstack.cash'
|
|
1850
|
-
})
|
|
1851
|
-
|
|
1852
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1853
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1854
|
-
|
|
1855
|
-
// Values should be 'null' to signal that a determination could not be made
|
|
1856
|
-
// due to a throttling issue.
|
|
1857
|
-
assert.equal(data[0].isValid, null)
|
|
1858
|
-
assert.equal(data[1].isValid, null)
|
|
1859
|
-
})
|
|
1860
|
-
|
|
1861
|
-
// it("should handle a dust attack", async () => {
|
|
1862
|
-
it('should handle dust attack UTXOs', async () => {
|
|
1863
|
-
// Mock external dependencies.
|
|
1864
|
-
// Stub the calls to decodeOpReturn.
|
|
1865
|
-
sandbox
|
|
1866
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1867
|
-
.rejects(new Error('lokad id wrong size'))
|
|
1868
|
-
|
|
1869
|
-
const utxos = [
|
|
1870
|
-
{
|
|
1871
|
-
height: 655965,
|
|
1872
|
-
tx_hash:
|
|
1873
|
-
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
|
|
1874
|
-
tx_pos: 151,
|
|
1875
|
-
value: 547,
|
|
1876
|
-
satoshis: 547,
|
|
1877
|
-
txid:
|
|
1878
|
-
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
|
|
1879
|
-
vout: 151,
|
|
1880
|
-
address: 'bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww',
|
|
1881
|
-
hdIndex: 11
|
|
1882
|
-
}
|
|
1883
|
-
]
|
|
1884
|
-
|
|
1885
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1886
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1887
|
-
|
|
1888
|
-
assert.equal(data[0].isValid, false)
|
|
1889
|
-
})
|
|
1890
|
-
|
|
1891
|
-
it('should invalidate a malformed SLP OP_RETURN', async () => {
|
|
1892
|
-
sandbox
|
|
1893
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1894
|
-
.rejects(new Error('trailing data'))
|
|
1895
|
-
|
|
1896
|
-
const utxos = [
|
|
1897
|
-
// Malformed SLP tx
|
|
1898
|
-
{
|
|
1899
|
-
note: 'Malformed SLP tx',
|
|
1900
|
-
tx_hash:
|
|
1901
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
|
1902
|
-
tx_pos: 1,
|
|
1903
|
-
value: 546
|
|
1904
|
-
}
|
|
1905
|
-
]
|
|
1906
|
-
|
|
1907
|
-
const data = await uut.Utils.tokenUtxoDetails(utxos)
|
|
1908
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1909
|
-
|
|
1910
|
-
assert.equal(data[0].isValid, false)
|
|
1911
|
-
})
|
|
1912
|
-
})
|
|
1913
|
-
|
|
1914
|
-
describe('#tokenUtxoDetailsWL', () => {
|
|
1915
|
-
it('should return details for a simple SEND SLP token utxo', async () => {
|
|
1916
|
-
// Mock the call to REST API
|
|
1917
|
-
// Stub the calls to decodeOpReturn.
|
|
1918
|
-
sandbox
|
|
1919
|
-
.stub(uut.Utils, 'decodeOpReturn')
|
|
1920
|
-
.onCall(0)
|
|
1921
|
-
.resolves({
|
|
1922
|
-
tokenType: 1,
|
|
1923
|
-
txType: 'SEND',
|
|
1924
|
-
tokenId:
|
|
1925
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
1926
|
-
amounts: ['200000000', '99887500000000']
|
|
1927
|
-
})
|
|
1928
|
-
.onCall(1)
|
|
1929
|
-
.resolves({
|
|
1930
|
-
tokenType: 1,
|
|
1931
|
-
txType: 'GENESIS',
|
|
1932
|
-
ticker: 'TOK-CH',
|
|
1933
|
-
name: 'TokyoCash',
|
|
1934
|
-
tokenId:
|
|
1935
|
-
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
|
1936
|
-
documentUri: '',
|
|
1937
|
-
documentHash: '',
|
|
1938
|
-
decimals: 8,
|
|
1939
|
-
mintBatonVout: 0,
|
|
1940
|
-
qty: '2100000000000000'
|
|
1941
|
-
})
|
|
1942
|
-
|
|
1943
|
-
// Stub the call to validateTxid
|
|
1944
|
-
sandbox.stub(uut.Utils, 'validateTxid3').resolves([
|
|
1945
|
-
{
|
|
1946
|
-
txid:
|
|
1947
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
1948
|
-
valid: true
|
|
1949
|
-
}
|
|
1950
|
-
])
|
|
1951
|
-
|
|
1952
|
-
const utxos = [
|
|
1953
|
-
{
|
|
1954
|
-
txid:
|
|
1955
|
-
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
|
|
1956
|
-
vout: 1,
|
|
1957
|
-
amount: 0.00000546,
|
|
1958
|
-
satoshis: 546,
|
|
1959
|
-
height: 596089,
|
|
1960
|
-
confirmations: 748
|
|
1961
|
-
}
|
|
1962
|
-
]
|
|
1963
|
-
|
|
1964
|
-
const data = await uut.Utils.tokenUtxoDetailsWL(utxos)
|
|
1965
|
-
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
|
1966
|
-
|
|
1967
|
-
assert.property(data[0], 'txid')
|
|
1968
|
-
assert.property(data[0], 'vout')
|
|
1969
|
-
assert.property(data[0], 'height')
|
|
1970
|
-
assert.property(data[0], 'utxoType')
|
|
1971
|
-
assert.property(data[0], 'tokenId')
|
|
1972
|
-
assert.property(data[0], 'tokenTicker')
|
|
1973
|
-
assert.property(data[0], 'tokenName')
|
|
1974
|
-
assert.property(data[0], 'tokenDocumentUrl')
|
|
1975
|
-
assert.property(data[0], 'tokenDocumentHash')
|
|
1976
|
-
assert.property(data[0], 'decimals')
|
|
1977
|
-
assert.property(data[0], 'tokenQty')
|
|
1978
|
-
assert.property(data[0], 'isValid')
|
|
1979
|
-
assert.equal(data[0].isValid, true)
|
|
1980
|
-
})
|
|
1981
|
-
})
|
|
1982
|
-
|
|
1983
|
-
describe('#txDetails', () => {
|
|
1984
|
-
it('should throw an error if txid is not included', async () => {
|
|
1985
|
-
try {
|
|
1986
|
-
await uut.Utils.txDetails()
|
|
1987
|
-
} catch (err) {
|
|
1988
|
-
assert.include(
|
|
1989
|
-
err.message,
|
|
1990
|
-
'txid string must be included',
|
|
1991
|
-
'Expected error message.'
|
|
1992
|
-
)
|
|
1993
|
-
}
|
|
1994
|
-
})
|
|
1995
|
-
|
|
1996
|
-
it('should throw error for non-existent txid', async () => {
|
|
1997
|
-
try {
|
|
1998
|
-
// Mock the call to the REST API
|
|
1999
|
-
if (process.env.TEST === 'unit') {
|
|
2000
|
-
sandbox
|
|
2001
|
-
.stub(uut.Utils.axios, 'get')
|
|
2002
|
-
// .resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn })
|
|
2003
|
-
.throws({ error: 'TXID not found' })
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
const txid =
|
|
2007
|
-
'd284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e'
|
|
2008
|
-
|
|
2009
|
-
await uut.Utils.txDetails(txid)
|
|
2010
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2011
|
-
|
|
2012
|
-
assert.fail('Unexpected result')
|
|
2013
|
-
} catch (err) {
|
|
2014
|
-
// console.log(`err: `, err)
|
|
2015
|
-
assert.include(err.error, 'TXID not found', 'Expected error message.')
|
|
2016
|
-
}
|
|
2017
|
-
})
|
|
2018
|
-
|
|
2019
|
-
it('should return details for an SLP txid', async () => {
|
|
2020
|
-
// Mock the call to the REST API
|
|
2021
|
-
if (process.env.TEST === 'unit') {
|
|
2022
|
-
sandbox
|
|
2023
|
-
.stub(uut.Utils.axios, 'get')
|
|
2024
|
-
.resolves({ data: mockData.mockTxDetails })
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
const txid =
|
|
2028
|
-
'9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0'
|
|
2029
|
-
|
|
2030
|
-
const result = await uut.Utils.txDetails(txid)
|
|
2031
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2032
|
-
|
|
2033
|
-
assert.hasAnyKeys(result, [
|
|
2034
|
-
'txid',
|
|
2035
|
-
'version',
|
|
2036
|
-
'locktime',
|
|
2037
|
-
'vin',
|
|
2038
|
-
'vout',
|
|
2039
|
-
'blockhash',
|
|
2040
|
-
'blockheight',
|
|
2041
|
-
'confirmations',
|
|
2042
|
-
'time',
|
|
2043
|
-
'blocktime',
|
|
2044
|
-
'valueOut',
|
|
2045
|
-
'size',
|
|
2046
|
-
'valueIn',
|
|
2047
|
-
'fees',
|
|
2048
|
-
'tokenInfo',
|
|
2049
|
-
'tokenIsValid'
|
|
2050
|
-
])
|
|
2051
|
-
})
|
|
2052
|
-
})
|
|
2053
|
-
|
|
2054
|
-
describe('#hydrateUtxos', () => {
|
|
2055
|
-
it('should throw an error if input is not an array', async () => {
|
|
2056
|
-
try {
|
|
2057
|
-
const utxos = 1234
|
|
2058
|
-
|
|
2059
|
-
await uut.Utils.hydrateUtxos(utxos)
|
|
2060
|
-
|
|
2061
|
-
assert.equal(true, false, 'Uh oh. Code path should not end here.')
|
|
2062
|
-
} catch (err) {
|
|
2063
|
-
// console.log(`Error: `, err)
|
|
2064
|
-
assert.include(err.message, 'Input must be an array.')
|
|
2065
|
-
}
|
|
2066
|
-
})
|
|
2067
|
-
})
|
|
2068
|
-
|
|
2069
|
-
describe('#hydrateUtxosWL', () => {
|
|
2070
|
-
it('should throw an error if input is not an array', async () => {
|
|
2071
|
-
try {
|
|
2072
|
-
const utxos = 1234
|
|
2073
|
-
|
|
2074
|
-
await uut.Utils.hydrateUtxosWL(utxos)
|
|
2075
|
-
|
|
2076
|
-
assert.equal(true, false, 'Uh oh. Code path should not end here.')
|
|
2077
|
-
} catch (err) {
|
|
2078
|
-
// console.log(`Error: `, err)
|
|
2079
|
-
assert.include(err.message, 'Input must be an array.')
|
|
2080
|
-
}
|
|
2081
|
-
})
|
|
2082
|
-
})
|
|
2083
|
-
|
|
2084
|
-
describe('#validateTxid2', () => {
|
|
2085
|
-
it('should throw an error if the input is an array', async () => {
|
|
2086
|
-
try {
|
|
2087
|
-
const txid = [
|
|
2088
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
2089
|
-
]
|
|
2090
|
-
|
|
2091
|
-
await uut.Utils.validateTxid2(txid)
|
|
2092
|
-
|
|
2093
|
-
assert.equal(true, false, 'Unexpected result')
|
|
2094
|
-
} catch (err) {
|
|
2095
|
-
// console.log("err: ", err)
|
|
2096
|
-
assert.include(err.message, 'txid must be 64 character string')
|
|
2097
|
-
}
|
|
2098
|
-
})
|
|
2099
|
-
|
|
2100
|
-
it('should throw an error for a malformed txid', async () => {
|
|
2101
|
-
try {
|
|
2102
|
-
const txid =
|
|
2103
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783'
|
|
2104
|
-
|
|
2105
|
-
await uut.Utils.validateTxid2(txid)
|
|
2106
|
-
|
|
2107
|
-
assert.equal(true, false, 'Unexpected result')
|
|
2108
|
-
} catch (err) {
|
|
2109
|
-
// console.log("err: ", err)
|
|
2110
|
-
assert.include(err.message, 'txid must be 64 character string')
|
|
2111
|
-
}
|
|
2112
|
-
})
|
|
2113
|
-
|
|
2114
|
-
it('should invalidate a known invalid TXID', async () => {
|
|
2115
|
-
const txid =
|
|
2116
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
2117
|
-
|
|
2118
|
-
// Mock live network calls.
|
|
2119
|
-
sandbox.stub(uut.Utils.axios, 'get').resolves({
|
|
2120
|
-
data: {
|
|
2121
|
-
txid: txid,
|
|
2122
|
-
isValid: false,
|
|
2123
|
-
msg: ''
|
|
2124
|
-
}
|
|
2125
|
-
})
|
|
2126
|
-
|
|
2127
|
-
const result = await uut.Utils.validateTxid2(txid)
|
|
2128
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2129
|
-
|
|
2130
|
-
assert.property(result, 'txid')
|
|
2131
|
-
assert.equal(result.txid, txid)
|
|
2132
|
-
|
|
2133
|
-
assert.property(result, 'isValid')
|
|
2134
|
-
assert.equal(result.isValid, false)
|
|
2135
|
-
})
|
|
2136
|
-
|
|
2137
|
-
it('should validate a known valid TXID', async () => {
|
|
2138
|
-
const txid =
|
|
2139
|
-
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
|
|
2140
|
-
|
|
2141
|
-
// Mock live network calls.
|
|
2142
|
-
sandbox.stub(uut.Utils.axios, 'get').resolves({
|
|
2143
|
-
data: {
|
|
2144
|
-
txid: txid,
|
|
2145
|
-
isValid: true,
|
|
2146
|
-
msg: ''
|
|
2147
|
-
}
|
|
2148
|
-
})
|
|
2149
|
-
|
|
2150
|
-
const result = await uut.Utils.validateTxid2(txid)
|
|
2151
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2152
|
-
|
|
2153
|
-
assert.property(result, 'txid')
|
|
2154
|
-
assert.equal(result.txid, txid)
|
|
2155
|
-
|
|
2156
|
-
assert.property(result, 'isValid')
|
|
2157
|
-
assert.equal(result.isValid, true)
|
|
2158
|
-
})
|
|
2159
|
-
|
|
2160
|
-
// slp-validate can take a long time. bch-api cuts it off if it fails to
|
|
2161
|
-
// return is less than 10 seconds. This test case handle this situation.
|
|
2162
|
-
it('should handle timeout errors', async () => {
|
|
2163
|
-
try {
|
|
2164
|
-
const txid =
|
|
2165
|
-
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
|
|
2166
|
-
|
|
2167
|
-
// Mock live network calls.
|
|
2168
|
-
sandbox.stub(uut.Utils.axios, 'get').rejects({
|
|
2169
|
-
error:
|
|
2170
|
-
'Network error: Could not communicate with full node or other external service.'
|
|
2171
|
-
})
|
|
2172
|
-
|
|
2173
|
-
await uut.Utils.validateTxid2(txid)
|
|
2174
|
-
|
|
2175
|
-
assert.equal(true, false, 'Unexpected result')
|
|
2176
|
-
} catch (err) {
|
|
2177
|
-
// console.log("err: ", err)
|
|
2178
|
-
assert.include(err.message, 'slp-validate timed out')
|
|
2179
|
-
}
|
|
2180
|
-
})
|
|
2181
|
-
})
|
|
2182
|
-
|
|
2183
|
-
describe('#validateTxid', () => {
|
|
2184
|
-
it('should invalidate a known invalid TXID', async () => {
|
|
2185
|
-
const txid =
|
|
2186
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
2187
|
-
|
|
2188
|
-
// Mock live network calls.
|
|
2189
|
-
sandbox.stub(uut.Utils.axios, 'post').resolves({
|
|
2190
|
-
data: mockData.mockValidateTxid3Invalid
|
|
2191
|
-
})
|
|
2192
|
-
|
|
2193
|
-
const result = await uut.Utils.validateTxid(txid)
|
|
2194
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2195
|
-
|
|
2196
|
-
assert.isArray(result)
|
|
2197
|
-
|
|
2198
|
-
assert.property(result[0], 'txid')
|
|
2199
|
-
assert.equal(result[0].txid, txid)
|
|
2200
|
-
|
|
2201
|
-
assert.property(result[0], 'valid')
|
|
2202
|
-
assert.equal(result[0].valid, null)
|
|
2203
|
-
})
|
|
2204
|
-
/*
|
|
2205
|
-
it("should handle a mix of valid, invalid, and non-SLP txs", async () => {
|
|
2206
|
-
const txids = [
|
|
2207
|
-
// Malformed SLP tx
|
|
2208
|
-
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
|
|
2209
|
-
// Normal TX (non-SLP)
|
|
2210
|
-
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
|
|
2211
|
-
// Valid PSF SLP tx
|
|
2212
|
-
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
|
|
2213
|
-
// Valid SLP token not in whitelist
|
|
2214
|
-
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
|
|
2215
|
-
]
|
|
2216
|
-
|
|
2217
|
-
const result = await uut.Utils.validateTxid(txids)
|
|
2218
|
-
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2219
|
-
})
|
|
2220
|
-
*/
|
|
2221
|
-
})
|
|
2222
|
-
|
|
2223
|
-
describe('#waterfallValidateTxid', () => {
|
|
2224
|
-
// This test ensures that the whitelist SLPDB is called before the
|
|
2225
|
-
// general purpose SLPDB.
|
|
2226
|
-
it('should call validateTxid3() first', async () => {
|
|
2227
|
-
const txid = 'fakeTxid'
|
|
2228
|
-
|
|
2229
|
-
const retVal = [{ txid, valid: true }]
|
|
2230
|
-
|
|
2231
|
-
// Use stubs to force the desired code path.
|
|
2232
|
-
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal)
|
|
2233
|
-
sandbox
|
|
2234
|
-
.stub(uut.Utils, 'validateTxid')
|
|
2235
|
-
.rejects(new Error('Unexpected code path'))
|
|
2236
|
-
sandbox
|
|
2237
|
-
.stub(uut.Utils, 'validateTxid2')
|
|
2238
|
-
.rejects(new Error('Unexpected code path'))
|
|
2239
|
-
|
|
2240
|
-
const result = await uut.Utils.waterfallValidateTxid(txid)
|
|
2241
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2242
|
-
|
|
2243
|
-
assert.equal(result, true)
|
|
2244
|
-
})
|
|
2245
|
-
|
|
2246
|
-
// This test ensures that the general purpose SLPDB is called after the
|
|
2247
|
-
// whitelist SLPDB, but before slp-api.
|
|
2248
|
-
it('should call validateTxid() second', async () => {
|
|
2249
|
-
const txid = 'fakeTxid'
|
|
2250
|
-
|
|
2251
|
-
const retVal1 = [{ txid, valid: null }]
|
|
2252
|
-
const retVal2 = [{ txid, valid: true }]
|
|
2253
|
-
|
|
2254
|
-
// Use stubs to force the desired code path.
|
|
2255
|
-
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1)
|
|
2256
|
-
sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal2)
|
|
2257
|
-
sandbox
|
|
2258
|
-
.stub(uut.Utils, 'validateTxid2')
|
|
2259
|
-
.rejects(new Error('Unexpected code path'))
|
|
2260
|
-
|
|
2261
|
-
const result = await uut.Utils.waterfallValidateTxid(txid)
|
|
2262
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2263
|
-
|
|
2264
|
-
assert.equal(result, true)
|
|
2265
|
-
})
|
|
2266
|
-
|
|
2267
|
-
// This test ensures that slp-api is called if both SLPDBs return null.
|
|
2268
|
-
it('should call slp-api last', async () => {
|
|
2269
|
-
const txid = 'fakeTxid'
|
|
2270
|
-
|
|
2271
|
-
const retVal1 = [{ txid, valid: null }]
|
|
2272
|
-
|
|
2273
|
-
// Use stubs to force the desired code path.
|
|
2274
|
-
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1)
|
|
2275
|
-
sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal1)
|
|
2276
|
-
sandbox.stub(uut.Utils, 'validateTxid2').resolves({ isValid: true })
|
|
2277
|
-
|
|
2278
|
-
const result = await uut.Utils.waterfallValidateTxid(txid)
|
|
2279
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2280
|
-
|
|
2281
|
-
assert.equal(result, true)
|
|
2282
|
-
})
|
|
2283
|
-
|
|
2284
|
-
// CT 08-09-21 Changed behavior. validateTxid3() is now wrapped in a
|
|
2285
|
-
// try/catch statement that exits quietly if there is an error.
|
|
2286
|
-
// it('catches and throws an error', async () => {
|
|
2287
|
-
// try {
|
|
2288
|
-
// const txid = 'fakeTxid'
|
|
2289
|
-
//
|
|
2290
|
-
// // Force an error
|
|
2291
|
-
// sandbox
|
|
2292
|
-
// .stub(uut.Utils, 'validateTxid3')
|
|
2293
|
-
// .rejects(new Error('fake error'))
|
|
2294
|
-
//
|
|
2295
|
-
// await await uut.Utils.waterfallValidateTxid(txid)
|
|
2296
|
-
//
|
|
2297
|
-
// assert.fail('Unexpected result')
|
|
2298
|
-
// } catch (err) {
|
|
2299
|
-
// // console.log(`err.message: ${err.message}`)
|
|
2300
|
-
// assert.include(err.message, 'fake error')
|
|
2301
|
-
// }
|
|
2302
|
-
// })
|
|
2303
|
-
})
|
|
2304
|
-
|
|
2305
|
-
describe('#getWhitelist', () => {
|
|
2306
|
-
it('should return the list', async () => {
|
|
2307
|
-
sandbox
|
|
2308
|
-
.stub(uut.Utils.axios, 'get')
|
|
2309
|
-
.resolves({ data: mockData.mockWhitelist })
|
|
2310
|
-
|
|
2311
|
-
const result = await uut.Utils.getWhitelist()
|
|
2312
|
-
|
|
2313
|
-
assert.isArray(result)
|
|
2314
|
-
assert.property(result[0], 'name')
|
|
2315
|
-
assert.property(result[1], 'tokenId')
|
|
2316
|
-
})
|
|
2317
|
-
|
|
2318
|
-
it('catches and throws an error', async () => {
|
|
2319
|
-
try {
|
|
2320
|
-
sandbox.stub(uut.Utils.axios, 'get').rejects({
|
|
2321
|
-
error:
|
|
2322
|
-
'Network error: Could not communicate with full node or other external service.'
|
|
2323
|
-
})
|
|
2324
|
-
|
|
2325
|
-
await uut.Utils.getWhitelist()
|
|
2326
|
-
|
|
2327
|
-
assert.fail('Unexpected result')
|
|
2328
|
-
} catch (err) {
|
|
2329
|
-
console.log('err: ', err)
|
|
2330
|
-
assert.include(err.error, 'Network error')
|
|
2331
|
-
}
|
|
2332
|
-
})
|
|
2333
|
-
})
|
|
2334
|
-
|
|
2335
|
-
describe('#validateTxid3', () => {
|
|
2336
|
-
it('should invalidate a known invalid TXID', async () => {
|
|
2337
|
-
const txid =
|
|
2338
|
-
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
|
2339
|
-
|
|
2340
|
-
// Mock live network calls.
|
|
2341
|
-
sandbox.stub(uut.Utils.axios, 'post').resolves({
|
|
2342
|
-
data: mockData.mockValidateTxid3Invalid
|
|
2343
|
-
})
|
|
2344
|
-
|
|
2345
|
-
const result = await uut.Utils.validateTxid3(txid)
|
|
2346
|
-
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
2347
|
-
|
|
2348
|
-
assert.isArray(result)
|
|
2349
|
-
|
|
2350
|
-
assert.property(result[0], 'txid')
|
|
2351
|
-
assert.equal(result[0].txid, txid)
|
|
2352
|
-
|
|
2353
|
-
assert.property(result[0], 'valid')
|
|
2354
|
-
assert.equal(result[0].valid, null)
|
|
2355
|
-
})
|
|
2356
|
-
|
|
2357
|
-
it('should handle an array with a single element', async () => {
|
|
2358
|
-
sandbox
|
|
2359
|
-
.stub(uut.Utils.axios, 'post')
|
|
2360
|
-
.resolves({ data: mockData.mockValidateTxid3Valid })
|
|
2361
|
-
|
|
2362
|
-
const txid = [
|
|
2363
|
-
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
|
|
2364
|
-
]
|
|
2365
|
-
|
|
2366
|
-
const result = await uut.Utils.validateTxid3(txid)
|
|
2367
|
-
|
|
2368
|
-
assert.isArray(result)
|
|
2369
|
-
|
|
2370
|
-
assert.property(result[0], 'txid')
|
|
2371
|
-
assert.equal(result[0].txid, txid)
|
|
2372
|
-
|
|
2373
|
-
assert.property(result[0], 'valid')
|
|
2374
|
-
assert.equal(result[0].valid, true)
|
|
2375
|
-
})
|
|
2376
|
-
|
|
2377
|
-
it('should handle an single string input', async () => {
|
|
2378
|
-
sandbox
|
|
2379
|
-
.stub(uut.Utils.axios, 'post')
|
|
2380
|
-
.resolves({ data: mockData.mockValidateTxid3Valid })
|
|
2381
|
-
|
|
2382
|
-
const txid =
|
|
2383
|
-
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
|
|
2384
|
-
|
|
2385
|
-
const result = await uut.Utils.validateTxid3(txid)
|
|
2386
|
-
|
|
2387
|
-
assert.isArray(result)
|
|
2388
|
-
|
|
2389
|
-
assert.property(result[0], 'txid')
|
|
2390
|
-
assert.equal(result[0].txid, txid)
|
|
2391
|
-
|
|
2392
|
-
assert.property(result[0], 'valid')
|
|
2393
|
-
assert.equal(result[0].valid, true)
|
|
2394
|
-
})
|
|
2395
|
-
|
|
2396
|
-
it('catches and throws an error', async () => {
|
|
2397
|
-
try {
|
|
2398
|
-
sandbox.stub(uut.Utils.axios, 'post').rejects({
|
|
2399
|
-
error:
|
|
2400
|
-
'Network error: Could not communicate with full node or other external service.'
|
|
2401
|
-
})
|
|
2402
|
-
|
|
2403
|
-
await uut.Utils.validateTxid3()
|
|
2404
|
-
|
|
2405
|
-
assert.equal(true, false, 'Unexpected result')
|
|
2406
|
-
} catch (err) {
|
|
2407
|
-
// console.log("err: ", err)
|
|
2408
|
-
assert.include(err.error, 'Network error')
|
|
2409
|
-
}
|
|
2410
|
-
})
|
|
2411
|
-
})
|
|
2412
|
-
|
|
2413
|
-
describe('#getStatus', () => {
|
|
2414
|
-
it('should return the current block height of the SLPDB indexer', async () => {
|
|
2415
|
-
sandbox
|
|
2416
|
-
.stub(uut.Utils.axios, 'get')
|
|
2417
|
-
.resolves({ data: mockData.slpdbStatus })
|
|
2418
|
-
|
|
2419
|
-
const result = await uut.Utils.getStatus()
|
|
2420
|
-
|
|
2421
|
-
// console.log(`result: `, result)
|
|
2422
|
-
|
|
2423
|
-
assert.property(result, 'bchBlockHeight')
|
|
2424
|
-
assert.property(result, 'slpProcessedBlockHeight')
|
|
2425
|
-
})
|
|
2426
|
-
})
|
|
2427
322
|
})
|