@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
283 lines (282 loc) • 9.9 kB
JavaScript
import { BufferHelper } from '../utils/BufferHelper.js';
import { ADDRESS_BYTE_LENGTH, I128_BYTE_LENGTH, U128_BYTE_LENGTH, U16_BYTE_LENGTH, U256_BYTE_LENGTH, U32_BYTE_LENGTH, U64_BYTE_LENGTH, U8_BYTE_LENGTH, } from '../utils/lengths.js';
import { BinaryReader } from './BinaryReader.js';
export class BinaryWriter {
constructor(length = 0) {
this.currentOffset = 0;
this.buffer = this.getDefaultBuffer(length);
}
static estimateArrayOfBufferLength(values) {
if (values.length > 65535)
throw new Error('Array size is too large');
let totalLength = U16_BYTE_LENGTH;
for (let i = 0; i < values.length; i++) {
totalLength += U32_BYTE_LENGTH + values[i].length;
}
return totalLength;
}
writeU8(value) {
if (value > 255)
throw new Error('u8 value is too large.');
this.allocSafe(U8_BYTE_LENGTH);
this.buffer.setUint8(this.currentOffset++, value);
}
writeU16(value, be = true) {
if (value > 65535)
throw new Error('u16 value is too large.');
this.allocSafe(U16_BYTE_LENGTH);
this.buffer.setUint16(this.currentOffset, value, !be);
this.currentOffset += 2;
}
writeU32(value, be = true) {
if (value > 4294967295)
throw new Error('u32 value is too large.');
this.allocSafe(U32_BYTE_LENGTH);
this.buffer.setUint32(this.currentOffset, value, !be);
this.currentOffset += 4;
}
writeU64(value, be = true) {
if (value > 18446744073709551615n)
throw new Error('u64 value is too large.');
this.allocSafe(U64_BYTE_LENGTH);
this.buffer.setBigUint64(this.currentOffset, value, !be);
this.currentOffset += 8;
}
writeSelector(value) {
this.writeU32(value, true);
}
writeBoolean(value) {
this.writeU8(value ? 1 : 0);
}
writeI128(bigIntValue, be = true) {
if (bigIntValue > 170141183460469231731687303715884105727n ||
bigIntValue < -170141183460469231731687303715884105728n) {
throw new Error('i128 value is too large.');
}
this.allocSafe(I128_BYTE_LENGTH);
const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue, I128_BYTE_LENGTH);
if (bytesToHex.byteLength !== I128_BYTE_LENGTH) {
throw new Error(`Invalid i128 value: ${bigIntValue}`);
}
if (be) {
for (let i = 0; i < bytesToHex.byteLength; i++) {
this.writeU8(bytesToHex[i]);
}
}
else {
for (let i = bytesToHex.byteLength - 1; i >= 0; i--) {
this.writeU8(bytesToHex[i]);
}
}
}
writeU256(bigIntValue, be = true) {
if (bigIntValue >
115792089237316195423570985008687907853269984665640564039457584007913129639935n &&
bigIntValue < 0n) {
throw new Error('u256 value is too large or negative.');
}
this.allocSafe(U256_BYTE_LENGTH);
const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue);
if (bytesToHex.byteLength !== U256_BYTE_LENGTH) {
throw new Error(`Invalid u256 value: ${bigIntValue}`);
}
if (be) {
for (let i = 0; i < bytesToHex.byteLength; i++) {
this.writeU8(bytesToHex[i]);
}
}
else {
for (let i = bytesToHex.byteLength - 1; i >= 0; i--) {
this.writeU8(bytesToHex[i]);
}
}
}
writeU128(bigIntValue, be = true) {
if (bigIntValue > 340282366920938463463374607431768211455n && bigIntValue < 0n) {
throw new Error('u128 value is too large or negative.');
}
this.allocSafe(U128_BYTE_LENGTH);
const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue, U128_BYTE_LENGTH);
if (bytesToHex.byteLength !== U128_BYTE_LENGTH) {
throw new Error(`Invalid u128 value: ${bigIntValue}`);
}
if (be) {
for (let i = 0; i < bytesToHex.byteLength; i++) {
this.writeU8(bytesToHex[i]);
}
}
else {
for (let i = bytesToHex.byteLength - 1; i >= 0; i--) {
this.writeU8(bytesToHex[i]);
}
}
}
writeBytes(value) {
this.allocSafe(value.byteLength);
for (let i = 0; i < value.byteLength; i++) {
this.writeU8(value[i]);
}
}
writeString(value) {
const encoder = new TextEncoder();
const bytes = encoder.encode(value);
this.allocSafe(bytes.length);
this.writeBytes(bytes);
}
writeStringWithLength(value) {
const encoder = new TextEncoder();
const bytes = encoder.encode(value);
this.allocSafe(U32_BYTE_LENGTH + bytes.length);
this.writeU32(bytes.length);
this.writeBytes(bytes);
}
writeAddress(value) {
this.verifyAddress(value);
this.writeBytes(value);
}
getBuffer(clear = true) {
const buf = new Uint8Array(this.buffer.byteLength);
for (let i = 0; i < this.buffer.byteLength; i++) {
buf[i] = this.buffer.getUint8(i);
}
if (clear)
this.clear();
return buf;
}
reset() {
this.currentOffset = 0;
this.buffer = this.getDefaultBuffer(4);
}
toBytesReader() {
return new BinaryReader(this.getBuffer());
}
getOffset() {
return this.currentOffset;
}
setOffset(offset) {
this.currentOffset = offset;
}
clear() {
this.currentOffset = 0;
this.buffer = this.getDefaultBuffer();
}
allocSafe(size) {
if (this.currentOffset + size > this.buffer.byteLength) {
this.resize(size);
}
}
writeAddressValueTuple(map, be = true) {
if (map.size > 65535)
throw new Error('Map size is too large');
this.writeU16(map.size, be);
const keys = Array.from(map.keys());
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = map.get(key);
if (value === null || value === undefined)
throw new Error('Value not found');
this.writeAddress(key);
this.writeU256(value, be);
}
}
writeBytesWithLength(value) {
this.writeU32(value.length);
this.writeBytes(value);
}
writeArrayOfBuffer(values, be = true) {
const totalLength = BinaryWriter.estimateArrayOfBufferLength(values);
this.allocSafe(totalLength);
this.writeU16(values.length, be);
for (let i = 0; i < values.length; i++) {
this.writeU32(values[i].length, be);
this.writeBytes(values[i]);
}
}
writeAddressArray(value) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length);
for (let i = 0; i < value.length; i++) {
this.writeAddress(value[i]);
}
}
writeU32Array(value, be = true) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length, be);
for (let i = 0; i < value.length; i++) {
this.writeU32(value[i], be);
}
}
writeU256Array(value, be = true) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length, be);
for (let i = 0; i < value.length; i++) {
this.writeU256(value[i], be);
}
}
writeU128Array(value, be = true) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length, be);
for (let i = 0; i < value.length; i++) {
this.writeU128(value[i], be);
}
}
writeStringArray(value) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length);
for (let i = 0; i < value.length; i++) {
this.writeStringWithLength(value[i]);
}
}
writeU16Array(value, be = true) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length, be);
for (let i = 0; i < value.length; i++) {
this.writeU16(value[i], be);
}
}
writeU8Array(value) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length);
for (let i = 0; i < value.length; i++) {
this.writeU8(value[i]);
}
}
writeU64Array(value, be = true) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length, be);
for (let i = 0; i < value.length; i++) {
this.writeU64(value[i], be);
}
}
writeBytesArray(value) {
if (value.length > 65535)
throw new Error('Array size is too large');
this.writeU16(value.length);
for (let i = 0; i < value.length; i++) {
this.writeBytesWithLength(value[i]);
}
}
verifyAddress(pubKey) {
if (pubKey.byteLength > ADDRESS_BYTE_LENGTH) {
throw new Error(`Address is too long ${pubKey.byteLength} > ${ADDRESS_BYTE_LENGTH} bytes`);
}
}
resize(size) {
const buf = new Uint8Array(this.buffer.byteLength + size);
for (let i = 0; i < this.buffer.byteLength; i++) {
buf[i] = this.buffer.getUint8(i);
}
this.buffer = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
}
getDefaultBuffer(length = 0) {
return new DataView(new ArrayBuffer(length));
}
}