@unspent/phi
Version:
a collection of anyone can spend contracts
214 lines (178 loc) • 6.12 kB
text/typescript
import type { Artifact, Utxo, NetworkProvider } from "cashscript";
import type { UtxPhiIface, ContractOptions } from "../../common/interface.js";
import { DefaultOptions } from "../../common/constant.js";
import { BaseUtxPhiContract } from "../../common/contract.js";
import { sum, toHex, binToBigInt } from "../../common/util.js";
import { artifact as v3 } from "./cash/v3.js";
export class Drip extends BaseUtxPhiContract implements UtxPhiIface {
public static c: string = "$";
private static fn: string = "drip";
public static minPayout: bigint = 164n;
constructor(
public options: ContractOptions = DefaultOptions
) {
let script: Artifact;
if (options.version === 3) {
script = v3;
} else {
throw Error("Unrecognized Drip Version");
}
super(options.network!, script, []);
this.options = options;
}
refresh(): void {
this._refresh([]);
}
static fromString(str: string, network = "mainnet"): Drip {
const p = this.parseSerializedString(str, network);
// if the contract shortcode doesn't match, error
if (!(Drip.c == p.code))
throw "non-faucet serialized string passed to faucet constructor";
if (![3].includes(p.options.version))
throw Error("faucet contract version not recognized");
if (p.args.length != 0)
throw `invalid number of arguments ${p.args.length}`;
const faucet = new Drip(p.options);
faucet.checkLockingBytecode(p.lockingBytecode);
return faucet;
}
// Create a Drip contract from an OpReturn by building a serialized string.
static fromOpReturn(
opReturn: Uint8Array | string,
network = "mainnet"
): Drip {
const p = this.parseOpReturn(opReturn, network);
// check code
if (p.code !== this.c)
throw Error(`Wrong short code passed to ${this.name} class: ${p.code}`);
// version
if (![3].includes(p.options.version))
throw Error(
`Wrong version code passed to ${this.name} class: ${p.options.version}`
);
// parse arguments
if (p.args.length != 0)
throw `invalid number of arguments ${p.args.length}`;
const faucet = new Drip(p.options);
faucet.checkLockingBytecode(p.lockingBytecode);
return faucet;
}
static async getSpendableBalance(
opReturn: Uint8Array | string,
network = "mainnet",
networkProvider: NetworkProvider,
blockHeight: number
): Promise<bigint> {
const p = this.parseOpReturn(opReturn, network);
const utxos = await networkProvider.getUtxos(p.address);
const spendableUtxos = utxos.map((u: Utxo) => {
// @ts-ignore
if (u.height !== 0) {
// @ts-ignore
if (blockHeight - u.height > period) {
return u.satoshis;
} else {
return 0n;
}
} else {
return 0n;
}
});
const spendable =
spendableUtxos.length > 0 ? spendableUtxos.reduce(sum) : 0n;
if (spendable > Drip.minPayout) {
return spendable;
} else {
return 0n;
}
}
static getExecutorAllowance(
opReturn: Uint8Array | string,
network = "mainnet"
): bigint {
const p = this.parseOpReturn(opReturn, network);
// pop the index to get to the payout
p.args.pop()!;
return binToBigInt(p.args.pop()!);
}
override toString() {
return [
`${Drip.c}`,
`${this.options!.version}`,
`${this.getLockingBytecode()}`,
].join(Drip.delimiter);
}
override asText() {
return `The dripping miner MEV faucet`;
}
override asCommand(): string {
let chipnetFlag = this.options.network == 'mainnet' ? '' : "--chipnet ";
return `unspent drip ${chipnetFlag} --version ${this.options.version}`;
}
toOpReturn(hex = false): string | Uint8Array {
const chunks = [
Drip._PROTOCOL_ID,
Drip.c,
toHex(this.options!.version!),
"0x" + this.getLockingBytecode(true),
];
return this.asOpReturn(chunks, hex);
}
getOutputLockingBytecodes(hex = true) {
hex;
return [];
}
isSpecial(): boolean {
return false;
}
async execute(
//@ts-ignore
exAddress?: string,
//@ts-ignore
fee?: bigint,
utxos?: Utxo[],
debug?: boolean
): Promise<string> {
// Filter to inputs of sufficient age
if (!utxos) utxos = await this.getUtxos(1);
debug;
const fn = this.getFunction(Drip.fn)
let txids = await Promise.all(utxos!.map(async (utxo) => await this.doDrip(utxo, fn)))
return txids!.join(",")
}
async doDrip(utxo: Utxo, fn: any) {
let balance = BigInt(utxo!.satoshis!);
let tx = fn();
let newPrincipal = balance - BigInt(balance * 4392n / 1333036486n) + 1n
const to = []
tx = tx.from([utxo]);
// if enough remains for an additional payout
if ((balance - newPrincipal) > Drip.minPayout) {
to.push({
to: this.getAddress(),
amount: newPrincipal,
})
tx.to(to)
} else if (balance > Drip.minPayout) {
newPrincipal = balance - Drip.minPayout
if(newPrincipal > 576){
to.push({
to: this.getAddress(),
amount: newPrincipal,
})
tx.to(to)
}else{
tx.withOpReturn([])
}
}
else {
tx.withOpReturn([])
}
tx
.withAge(1)
.withoutChange();
let txn = ""
txn = (await tx.send()).txid;
return txn;
}
}