@unspent/phi
Version:
a collection of anyone can spend contracts
183 lines (156 loc) • 5.48 kB
text/typescript
import { binToHex, hexToBin, lockingBytecodeToCashAddress } from "@bitauth/libauth";
import { Artifact } from "@cashscript/utils";
import { NetworkProvider, Network } from "cashscript";
import { nameMap, contractMap, CodeType } from "../contract/constant.js";
import { decodeNullDataScript } from "./util.js";
import { BaseUtxPhiContract } from "./contract.js";
import { PsiNetworkProvider } from "@unspent/psi";
type ContractType = typeof contractMap[keyof typeof contractMap];
export function parseOpReturn(serialized: string | Uint8Array, network: Network = 'mainnet') {
if (typeof serialized === "string") {
serialized = hexToBin(serialized);
}
const data = BaseUtxPhiContract.parseOpReturn(serialized, network);
return {
name: nameMap[data.code as CodeType] as string,
opReturn: serialized,
...data,
};
}
export function parseOutputs(serialized: string | Uint8Array) {
if (typeof serialized === "string") serialized = hexToBin(serialized);
return BaseUtxPhiContract.parseOutputs(serialized);
}
export function opReturnToInstance(
serialized: string | Uint8Array,
network?: string
): InstanceType<ContractType> | undefined {
if (typeof serialized === "string") {
serialized = hexToBin(serialized);
}
const serializedBinChunks = decodeNullDataScript(serialized);
const contractCode = binToHex(serializedBinChunks[1]!);
const code = String.fromCharCode(parseInt(contractCode, 16)) as CodeType;
const instance = contractMap[code].fromOpReturn(serialized, network);
if(instance.isSpecial()) throw("Too Special: " + serialized)
return instance;
}
export function opReturnToExecutorAllowance(
serialized: string | Uint8Array,
network?: string
): bigint {
if (typeof serialized === "string") {
serialized = hexToBin(serialized);
}
const serializedBinChunks = decodeNullDataScript(serialized);
const contractCode = binToHex(serializedBinChunks[1]!);
const code = String.fromCharCode(parseInt(contractCode, 16)) as CodeType;
const exAllowance = contractMap[code].getExecutorAllowance(serialized, network);
return exAllowance;
}
export async function opReturnToSpendableBalance(
serialized: string | Uint8Array,
network:Network = Network.MAINNET,
networkProvider?: NetworkProvider,
blockHeight?: number
): Promise<bigint> {
if (typeof serialized === "string") {
serialized = hexToBin(serialized);
}
const serializedBinChunks = decodeNullDataScript(serialized);
const contractCode = binToHex(serializedBinChunks[1]!);
const code = String.fromCharCode(parseInt(contractCode, 16)) as CodeType;
if (!networkProvider) networkProvider = new PsiNetworkProvider(network);
if (!blockHeight) blockHeight = await networkProvider!.getBlockHeight();
try{
const spendableBalance = await contractMap[code].getSpendableBalance(
serialized,
network,
networkProvider,
blockHeight
);
return spendableBalance;
}catch(e:any){
console.log(`error getting balance for ${binToHex(serialized)}`)
return 0n
}
}
export async function opReturnToBalance(
serialized: string | Uint8Array,
network = Network.MAINNET,
networkProvider?: PsiNetworkProvider,
blockHeight?: number
): Promise<bigint> {
if (typeof serialized === "string") {
serialized = hexToBin(serialized);
}
const serializedBinChunks = decodeNullDataScript(serialized);
const address = lockingBytecodeToCashAddress({bytecode: serializedBinChunks.pop()!, prefix:"bitcoincash"})
if(typeof address!=="string") throw Error("couldn't decode cashaddr")
if (!networkProvider) networkProvider = new PsiNetworkProvider(network);
if (!blockHeight) blockHeight = await networkProvider.getBlockHeight();
const balance = await BaseUtxPhiContract.getBalance(
address,
networkProvider
);
return balance;
}
export function opReturnToSerializedString(
serialized: string | Uint8Array,
network?: string
): string | undefined {
const instance = opReturnToInstance(serialized, network);
if (instance) {
return instance.toString();
} else {
return;
}
}
export function stringToInstance(
serialized: string,
network: string
): InstanceType<ContractType> | undefined {
const code = serialized[0]! as CodeType;
try {
const instance = contractMap[code].fromString(serialized, network);
return instance;
} catch (e) {
console.warn(`Couldn't parse serialized contract, ${e}`);
return;
}
}
export function castConstructorParametersFromArtifact(
parameters: string[],
artifact: Artifact
) {
const result = [];
const inputs = artifact.constructorInputs;
parameters.forEach(function (value, i) {
const abiInput = inputs[i]!;
let parsedVal = undefined;
if (abiInput.type.startsWith("bytes")) {
if (typeof value === "string") {
if (value.includes(",")) {
parsedVal = Uint8Array.from(
value.split(",").map((vStr) => parseInt(vStr))
);
} else {
parsedVal = hexToBin(value);
}
} else {
throw Error(`Couldn't parse ${value} from string to bytes`);
}
} else if (abiInput.type === "int") {
parsedVal = parseInt(value);
} else if (abiInput.type === "boolean") {
parsedVal = Boolean(value);
} else {
throw Error(`Couldn't parse type ${abiInput.type}`);
}
result.push({
name: abiInput.name,
cashScriptType: abiInput.type,
value: parsedVal,
});
});
}