dxs-stas-sdk 1.0.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/jest.config.js +6 -0
- package/package.json +38 -0
- package/rollup.config.ts +26 -0
- package/src/base.ts +4 -0
- package/src/bitcoin/address.ts +42 -0
- package/src/bitcoin/destination.ts +7 -0
- package/src/bitcoin/index.ts +15 -0
- package/src/bitcoin/mnemonic.ts +40 -0
- package/src/bitcoin/network.ts +11 -0
- package/src/bitcoin/op-codes.ts +143 -0
- package/src/bitcoin/out-point.ts +61 -0
- package/src/bitcoin/payment.ts +8 -0
- package/src/bitcoin/private-key.ts +57 -0
- package/src/bitcoin/script-type.ts +9 -0
- package/src/bitcoin/sig-hash-type.ts +7 -0
- package/src/bitcoin/token-scheme.ts +28 -0
- package/src/bitcoin/transaction-input.ts +33 -0
- package/src/bitcoin/transaction-output.ts +105 -0
- package/src/bitcoin/transaction.ts +31 -0
- package/src/bitcoin/wallet.ts +59 -0
- package/src/buffer/buffer-reader.ts +88 -0
- package/src/buffer/buffer-utils.ts +116 -0
- package/src/buffer/buffer-writer.ts +74 -0
- package/src/buffer/index.ts +3 -0
- package/src/hashes.ts +13 -0
- package/src/index.ts +8 -0
- package/src/script/build/null-data-builder.ts +21 -0
- package/src/script/build/p2pkh-builder.ts +34 -0
- package/src/script/build/p2stas-builder.ts +38 -0
- package/src/script/build/script-builder.ts +128 -0
- package/src/script/index.ts +10 -0
- package/src/script/read/script-reader.ts +79 -0
- package/src/script/script-samples.ts +27 -0
- package/src/script/script-token.ts +66 -0
- package/src/script/script-utils.ts +12 -0
- package/src/stas-bundle-factory.ts +367 -0
- package/src/transaction/build/input-builder.ts +270 -0
- package/src/transaction/build/output-builder.ts +25 -0
- package/src/transaction/build/transaction-builder.ts +163 -0
- package/src/transaction/index.ts +4 -0
- package/src/transaction/read/transaction-reader.ts +54 -0
- package/src/transaction-factory.ts +221 -0
- package/tests/script-build.test.ts +41 -0
- package/tests/script-read.test.ts +34 -0
- package/tests/stas-transactios.ts +41 -0
- package/tests/transaction-build.test.ts +394 -0
- package/tests/transaction-reader.test.ts +50 -0
- package/tsconfig.json +110 -0
- package/tslint.json +3 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cloneBuffer,
|
|
3
|
+
estimateChunkSize,
|
|
4
|
+
getChunkSize,
|
|
5
|
+
getNumberSize,
|
|
6
|
+
reverseBuffer,
|
|
7
|
+
splitBuffer,
|
|
8
|
+
} from "../../buffer/buffer-utils";
|
|
9
|
+
import { BufferWriter } from "../../buffer/buffer-writer";
|
|
10
|
+
import { OpCode } from "../../bitcoin/op-codes";
|
|
11
|
+
import { OutPoint } from "../../bitcoin/out-point";
|
|
12
|
+
import { PrivateKey } from "../../bitcoin/private-key";
|
|
13
|
+
import { ScriptType } from "../../bitcoin/script-type";
|
|
14
|
+
import { SignatureHashType } from "../../bitcoin/sig-hash-type";
|
|
15
|
+
import { hash256 } from "../../hashes";
|
|
16
|
+
import { ScriptBuilder } from "../../script/build/script-builder";
|
|
17
|
+
import { TransactionBuilder } from "./transaction-builder";
|
|
18
|
+
import { Wallet } from "../../bitcoin";
|
|
19
|
+
|
|
20
|
+
export class InputBilder {
|
|
21
|
+
protected TxBuilder: TransactionBuilder;
|
|
22
|
+
protected Owner: PrivateKey | Wallet;
|
|
23
|
+
protected Idx: number;
|
|
24
|
+
|
|
25
|
+
OutPoint: OutPoint;
|
|
26
|
+
Merge: Boolean;
|
|
27
|
+
UnlockingScript?: Buffer;
|
|
28
|
+
Sequence = TransactionBuilder.DefaultSequence;
|
|
29
|
+
|
|
30
|
+
private _mergeVout: number = 0;
|
|
31
|
+
private _mergeSegments: Buffer[] = [];
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
txBuilder: TransactionBuilder,
|
|
35
|
+
outPoint: OutPoint,
|
|
36
|
+
signer: PrivateKey | Wallet,
|
|
37
|
+
merge: boolean
|
|
38
|
+
) {
|
|
39
|
+
this.TxBuilder = txBuilder;
|
|
40
|
+
this.Idx = txBuilder.Inputs.length;
|
|
41
|
+
this.OutPoint = outPoint;
|
|
42
|
+
this.Owner = signer;
|
|
43
|
+
this.Merge = merge;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
sign = () => {
|
|
47
|
+
const preimage = this.preimage(TransactionBuilder.DefaultSighashType);
|
|
48
|
+
const hashedPreimage = hash256(preimage);
|
|
49
|
+
const der = this.Owner.sign(hashedPreimage);
|
|
50
|
+
const derWithSigHashType = Buffer.alloc(der.length + 1);
|
|
51
|
+
|
|
52
|
+
der.copy(derWithSigHashType);
|
|
53
|
+
derWithSigHashType.writeInt8(
|
|
54
|
+
TransactionBuilder.DefaultSighashType,
|
|
55
|
+
der.length
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (this.OutPoint.ScriptType === ScriptType.p2pkh) {
|
|
59
|
+
const size =
|
|
60
|
+
getChunkSize(derWithSigHashType) + getChunkSize(this.Owner.PublicKey);
|
|
61
|
+
const buffer = Buffer.alloc(size);
|
|
62
|
+
const bufferWriter = new BufferWriter(buffer);
|
|
63
|
+
|
|
64
|
+
bufferWriter.writeVarChunk(derWithSigHashType);
|
|
65
|
+
bufferWriter.writeVarChunk(this.Owner.PublicKey);
|
|
66
|
+
|
|
67
|
+
this.UnlockingScript = buffer;
|
|
68
|
+
} else if (this.OutPoint.ScriptType === ScriptType.p2stas) {
|
|
69
|
+
this.prepareMergeInfo();
|
|
70
|
+
|
|
71
|
+
const script = new ScriptBuilder(ScriptType.p2stas);
|
|
72
|
+
let hasNote = false;
|
|
73
|
+
|
|
74
|
+
for (const output of this.TxBuilder.Outputs) {
|
|
75
|
+
if (output.LockingScript.ScriptType === ScriptType.nullData) {
|
|
76
|
+
const nulldata = output.LockingScript.toBuffer();
|
|
77
|
+
const payload = Buffer.alloc(nulldata.length - 2);
|
|
78
|
+
|
|
79
|
+
nulldata.copy(payload, 0, 2);
|
|
80
|
+
script.addData(payload);
|
|
81
|
+
|
|
82
|
+
hasNote = true;
|
|
83
|
+
} else {
|
|
84
|
+
script
|
|
85
|
+
.addNumber(output.Satoshis)
|
|
86
|
+
.addData(output.LockingScript.ToAddress!.Hash160);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!hasNote) script.addOpCode(OpCode.OP_0);
|
|
91
|
+
|
|
92
|
+
var fundingInput =
|
|
93
|
+
this.TxBuilder.Inputs[this.TxBuilder.Inputs.length - 1];
|
|
94
|
+
|
|
95
|
+
script
|
|
96
|
+
.addNumber(fundingInput.OutPoint.Vout)
|
|
97
|
+
.addData(reverseBuffer(Buffer.from(fundingInput.OutPoint.TxId, "hex")));
|
|
98
|
+
|
|
99
|
+
if (this.Merge) {
|
|
100
|
+
script
|
|
101
|
+
.addNumber(this._mergeVout)
|
|
102
|
+
.addDatas(this._mergeSegments)
|
|
103
|
+
.addNumber(this._mergeSegments.length);
|
|
104
|
+
} else {
|
|
105
|
+
script.addOpCode(OpCode.OP_0);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
script
|
|
109
|
+
.addData(preimage)
|
|
110
|
+
.addData(derWithSigHashType)
|
|
111
|
+
.addData(this.Owner.PublicKey);
|
|
112
|
+
|
|
113
|
+
this.UnlockingScript = script.toBuffer();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
writeTo(bufferWriter: BufferWriter) {
|
|
118
|
+
bufferWriter.writeChunk(
|
|
119
|
+
reverseBuffer(Buffer.from(this.OutPoint.TxId, "hex"))
|
|
120
|
+
);
|
|
121
|
+
bufferWriter.writeUInt32(this.OutPoint.Vout);
|
|
122
|
+
bufferWriter.writeVarChunk(this.UnlockingScript!);
|
|
123
|
+
bufferWriter.writeUInt32(this.Sequence);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
size = () =>
|
|
127
|
+
32 + // TX.Id
|
|
128
|
+
4 + // Vout
|
|
129
|
+
this.unlockingScriptSize() +
|
|
130
|
+
4; // Sequence
|
|
131
|
+
|
|
132
|
+
preimageLength = (): number =>
|
|
133
|
+
4 + // Tx version
|
|
134
|
+
32 + // Prevout hash
|
|
135
|
+
32 + // Sequence hash
|
|
136
|
+
32 + // Output Tx id
|
|
137
|
+
4 + // VOUT ;
|
|
138
|
+
getChunkSize(this.OutPoint.LockignScript) +
|
|
139
|
+
8 + // Satoshis
|
|
140
|
+
4 + // Sequence
|
|
141
|
+
32 + //Outputs hash
|
|
142
|
+
4 + // Lock time
|
|
143
|
+
4; // Signature type
|
|
144
|
+
|
|
145
|
+
stasNullDataLength = () => {
|
|
146
|
+
const nullDataOutput = this.TxBuilder.Outputs.find(
|
|
147
|
+
(x) => x.LockingScript.ScriptType === ScriptType.nullData
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (!nullDataOutput) return 1;
|
|
151
|
+
|
|
152
|
+
return estimateChunkSize(nullDataOutput.LockingScript.size() - 2);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
prevoutHashLength = () => (32 + 4) * this.TxBuilder.Inputs.length;
|
|
156
|
+
|
|
157
|
+
unlockingScriptSize = (): number => {
|
|
158
|
+
if (this.UnlockingScript !== undefined) {
|
|
159
|
+
return estimateChunkSize(this.UnlockingScript.length);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let size =
|
|
163
|
+
1 + // OP_PUSH
|
|
164
|
+
73 + // DER-encoded signature (70-73 bytes)
|
|
165
|
+
1 + // OP_PUSH
|
|
166
|
+
33; // Public Key
|
|
167
|
+
|
|
168
|
+
if (this.OutPoint.ScriptType === ScriptType.p2stas) {
|
|
169
|
+
this.prepareMergeInfo();
|
|
170
|
+
|
|
171
|
+
const fundingIdx = this.TxBuilder.Inputs.length - 1;
|
|
172
|
+
const fundingOutpoint = this.TxBuilder.Inputs[fundingIdx].OutPoint;
|
|
173
|
+
|
|
174
|
+
size += this.stasNullDataLength();
|
|
175
|
+
size += this.TxBuilder.Outputs.reduce((a, x) => {
|
|
176
|
+
if (x.LockingScript.ScriptType === ScriptType.nullData) return a;
|
|
177
|
+
|
|
178
|
+
return a + getNumberSize(x.Satoshis) + 21;
|
|
179
|
+
}, 0);
|
|
180
|
+
|
|
181
|
+
size += getNumberSize(fundingOutpoint.Vout);
|
|
182
|
+
size += estimateChunkSize(32); // Funding Tx vout
|
|
183
|
+
size += estimateChunkSize(this.preimageLength());
|
|
184
|
+
|
|
185
|
+
if (!this.Merge) {
|
|
186
|
+
size += 1; // OP_0
|
|
187
|
+
} else {
|
|
188
|
+
size += getNumberSize(this._mergeVout);
|
|
189
|
+
size += getNumberSize(this._mergeSegments.length);
|
|
190
|
+
size += this._mergeSegments.reduce((a, x) => getChunkSize(x) + a, 0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return estimateChunkSize(size);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/// <summary>
|
|
198
|
+
/// Only SIGHASH_ALL|FORK_ID implemented
|
|
199
|
+
/// </summary>
|
|
200
|
+
preimage = (signatureHashType: SignatureHashType) => {
|
|
201
|
+
const size = this.preimageLength();
|
|
202
|
+
const buffer = Buffer.alloc(size);
|
|
203
|
+
var writer = new BufferWriter(buffer);
|
|
204
|
+
|
|
205
|
+
writer.writeUInt32(this.TxBuilder.Version); // 4
|
|
206
|
+
this.writePrevoutHash(writer); // 32
|
|
207
|
+
this.writeSequenceHash(writer); // 32
|
|
208
|
+
writer.writeChunk(reverseBuffer(Buffer.from(this.OutPoint.TxId, "hex"))); // 32
|
|
209
|
+
writer.writeUInt32(this.OutPoint.Vout); // 4
|
|
210
|
+
writer.writeVarChunk(this.OutPoint.LockignScript);
|
|
211
|
+
writer.writeUInt64(this.OutPoint.Satoshis); // 8
|
|
212
|
+
writer.writeUInt32(this.Sequence); // 4
|
|
213
|
+
this.writeOutputsHash(writer); // 32
|
|
214
|
+
writer.writeUInt32(this.TxBuilder.LockTime); // 4
|
|
215
|
+
writer.writeUInt32(signatureHashType); // 4
|
|
216
|
+
|
|
217
|
+
return buffer;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
private writePrevoutHash = (bufferWriter: BufferWriter) => {
|
|
221
|
+
const size = this.prevoutHashLength();
|
|
222
|
+
const buffer = Buffer.alloc(size);
|
|
223
|
+
const writer = new BufferWriter(buffer);
|
|
224
|
+
|
|
225
|
+
for (const input of this.TxBuilder.Inputs) {
|
|
226
|
+
writer.writeChunk(reverseBuffer(Buffer.from(input.OutPoint.TxId, "hex")));
|
|
227
|
+
writer.writeUInt32(input.OutPoint.Vout);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
bufferWriter.writeChunk(hash256(buffer));
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
private writeSequenceHash = (bufferWriter: BufferWriter) => {
|
|
234
|
+
const buffer = Buffer.alloc(4 * this.TxBuilder.Inputs.length);
|
|
235
|
+
const writer = new BufferWriter(buffer);
|
|
236
|
+
|
|
237
|
+
for (const input of this.TxBuilder.Inputs)
|
|
238
|
+
writer.writeUInt32(input.Sequence);
|
|
239
|
+
|
|
240
|
+
bufferWriter.writeChunk(hash256(buffer));
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
private writeOutputsHash = (bufferWriter: BufferWriter) => {
|
|
244
|
+
var size = this.TxBuilder.Outputs.reduce((a, x) => a + x.size(), 0);
|
|
245
|
+
|
|
246
|
+
const buffer = Buffer.alloc(size);
|
|
247
|
+
const writer = new BufferWriter(buffer);
|
|
248
|
+
|
|
249
|
+
for (const output of this.TxBuilder.Outputs) {
|
|
250
|
+
writer.writeUInt64(output.Satoshis);
|
|
251
|
+
writer.writeVarChunk(output.LockingScript.toBuffer());
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
bufferWriter.writeChunk(hash256(buffer));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
private prepareMergeInfo = () => {
|
|
258
|
+
if (!this.Merge || this._mergeSegments.length > 0) return;
|
|
259
|
+
|
|
260
|
+
const lockingScript = this.TxBuilder.Inputs[0].OutPoint.LockignScript;
|
|
261
|
+
const scriptToCut = cloneBuffer(lockingScript, 0, 23);
|
|
262
|
+
const mergeUtxo = this.TxBuilder.Inputs[this.Idx === 0 ? 1 : 0];
|
|
263
|
+
|
|
264
|
+
this._mergeVout = mergeUtxo.OutPoint.Vout;
|
|
265
|
+
this._mergeSegments = splitBuffer(
|
|
266
|
+
mergeUtxo.OutPoint.Transaction!.Raw,
|
|
267
|
+
scriptToCut
|
|
268
|
+
).reverse();
|
|
269
|
+
};
|
|
270
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { estimateChunkSize } from "../../buffer/buffer-utils";
|
|
2
|
+
import { BufferWriter } from "../../buffer/buffer-writer";
|
|
3
|
+
import { ScriptBuilder } from "../../script/build/script-builder";
|
|
4
|
+
|
|
5
|
+
export class OutputBuilder {
|
|
6
|
+
Satoshis: number;
|
|
7
|
+
LockingScript: ScriptBuilder;
|
|
8
|
+
|
|
9
|
+
constructor(lockingScript: ScriptBuilder, satoshis: number) {
|
|
10
|
+
this.LockingScript = lockingScript;
|
|
11
|
+
this.Satoshis = satoshis;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
size() {
|
|
15
|
+
return (
|
|
16
|
+
8 + // satoshis Size, always 8 bytes
|
|
17
|
+
estimateChunkSize(this.LockingScript.size())
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
writeTo(bufferWriter: BufferWriter) {
|
|
22
|
+
bufferWriter.writeUInt64(this.Satoshis);
|
|
23
|
+
bufferWriter.writeVarChunk(this.LockingScript.toBuffer());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { getVarIntLength } from "../../buffer/buffer-utils";
|
|
2
|
+
import { BufferWriter } from "../../buffer/buffer-writer";
|
|
3
|
+
import { Address } from "../../bitcoin/address";
|
|
4
|
+
import { OpCode } from "../../bitcoin/op-codes";
|
|
5
|
+
import { OutPoint } from "../../bitcoin/out-point";
|
|
6
|
+
import { PrivateKey } from "../../bitcoin/private-key";
|
|
7
|
+
import { SignatureHashType } from "../../bitcoin/sig-hash-type";
|
|
8
|
+
import { TokenScheme } from "../../bitcoin/token-scheme";
|
|
9
|
+
import { NullDataBuilder } from "../../script/build/null-data-builder";
|
|
10
|
+
import { P2pkhBuilder } from "../../script/build/p2pkh-builder";
|
|
11
|
+
import { P2stasBuilder } from "../../script/build/p2stas-builder";
|
|
12
|
+
import { ScriptReader } from "../../script/read/script-reader";
|
|
13
|
+
import { InputBilder } from "./input-builder";
|
|
14
|
+
import { OutputBuilder } from "./output-builder";
|
|
15
|
+
import { Wallet } from "../../bitcoin";
|
|
16
|
+
|
|
17
|
+
export class TransactionBuilder {
|
|
18
|
+
static DefaultSequence = 0xffffffff;
|
|
19
|
+
static DefaultSighashType =
|
|
20
|
+
SignatureHashType.SIGHASH_ALL | SignatureHashType.SIGHASH_FORKID;
|
|
21
|
+
|
|
22
|
+
Inputs: InputBilder[] = [];
|
|
23
|
+
Outputs: OutputBuilder[] = [];
|
|
24
|
+
|
|
25
|
+
Version = 1;
|
|
26
|
+
LockTime = 0;
|
|
27
|
+
|
|
28
|
+
static init = () => new TransactionBuilder();
|
|
29
|
+
|
|
30
|
+
size = () =>
|
|
31
|
+
4 + // version
|
|
32
|
+
4 + // locktime
|
|
33
|
+
getVarIntLength(this.Inputs.length) +
|
|
34
|
+
this.Inputs.reduce((a, x) => a + x.size(), 0) +
|
|
35
|
+
getVarIntLength(this.Outputs.length) +
|
|
36
|
+
this.Outputs.reduce((a, x) => a + x.size(), 0);
|
|
37
|
+
|
|
38
|
+
getFee = (satoshisPerByte: number) =>
|
|
39
|
+
Math.ceil(this.size() * satoshisPerByte);
|
|
40
|
+
|
|
41
|
+
addInput = (outPoint: OutPoint, signer: PrivateKey | Wallet) => {
|
|
42
|
+
this.Inputs.push(new InputBilder(this, outPoint, signer, false));
|
|
43
|
+
|
|
44
|
+
return this;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
addStasMergeInput = (outPoint: OutPoint, signer: PrivateKey | Wallet) => {
|
|
48
|
+
this.Inputs.push(new InputBilder(this, outPoint, signer, true));
|
|
49
|
+
|
|
50
|
+
return this;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
addP2PkhOutput = (value: number, to: Address, data: Buffer[] = []) => {
|
|
54
|
+
const script = new P2pkhBuilder(to);
|
|
55
|
+
|
|
56
|
+
for (const d of data) {
|
|
57
|
+
script.addReturnData(d);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.Outputs.push(new OutputBuilder(script, value));
|
|
61
|
+
|
|
62
|
+
return this;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
addNullDataOutput(data: Buffer[]) {
|
|
66
|
+
const script = new NullDataBuilder(data);
|
|
67
|
+
|
|
68
|
+
this.Outputs.push(new OutputBuilder(script, 0));
|
|
69
|
+
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
addChangeOutputWithFee(
|
|
74
|
+
to: Address,
|
|
75
|
+
change: number,
|
|
76
|
+
satoshisPerByte: number,
|
|
77
|
+
idx: number | null = null
|
|
78
|
+
) {
|
|
79
|
+
const script = new P2pkhBuilder(to);
|
|
80
|
+
const output = new OutputBuilder(script, change);
|
|
81
|
+
|
|
82
|
+
if (idx !== null) this.Outputs.splice(idx, 0, output);
|
|
83
|
+
else this.Outputs.push(output);
|
|
84
|
+
|
|
85
|
+
let fee = this.getFee(satoshisPerByte);
|
|
86
|
+
|
|
87
|
+
if (fee >= change) throw new Error("Insufficient satoshis to pay fee");
|
|
88
|
+
|
|
89
|
+
output.Satoshis = change - fee;
|
|
90
|
+
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
addStasOutputByScheme = (
|
|
95
|
+
schema: TokenScheme,
|
|
96
|
+
satoshis: number,
|
|
97
|
+
to: Address,
|
|
98
|
+
data: Buffer[] = []
|
|
99
|
+
) => {
|
|
100
|
+
const script = new P2stasBuilder(to, schema.TokenId, schema.Symbol);
|
|
101
|
+
|
|
102
|
+
for (const d of data) {
|
|
103
|
+
script.addData(d);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.Outputs.push(new OutputBuilder(script, satoshis));
|
|
107
|
+
|
|
108
|
+
return this;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
addStasOutputByPrevLockingScript = (
|
|
112
|
+
satoshis: number,
|
|
113
|
+
to: Address,
|
|
114
|
+
prevStasLockingScript: Buffer
|
|
115
|
+
) => {
|
|
116
|
+
const prevScriptTokens = ScriptReader.read(prevStasLockingScript);
|
|
117
|
+
const opReturnIdx = prevScriptTokens.findIndex(
|
|
118
|
+
(x) => x.OpCodeNum === OpCode.OP_RETURN
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const toknenId = prevScriptTokens[opReturnIdx + 1].Data!.toString("hex");
|
|
122
|
+
const symbol = prevScriptTokens[opReturnIdx + 2].Data!.toString("utf8");
|
|
123
|
+
const data: Buffer[] = [];
|
|
124
|
+
|
|
125
|
+
for (let i = opReturnIdx + 3; i < prevScriptTokens.length; i++) {
|
|
126
|
+
data.push(prevScriptTokens[i].Data!);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const script = new P2stasBuilder(to, toknenId, symbol, data);
|
|
130
|
+
|
|
131
|
+
this.Outputs.push(new OutputBuilder(script, satoshis));
|
|
132
|
+
|
|
133
|
+
return this;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
sign = () => {
|
|
137
|
+
for (const input of this.Inputs) {
|
|
138
|
+
input.sign();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return this;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
toBuffer = () => {
|
|
145
|
+
const size = this.size();
|
|
146
|
+
const buffer = Buffer.alloc(size);
|
|
147
|
+
const bufferWriter = new BufferWriter(buffer);
|
|
148
|
+
|
|
149
|
+
bufferWriter.writeUInt32(this.Version);
|
|
150
|
+
|
|
151
|
+
bufferWriter.writeVarInt(this.Inputs.length);
|
|
152
|
+
for (const input of this.Inputs) input.writeTo(bufferWriter);
|
|
153
|
+
|
|
154
|
+
bufferWriter.writeVarInt(this.Outputs.length);
|
|
155
|
+
for (const output of this.Outputs) output.writeTo(bufferWriter);
|
|
156
|
+
|
|
157
|
+
bufferWriter.writeUInt32(this.LockTime);
|
|
158
|
+
|
|
159
|
+
return buffer;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
toHex = () => this.toBuffer().toString("hex");
|
|
163
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BufferReader } from "../../buffer/buffer-reader";
|
|
2
|
+
import { reverseBuffer } from "../../buffer/buffer-utils";
|
|
3
|
+
import { Transaction } from "../../bitcoin/transaction";
|
|
4
|
+
import { TransactionInput } from "../../bitcoin/transaction-input";
|
|
5
|
+
import { TransactionOutput } from "../../bitcoin/transaction-output";
|
|
6
|
+
|
|
7
|
+
export class TransactionReader {
|
|
8
|
+
static readHex = (raw: string) =>
|
|
9
|
+
TransactionReader.readBuffer(Buffer.from(raw, "hex"));
|
|
10
|
+
|
|
11
|
+
static readBuffer = (buffer: Buffer) => {
|
|
12
|
+
const reader = new BufferReader(buffer);
|
|
13
|
+
|
|
14
|
+
const version = reader.readUInt32();
|
|
15
|
+
const inputCount = reader.readVarInt();
|
|
16
|
+
const inputs = [];
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < inputCount; i++) {
|
|
19
|
+
inputs.push(TransactionReader.readInput(reader));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const outputCount = reader.readVarInt();
|
|
23
|
+
const outputs = [];
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < outputCount; i++) {
|
|
26
|
+
outputs.push(TransactionReader.readOutput(reader));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const lockTime = reader.readUInt32();
|
|
30
|
+
|
|
31
|
+
return new Transaction(buffer, inputs, outputs, version, lockTime);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
static readInput = (reader: BufferReader): TransactionInput => {
|
|
35
|
+
const txId = reverseBuffer(reader.readChunk(32));
|
|
36
|
+
const vout = reader.readUInt32();
|
|
37
|
+
const unlockingScript = reader.readVarChunk();
|
|
38
|
+
const sequence = reader.readUInt32();
|
|
39
|
+
|
|
40
|
+
return new TransactionInput(
|
|
41
|
+
txId.toString("hex"),
|
|
42
|
+
vout,
|
|
43
|
+
unlockingScript,
|
|
44
|
+
sequence
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
static readOutput = (reader: BufferReader): TransactionOutput => {
|
|
49
|
+
const satoshis = reader.readUInt64();
|
|
50
|
+
const lockignScript = reader.readVarChunk();
|
|
51
|
+
|
|
52
|
+
return new TransactionOutput(satoshis, lockignScript);
|
|
53
|
+
};
|
|
54
|
+
}
|