libnexa-ts
Version:
A pure and powerful Nexa SDK library.
144 lines (121 loc) • 4.86 kB
text/typescript
import BN from "../../crypto/bn.extension";
import CommonUtils from "../../utils/common.utils";
import ValidationUtils from "../../utils/validation.utils";
import Script from "../script/script";
import type BufferReader from "../../encoding/bufferreader";
import BufferWriter from "../../encoding/bufferwriter";
import type { IOutput } from "./interfaces";
import Address from "../address/address";
import { networks } from "../network/network-manager";
import { AddressType } from "../address/address-formatter";
import GroupToken from "../grouptoken";
export enum OutputType {
SATOSCRIPT = 0,
TEMPLATE = 1,
INFER = 0x8000 // Not a real type: implies the type should be infered from the script at construction time
}
export default class Output implements IOutput {
public type: OutputType;
private _value!: bigint;
private _scriptPubKey!: Script;
constructor(value: bigint | number | string, scriptPubKey: Script | string, type = OutputType.INFER) {
this.type = type;
this.value = value;
this.scriptPubKey = scriptPubKey;
}
public get value(): bigint {
return this._value;
}
public set value(sats: bigint | number | string) {
sats = BigInt(sats);
ValidationUtils.validateArgument(CommonUtils.isNaturalBigInt(sats), "Output value is not a natural bigint");
this._value = sats;
}
public get scriptPubKey(): Script {
return this._scriptPubKey;
}
public set scriptPubKey(script: Script | string) {
if (this._scriptPubKey != null) {
this.type = OutputType.INFER;
}
if (script instanceof Script) {
this._scriptPubKey = script;
} else if (typeof script === 'string') {
this._scriptPubKey = Script.fromString(script);
} else {
throw new TypeError('Invalid argument type: script');
}
// in case we replace script or type wasn't passed in c'tor, we need to define the output type
if (this.type == OutputType.INFER) {
this.type = this._scriptPubKey.isPublicKeyTemplateOut() || this._scriptPubKey.isScriptTemplateOut()
? OutputType.TEMPLATE : OutputType.SATOSCRIPT;
}
}
public get address(): string | undefined {
if (this._scriptPubKey.isPublicKeyTemplateOut()) {
return Address.fromScriptTemplate(this._scriptPubKey.getTemplateHash(), this._scriptPubKey.getConstraintHash()).toString();
} else if (this._scriptPubKey.isPublicKeyHashOut()) {
return new Address(this._scriptPubKey.getPublicKeyHash(), networks.defaultNetwork, AddressType.PayToPublicKeyHash).toString();
} else if (this._scriptPubKey.isScriptTemplateOut()) {
return Address.fromScriptTemplate(this._scriptPubKey.getTemplateHash(), this._scriptPubKey.getConstraintHash(), this._scriptPubKey.getVisibleArgs()).toString();
}
return undefined;
}
public get groupData(): { groupId: string, groupAmount: string } | undefined {
try {
let groupData = this._scriptPubKey.getGroupData();
if (groupData) {
return {
groupId: new Address(groupData.groupId, networks.defaultNetwork, AddressType.GroupIdAddress).toString(),
groupAmount: GroupToken.getAmountValue(groupData.groupAmount).toString()
};
}
} catch {
return undefined;
}
}
public invalidValue(): string | false {
if (this.value > BigInt(Number.MAX_SAFE_INTEGER)) {
return 'transaction txout value greater than max safe integer';
}
if (this.value < 0n) {
return 'transaction txout negative';
}
return false;
}
public toObject(): IOutput {
let group = this.groupData;
return {
type: this.type,
value: this.value.toString(),
scriptPubKey: this.scriptPubKey.toHex(),
address: this.address,
groupId: group?.groupId,
groupAmount: group?.groupAmount
};
}
public toJSON = this.toObject;
public static fromObject(data: IOutput): Output {
ValidationUtils.validateArgument(typeof data === 'object' && data !== null, "data", "Unrecognized argument for Output")
return new Output(data.value, data.scriptPubKey, data.type);
}
public inspect(): string {
return `<Output (type: ${this.type}) (${this.value.toString()} sats) ${this.scriptPubKey.inspect()}>`;
}
public static fromBufferReader(br: BufferReader): Output {
let type = br.readVarintNum();
let value = br.readUInt64LEBN();
let size = br.readVarintNum();
let scriptBuf = size !== 0 ? br.read(size) : new Uint8Array();
return new Output(value.toBigInt(), Script.fromBuffer(scriptBuf), type);
}
public toBufferWriter(writer?: BufferWriter): BufferWriter {
if (!writer) {
writer = new BufferWriter();
}
writer.writeUInt8(this.type);
writer.writeUInt64LEBN(BN.fromBigInt(this.value));
writer.writeVarLengthBuf(this.scriptPubKey.toBuffer());
return writer;
}
}