A simple token burner contract

pragma cashscript ^0.9.0;

// Burner - where tokens enter the void
//
// vi: ft=solidity

contract Burner() {
    function burn() {
        require(tx.outputs.length == 1);
        require(tx.outputs[0].tokenCategory == 0x);
    }
}

The purpose of the contract is to have a simple way to burn tokens by sendinging them to an address.

This contract enforces this by requiring that spends from it must contain one output and this output must not contain tokens.

I hacked together a simple frontendhere : https://tokenburner.cash

4 Likes

Neat. Worth noting the economics you designed which incentivizes anyone to collect and execute the contract. Any way to resolve the contention issue, such as a way to isolate the redeemer to miners?

Also, consider upgrading to a newer version of cashscript to get p2sh32!

Any way to resolve the contention issue, such as a way to isolate the redeemer to miners?

We could check that first byte of tx.outputs[0].lockingBytecode is OP_RETURN

2 Likes

Contract has been updated with your suggestion :slight_smile:

contract Burner() {
    function burn() {
        require(tx.outputs.length == 1);
        require(tx.outputs[0].tokenCategory == 0x);
        bytes requiredPrefix = 0x6a /* op_return */;
        bytes prefix = tx.outputs[0].lockingBytecode.split(1)[0];
        require(prefix == requiredPrefix);
    }
}
1 Like

I think not using variables not splitting the op_return might save three (3) sats…

contract Burner() {
    function burn() {
        // Require a single output
        // OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY 
        require(tx.outputs.length == 1);

        // Without tokens
        // OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY
        require(tx.outputs[0].tokenCategory == 0x);

        // As an empty OP_RETURN
        // OP_0 OP_OUTPUTBYTECODE 6a OP_EQUAL
        require(tx.outputs[0].lockingBytecode == 0x6a);
    }
}


Over the proposed improvement.

contract Burner() {
    function burn() {
        // Require a single output
        // OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY 
        require(tx.outputs.length == 1);

        // Without tokens
        // OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY 
        require(tx.outputs[0].tokenCategory == 0x);

        // OP_RETURN ...
        // 6a 
        bytes requiredPrefix = 0x6a /* op_return */;

        // .. the locking bytecode ...
        // OP_0 OP_OUTPUTBYTECODE OP_1 OP_SPLIT 
        bytes prefix = tx.outputs[0].lockingBytecode.split(1)[0];

        // ... equal to is.
        // OP_DROP OP_EQUAL
        require(prefix == requiredPrefix);
    }
}

So we’re going to need a miner now? What have you done? :laughing:

Anyone can construct the TX and post it, but the sats can either go to miner or get burned.

Could require the output value be 0, so everything has to go to fee.

contract Burner() {
    function burn() {
        // Require a single output
        // OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY 
        require(tx.outputs.length == 1);

        // Without BCH
        // OP_0 OP_OUTPUTVALUE OP_0 OP_NUMEQUALVERIFY
        require(tx.outputs[0].value == 0);

        // Without tokens
        // OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY
        require(tx.outputs[0].tokenCategory == 0x);

        // As an empty OP_RETURN
        // OP_0 OP_OUTPUTBYTECODE 6a OP_EQUAL
        require(tx.outputs[0].lockingBytecode == 0x6a);
    }
}
1 Like