Skip to content

Instantly share code, notes, and snippets.

@msinkec
Last active December 16, 2023 03:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save msinkec/6389a7943ed054fa5c74ba8f79bf730e to your computer and use it in GitHub Desktop.
Save msinkec/6389a7943ed054fa5c74ba8f79bf730e to your computer and use it in GitHub Desktop.
Lock to Mint BSV-20 tokens using sCrypt

Implementing "Lock to Mint" Mechanism using sCrypt for Minting BSV-20 Tokens

Disclaimer: This code is provided as an example only and is not intended for production use. The author of this code and any parties involved in its distribution are not liable for any lost funds or damages arising from the use of this code. Users should conduct their own testing and due diligence before implementing or deploying this code in any environment.


To implement a "lock to mint" mechanism using sCrypt for minting BSV-20 (V2) tokens, the provided source code outlines a smart contract approach that integrates with the BSV-20 V2 standard. This method does not require an extended indexer and is compatible with the standard BSV-20 tokens, unlike the LRC-20 approach.

import { BSV20V2 } from 'scrypt-ord'
import {
    Addr,
    assert,
    ByteString,
    hash256,
    int2ByteString,
    method,
    prop,
    slice,
    toByteString,
    Utils,
} from 'scrypt-ts'

export class BSV20LockToMint 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 > transferAmt) {
            // 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')
    }
}

Summary of Key Functionalities and Mechanisms in the Smart Contract

For the seamless integration of ordinals functionality extends the BSV20V2 class from the scrypt-ord package.

Properties:

  • supply: Tracks the total supply of "unminted" tokens.
  • hodlRate: The amount of satoshis that must be locked to mint a single token.
  • hodlDeadline: The minimum deadline for locking satoshis to mint new tokens.
  • lockupScript: A locking script defining the conditions of the satoshi lockup. In our case, it is compiled from this contract.

Constructor:

Initializes the contract with parameters like token ID, symbol, maximum supply, decimals, current supply, hodl rate, hodl deadline, and the lockup script.

Minting Method (mint):

  • Checks if tokens are available for minting and updates the supply.
  • Constructs and enforces the transaction outputs, including:
    • A state output indicating the remaining token supply.
    • A transfer output for the minted tokens to the recipient.
    • An output to lock the required satoshis according to the hodlRate.

Lockup Mechanism:

  • The lockupScriptFinal is crafted by combining parts of the lockupScript with the lock address and the hodlDeadline.
  • The satoshis are locked in an output locked by this script, ensuring that they remain locked until the specified deadline.

Parallel Scaling

There have been instances where clients experience race conditions upon token minting, where a single instance of a contract, like the one described above, is deployed. This mainly happens due to many simultaneous contract calls via different network nodes.

This problem can be mitigated by splitting the entire minted supply into multiple contract instances. Suppose you mint a token X with a supply of 1000. You can simply mint the entire supply to a P2PKH and then split it into multiple outputs, each holding, say, 10 tokens. These outputs will each be instances of the aforementioned lock smart contract.

@EmanueleNusca
Copy link

<3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment