Skip to main content

Continuous Upgrades to the NOTE Protocol

· 6 min read

Since the public release of its Proof of Concept indexer in February 2024, the NOTE protocol has garnered widespread attention. Following feedback and demands from users, the protocol has been continuously upgraded.

As of April 4th, the following upgrades have been implemented:

  • The Payload data area has been fully expanded, meaning that Payload data can now be stored either in the unlocking script or in the redemption script.
  • Added a Burn method to N20. Tokens can now be burned.
  • The off-chain contract execution environment has been enriched with a plethora of environmental variables, including block information such as block time, block hash, block height, as well as transaction information like transaction hash, all inputs and outputs. Ticker information, such as the total mint amount, etc., has also been included.

The indexer code has undergone extensive refactoring.

One of the goals of this upgrade is to enable anyone to participate in the development of NOTE smart contracts. This allows for the creation of unique smart contracts that can be published to the NOTE indexer, facilitating the issuance of digital assets. A comprehensive tutorial on contract creation is currently being developed.

The capabilities of the NOTE protocol can be seen in the POW mining contract released in February.

We've added some comments to help everyone understand:

// Each contract is a subclass of SmartContract, and its member variables are read-only and need to be decorated with @prop().
export class N20_Pow extends SmartContract {
@prop()
readonly tick: ByteString

@prop()
readonly max: bigint

@prop()
readonly lim: bigint

@prop()
readonly dec: bigint

@prop()
readonly bitwork: ByteString

@prop()
readonly start: bigint

// If the contract includes member variables, they need to be initialized using a constructor.
constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, bitwork: ByteString, start: bigint) {
super(...arguments)
// The identifier of the digital asset, its name.
this.tick = tick
// The maximum issuance of the digital asset, if there is no limit, it can be specified as 0.
this.max = max
// The mining limit per transaction, it cannot exceed the maximum value.
this.lim = lim
// The decimal unit of the digital asset, for example, if dec=8, then 1 token should be followed by 8 zeros, all quantity values amt, including max and lim, are affected by this. 100000000 represents 1.00000000.
this.dec = dec
// Mining difficulty, the required leading characters of each transaction's hash256.
this.bitwork = bitwork
// The starting height of the mining contract, used to prevent pre-mining.
this.start = start
}

// The block limit algorithm for mining, needs to be decorated with @method().
@method()
getBlockLimit(height: bigint): bigint {
assert(height >= this.start, 'Block height is too low')
// Halving occurs every 1000 blocks, the number of halvings is determined by the block height minus the starting height.
let halvings = (height - this.start) / 1000n
// A maximum of 7 halvings.
halvings = halvings > 7n ? 7n : halvings
// The mining quantity limit is halved with each halving. Here, the binary rshift operator is used to perform the division by 2 operation.
const subsidy = rshift(this.lim, halvings)
return subsidy
}

// The quantity limit algorithm for mining, needs to be decorated with @method().
@method()
getAmountLimit(currentMined: bigint): bigint {
// The quantity limit starts from how much of the total amount has already been mined.
let miningAmount = this.lim
let threshold = this.max / 2n

// Loop 7 times, each loop, if the current mined amount reaches the threshold, then the mining amount is halved, and the threshold is updated.
for (let halving = 0n; halving < 7n; halving++) {
if (currentMined >= threshold) {
miningAmount /= 2n // Halve the mining amount
threshold += rshift(this.max, halving + 2n) // Update the next threshold
}
}

return miningAmount
}

@method()
public mint(tick: ByteString, amt: bigint, total: bigint, height: bigint, tx: ByteString) {
// Check if the transaction's hash256 leading characters match the mining contract's difficulty requirement.
const txid = hash256(tx)
assert(slice(txid, 0n, len(this.bitwork)) == this.bitwork, 'not match target')
// Check if the total mining amount exceeds the maximum issuance.
assert(this.max == 0n || total <= this.max, 'Over max')
assert(tick == this.tick, 'Tick does not match')
// Check if the mining amount exceeds the block limit and the total halving limit.
const limit1 = this.getBlockLimit(height)
const limit2 = this.getAmountLimit(total)
const limit = limit1 > limit2 ? limit2 : limit1
assert(amt <= limit && amt <= this.max - total, 'Amount check failed')
}

// The method for transferring Tokens, needs to be decorated with @method().
@method()
public transfer(tick: ByteString) {
assert(tick == this.tick, 'Tick does not match')
}
}

there is a bug in the above code about max=0. if you want to use it in your assets, please DONT set max=0.

This upgrade added the Burn method, allowing for the burning of Tokens. A new example is as follows.

// This example does not use a constructor but instead uses a static setting of parameters, because the values are entirely within the contract and cannot be reused for other digital assets.
export class N20_Joss extends SmartContract {
// Joss paper: A type of paper made to resemble money and burned in front of deities.
@prop()
static tick: ByteString = toByteString('JOSS', true)

// No issuance limit.
@prop()
static max: bigint = 0n

// Can issue 10,000 at a time.
@prop()
static lim: bigint = 10000n

// No decimal places.
@prop()
static dec: bigint = 0n

@method()
public mint(tick: ByteString, amt: bigint) {
assert(tick == N20_Joss.tick, 'Tick does not match')
// The issuance amount cannot exceed the limit of 10,000.
assert(amt == N20_Joss.lim, 'Limit does not match')
}

@method()
public transfer() {
// Cannot transfer.
assert(false)
}

@method()
public burn(tick: ByteString) {
// Can be burned.
assert(tick == N20_Joss.tick, 'Tick does not match')
}
}

The N20_Joss contract was released at https://mempool.space/tx/c2cda4c9da3a91bd245d9f6250b5ca8f2ec81d50c14821e87a033fde1f3b5561

Support for smart contracts is the biggest difference between the NOTE protocol and other protocols. The NOTE wallet and indexer create an execution environment for smart contracts, allowing users to write their own smart contracts. For basic knowledge about smart contracts, you can refer to:

Why Bitcoin smart contracts are Turing complete:

A tutorial on smart contract development for the NOTE protocol is being created, so stay tuned.