diff --git a/src/contracts/hodlocker.ts b/src/contracts/hodlocker.ts new file mode 100644 index 00000000..5d784a4c --- /dev/null +++ b/src/contracts/hodlocker.ts @@ -0,0 +1,41 @@ +import { + method, + prop, + SmartContract, + assert, + PubKeyHash, + Sig, + PubKey, + hash160, +} from 'scrypt-ts' + +export class Lockup extends SmartContract { + @prop() + lockUntilHeight: bigint + + @prop() + pkhash: PubKeyHash + + constructor(pkhash: PubKeyHash, lockUntilHeight: bigint) { + super(...arguments) + assert(lockUntilHeight < 500000000, 'must use blockHeight locktime') + this.lockUntilHeight = lockUntilHeight + this.pkhash = pkhash + } + + @method() + public redeem(sig: Sig, pubkey: PubKey) { + assert(this.ctx.locktime < 500000000, 'must use blockHeight locktime') + assert(this.ctx.sequence < 0xffffffff, 'must use sequence locktime') + assert( + this.ctx.locktime >= this.lockUntilHeight, + 'lockUntilHeight not reached' + ) + assert( + hash160(pubkey) == this.pkhash, + 'public key hashes are not equal' + ) + // Check signature validity. + assert(this.checkSig(sig, pubkey), 'signature check failed') + } +} diff --git a/src/contracts/lrc20V2.ts b/src/contracts/lrc20V2.ts new file mode 100644 index 00000000..dfc3a802 --- /dev/null +++ b/src/contracts/lrc20V2.ts @@ -0,0 +1,89 @@ +import { BSV20V2 } from 'scrypt-ord' +import { + Addr, + assert, + ByteString, + hash256, + int2ByteString, + method, + prop, + slice, + toByteString, + Utils, +} from 'scrypt-ts' + +export class Lrc20V2 extends BSV20V2 { + @prop(true) + supply: bigint + + // Amount of sats to lock up in order to mint a single token. + @prop() + hodlRate: bigint + + // Minimum deadline until you have to lock to mint new + // tokens. + @prop() + hodlDeadline: bigint + + // Hodl lock script. + @prop() + lockupScript: ByteString + + constructor( + id: ByteString, + sym: ByteString, + max: bigint, + dec: bigint, + supply: bigint, + hodlRate: bigint, + hodlDeadline: bigint, + lockupScript: ByteString + ) { + super(id, sym, max, dec) + this.init(...arguments) + + this.supply = supply + this.hodlRate = hodlRate + this.hodlDeadline = hodlDeadline + this.lockupScript = lockupScript + } + + @method() + public mint(ordinalAddress: Addr, lockAddress: Addr, amount: bigint) { + let outputs = toByteString('') + let transferAmt = amount + + if (this.supply > 0n) { + // If there are still tokens left, then update supply and + // build state output inscribed with leftover tokens. + this.supply -= transferAmt + outputs += this.buildStateOutputFT(this.supply) + } else { + // If not, then transfer all the remaining supply. + transferAmt = this.supply + } + + // Build FT P2PKH output to dest paying specified amount of tokens. + outputs += BSV20V2.buildTransferOutput( + ordinalAddress, + this.id, + transferAmt + ) + + // Make sure satoshis are locked. + const lockupScriptFinal = + slice(this.lockupScript, 0n, 114n) + + lockAddress + + int2ByteString(this.hodlDeadline, 4n) + + slice(this.lockupScript, 138n) + outputs += Utils.buildOutput( + lockupScriptFinal, + transferAmt * this.hodlRate + ) + + // Build change output. + outputs += this.buildChangeOutput() + + assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs mismatch') + } +}