-
Notifications
You must be signed in to change notification settings - Fork 7
Examples
The examples below constitute meaningful coverage over the scope of protorunes and its interactions with native runes. The final example on this page demonstrates a complete example where we compose protocol messages into one transaction script, constructed for a familiar AMM use case. The compactness of the final OP_RETURN buffers we construct is a proof on-paper that runes extensibility CAN work and it CAN enable a DeFi-like experience on Bitcoin, when we simply define constraints and leverage recursion of leb128 message structures on OP_RETURN.
For inputs containing 500 UNCOMMON•GOODS, let's protoburn 100 of them to subprotocol ID 0x4e20
, and we want it to be spendable by bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
. Outputs should be split to OP_RETURN on vout 0, 100 UNCOMMON•GOODS (subprotocol 0x4e20) on vout 1, and 400 UNCOMMON•GOODS runes refunded to vout 2.
Inputs:
- 500 UNCOMMON•GOODS
- Fees
Outputs:
- OP_RETURN Runestone output with 2 edicts and a Protostone
- p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats
OP_RETURN output:
[ OP_RETURN, OP_13, Runestone bytes as data push]
Protostone layout:
{
[Burn (83)]: u128(0x4e20),
[Pointer (91)]: u128(2)
}
Protocol tag leb128[]:
(Protocol ID -> number of leb128s for Protostone -> leb128[length])
[ u128(13), u128(4), u128(83), u128(0x4e20), u128(91), uint128(2) ]
Protocol field u128(0x0d0453a09c015b02)
Runestone layout:
{
[Edict (0)]: [{
Height: 0,
Txindex: 1,
Amount: 100,
Output: 0
}],
[Pointer (22)]: 0x01,
[Protocol (16383)]: u128(0x0d0453a09c015b02)
}
Encoded:
0x1601ff7f82b685e089f494820d0001006400
(18 bytes)
Full OP_RETURN output:
0x6a5d121601ff7f82b685e089f494820d0001006400
In this example, we will use a hypothetical AMM with protocol ID 0x4e20 to trade 100 units of UNCOMMON•GOODS for a minOut of 150000 RUN•OF•THE•MILL•GOODS (RuneId 1000000:1) and for this transaction our AMM runtime interprets a Message bytearray of [ UTF-8('S') . leb128[3] (output RuneId . output minOut) ]
or 0x535741508094ebdc03f09309.
We'll say our inputs already hold a balance of UNCOMMON•GOODS as protorunes and the Runestone thus only holds a Protocol field with the Protostone representing the transaction. Our outputs simply represent a success output which is the target of Protostone Pointer and a failure output which is the target of Protostone Refund.
We are transacting from bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
and outputs will be spendable by this address as well.
Inputs:
- 100 UNCOMMON•GOODS (subprotocol 0x4e20)
- Fees
Outputs:
- OP_RETURN Runestone output with 1 Protostone
- p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats
OP_RETURN output:
[ OP_RETURN, OP_13, Runestone bytes as data push]
Protostone layout:
{
[Message (81)]: u8[] = 0x535741508094ebdc03f09309,
[Pointer (91)]: u128(1),
[Refund (93)]: u128(2)
}
Protocol field leb128[]:
(Protocol ID -> number of leb128s for Protostone -> leb128[length]) (Message field u128s as well as Protocol field u128s have a max size of 15 bytes and are concatenated once decoded)
[ u128(0x4e20), u128(6), u128(81), u128(0x535741508094ebdc03f09309), u128(91), uint128(1), u128(93), u128(2) ]
Protocol field u8[] = 0xa09c01065189a6c29fc0fbbaca80a185bab50a5b015d02
Chunks are taken in sets of 15 bytes as so:
Message u128[] = [ u128(0xa09c01065189a6c29fc0fbbaca80a1, u128(0x85bab50a5b015d02) ]
Runestone layout:
{
[Protocol (16383)]: [ u128(0xa09c01065189a6c29fc0fbbaca80a1, u128(0x85bab50a5b015d02) ]
}
Encoded:
0xff7fa181aad6bb9ff0cfc2cda68ce5a080cea001ff7f82ba85d8a5a1addd8501
(32 bytes)
Full OP_RETURN output:
0x6a5d20ff7fa181aad6bb9ff0cfc2cda68ce5a080cea001ff7f82ba85d8a5a1addd8501
Inputs:
- 100 UNCOMMON•GOODS (subprotocol 0x4e20)
- Fees
Outputs:
- OP_RETURN Runestone output with 2 Protostones (first protostone is a protoburn, second is the protomessage)
- p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats
Note: these are described by the protomessage tests
In the following example, we will perform the following actions:
- protoburn 100 UNCOMMON•GOODS
- protoburn 100 RUN•OF•THE•MILL•GOODS
- swap 100 RUN•OF•THE•MILL•GOODS to UNCOMMON•GOODS
- pair 100 RUN•OF•THE•MILL•GOODS and swap output of UNCOMMON•GOODS to mint LP token
We will say UNCOMMON•GOODS is RuneId 2000000:1 and the Message bytearray to mint LP is [ UTF-8('M') . leb128 minOut) ]
The Message bytearray to swap is still [ UTF-8('S') . leb128[3] (output RuneId . output minOut) ]
The subprotocol ID is 0x4e20 for our AMM, and we want outputs to be spendable by bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
. Outputs should be split to OP_RETURN on vout 0, surplus runes above what we want to pay into our trade should end up on vout 1, LP token should be output to vout 2 on success, result of LP mint failure should end up on vout 3, and result of swap failure should be output to vout 4.
Example Inputs:
- 1000 UNCOMMON•GOODS
- 1000 RUN•OF•THE•MILL•GOODS
- Fees
Outputs:
- OP_RETURN Runestone output with Pointer field set, 3 Edicts (one is a 0 transfer to skip a protoburn), and 4 Protostones in Protocol field
- Protostones encode in the following order i. protoburn for RUN•OF•THE•MILL•GOODS directly to protomessage for SWAP (S) ii. protoburn for UNCOMMON•GOODS directly to protomessage for MINT (M) iii. protomessage for MINT (M) (shadow vout 7) iv. protomessage for SWAP (S) (shadow vout 8)
- Order of edicts is such that SWAP (S) message is evaluated first, then MINT (M) is evaluated with result of SWAP (S) as well as parent protoburn
- p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats (Runestone Pointer) - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats (LP protorunes on success) - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats (protomessage mint failure refund) - p2tr output spendable by
bc1pkgdwl8qlcxxc06ezsqans2x379t7drqh8u95k8wax8wr6ww5d4ssxu03q2
with value of 546 sats (protomessage swap failure refund)
OP_RETURN output:
[ OP_RETURN, OP_13, Runestone bytes as data push]
Protostone 0 layout, target of RUN•OF•THE•MILL•GOODS edict:
{
[Burn (83)]: u128(0x4e20),
[Pointer (91)]: u128(8)
}
Protostone 1 layout, target of UNCOMMON•GOODS edict:
{
[Burn (83)]: u128(0x4e20),
[Pointer (91)]: u128(7)
}
Protostone 2 layout (SWAP):
We use a hypothetical minOut of 99 EVERYDAY•CARRY•GOODS output from and construct a Message bytearray of [ UTF-8('S') . leb128[3] (output RuneId . output minOut) ]
or 0x5357415080a8d6b9070163
.
{
[Message (81)]: u8[] = 0x5357415080a8d6b9070163,
[Pointer (91)]: u128(7),
[Refund (93)]: u128(3)
}
Message for Protostone 3 we will construct with an example minOut of 100 LP tokens, encoded as leb128 and appended to UTF-8 "M"
This gives us:
0x4d494e5410
Protostone 3 layout (MINT):
{
[Message (81)]: u8[] = 0x4d494e5410,
[Pointer (91)]: u128(2),
[Refund (93)]: u128(3)
}
Recall that the format for the preimage for the bytearray stored in the Runestone Protocol field is:
Protocol ID -> number of leb128s for Protostone -> leb128[length]
This list is consumed in entirety.
The leb128[] representation of our Protocol field for a Runestone then, is:
[ u128(13), u128(4), u128(83), u128(0x4e20), u128(91), u128(7), u128(13), u128(4), u128(83), u128(0x4e20), u128(91), u128(8), u128(0x4e20), u128(6), u128(81), u128(0x5380897a019901), u128(91), u128(7), u128(93), u128(3), u128(0x4e20), u128(6), u128(81), u128(0x4d64), u128(91), u128(2), u128(93), u128(3) ]
As an array of leb128s, the bytearray in hexdecimal is:
0x0d0453a09c015b070d0453a09c015b08a09c01065181b286d09791e0295b075d03a09c010651e49a015b025d03
Runestone layout:
{
[Edict (0)]: [{
Height: 0,
Txindex: 1,
Amount: 0,
Output: 0
}, {
Height: 0,
Txindex: 1,
Amount: 100,
Output: 0
}, {
Height: 1000000,
Txindex: 1,
Amount: 100,
Output: 0
}],
[Pointer (22)]: 0x01,
[Protocol (16383)]: [ u128(0x0d0453a09c015b070d0453a09c015b), u128(0x08a09c01065181b286d09791e0295b), u128(0x075d03a09c010651e49a015b025d03) ]
}
Encoded:
0xc0843d0164001601ff7fdb82f084ba8ac18687b685e089f494820dff7fdbd2808ff992b4c3b283c6b29080a7d008ff7f83ba89d895c0a6f2d18c84e089f4c0ae070001000000006400
(73 bytes)
Full OP_RETURN output:
0x6a5d49c0843d0164001601ff7fdb82f084ba8ac18687b685e089f494820dff7fdbd2808ff992b4c3b283c6b29080a7d008ff7f83ba89d895c0a6f2d18c84e089f4c0ae070001000000006400