Skip to content

Instantly share code, notes, and snippets.

@alex-miller-0
Last active February 3, 2024 15:20
Show Gist options
  • Save alex-miller-0/a5f4de3f811872b7272e66a3697f88de to your computer and use it in GitHub Desktop.
Save alex-miller-0/a5f4de3f811872b7272e66a3697f88de to your computer and use it in GitHub Desktop.
UTXO tokens on Ethereum

Suppose we wish to model a UTXO system on the EVM. We need to represent UTXO tokens such that all value is preserved when tokens are spent. Note: For this example, we are not concerned about the security of the system and are satisfied with giving authorities the power to create tokens (e.g. as in a plasma system).

Consider the following object:

{
  owner: <address>,
  value: <uint>,
  createdBy: <bytes32>,
  id: <bytes32>
}

We can design a system of accounting these objects as follows.

Creation

A UTXO is created by an authority in the system. The createdBy field is left blank to indicate this is an origin UTXO.

Spending

A UTXO is consumed when the owner spends it. In the event that less than the total value is spent, this creates two UTXOs: one for the spender and one for the recipient.

e.g. suppose someone owns this:

{
  owner: '0x...ab',
  value: 10,
  createdBy: '0x...0',
  id: '0x...1'
}

and spends 5 units. This creates the following two UTXOs:

{
  owner: '0x...ab',
  value: 5,
  createdBy: '0x...1',
  id: '0x...2'
}

{
  owner: '0x...ac',
  value: 5,
  createdBy: '0x...1',
  id: '0x...3'
 }

The original UTXO (id=0x...1) is consumed and deleted from the system, i.e. it cannot be spent again.

Mock Contract

This could be implemented in the following solidity contract:

struct UTXO {
 owner: address,
 value: uint,
 createdBy: bytes32,
 id: bytes32
}
mapping (bytes32 => UTXO) utxos;
uint totalSupply;
event create(address indexed owner, bytes32 indexed id, uint value); 
event spend(address indexed from, address indexed to, bytes32 oldId, bytes32 newId, uint newValue);

// Update from @maurelian: https://gist.github.com/maurelian/d34c0e6fec9a5f60147b9faf27c39295#file-utxotoken-sol-L53
function getId(address _to, bytes32 _input) internal returns(bytes32) {
  return keccak256(block.number, msg.sender, _to, _input);
}

function create(to, value) onlyAdmin() {
  bytes32 id = keccak256(block.number, msg.sender, to);
  UTXO utxo = new UTXO(to, value, bytes32(0), id);
  utxos[id] = utxo;
  totalSupply += value;
}

function spend(id, amount, to) {
  assert(utxos[id].owner == msg.sender);
  assert(utxos[id].value >= amount);
  utxo memory oldUtxo = utxos[id];
  delete utxos[id];
  bytes32 newId1 = getId(_to, _id);
  utxo spend1 = new UTXO(to, amount, id, newId1);
  utxos[newId1] = spend1;
  spend(msg.sender, to, oldUtxo.id, newId1, amount);
  if (amount < oldUtxo.value) {
    bytes32 newId2 = getId(msg.sender, _id ^ bytes32(1));
    utxo spend2 = new UTXO(msg.sender, oldUtxo.value - amount, id, newId2);
    utxos[newId2] = spend2;
    spend(msg.sender, to oldUtxo.id, newId2, oldUtxo.value - amount);
  }
}

Rationale

The above is a simple scheme to represent a UTXO token in an EVM system. This could be useful from the perspective of a plasma chain, as UTXO fraud proofs are significantly smaller (and therefore less expensive) than account-based proofs.

Interestingly, one could imagine an ERC20 wrapper on top of this UTXO scheme, where a user might burn them and use that transaction to recreate them in an ERC20 contract.

There are many potential uses and interfaces for UTXO tokens and their simplicity for proofs makes them attractive from an architectural perspective.

@kerikailiwang
Copy link

I came across this gist and found it super interesting - is there other related work out there?

@solaasan
Copy link

Likewise very interesting, at a whim I prompted chatGPT (GPT-4) about the idea of making one. The code looked a bit too human and upon a quick google I came across this and maurelian's . Neat work! The "AI" definitely heavily borrowed code from this albeit with the addition of a half signed UXTO feature for a sender to sign offchain and an onchain receiver to well, redeem.

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