<p>I have forked the <a href="https://github.com/vbuterin/pybitcointools" rel="nofollow noreferrer" title="pybitcointools">pybitcointools library</a> to return a properly formatted <code>OP_RETURN</code> hex string, or insert the <code>OP_RETURN</code> into a raw hex transaction.</p>

<p><a href="https://github.com/simcity4242/pybitcointools" rel="nofollow noreferrer">My fork can be found here</a>. The code is as follows:</p>

<pre><code>from bitcoin.pyspecials import safe_hexlify, from_string_to_bytes, from_int_to_byte, from_string_to_bytes

def mk_opreturn(msg, rawtx=None, json=0):
    def op_push(data):
        import struct
        if len(data) &lt; 0x4c:
            return from_int_to_byte(len(data)) + from_string_to_bytes(data)
        elif len(data) &lt; 0xff:
            return from_int_to_byte(76) + struct.pack('&lt;B', len(data)) + from_string_to_bytes(data)
        elif len(data) &lt; 0xffff:
            return from_int_to_byte(77) + struct.pack('&lt;H', len(data)) + from_string_to_bytes(data)
        elif len(data) &lt; 0xffffffff:
            return from_int_to_byte(78) + struct.pack('&lt;I', len(data)) + from_string_to_bytes(data)
        else: raise Exception("Input data error. Rawtx must be hex chars" \
                            + "0xffffffff &gt; len(data) &gt; 0")

    orhex = safe_hexlify(b'\x6a' + op_push(msg))
    orjson = {'script' : orhex, 'value' : 0}
    if rawtx is not None:
        try:
            txo = deserialize(rawtx)
            if not 'outs' in txo.keys(): raise Exception("OP_Return cannot be the sole output!")
            txo['outs'].append(orjson)
            newrawtx = serialize(txo)
            return newrawtx
        except:
            raise Exception("Raw Tx Error!")
    return orhex if not json else orjson
</code></pre>

<p>Note that the module name has been changed to <code>btc</code> (from <code>bitcoin</code>) in my fork. </p>

<p>To run this, you'll use <code>os.chdir("c:/python/pybitcointools")</code> (or whatever directory it's been downloaded to. Then <code>from bitcoin import *</code>. Now, let's use <code>msg = 'The enemy of my enemy is my friend'</code> and <code>rawtx = "01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0168d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac00000000"</code>.</p>

<p><code>mk_opreturn(msg, rawtx)</code> returns: </p>

<pre><code>01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0268d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac0000000000000000246a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e6400000000
</code></pre>

<p>This is the raw Tx with the <code>OP_RETURN</code> properly inserted. Run the function without the <code>rawtx</code> parameter and it returns the string <code>6a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e64</code></p>

CC BY-SA 3.0
2015-05-18T03:08:57.400Z
Did you mean to answer this twice? PS. I sent you a PR on github. :)
Nick ODell
CC BY-SA 3.0
2015-05-18T03:11:03.470Z
Yes and no :). I felt it needed a separate answer because I'm trying to answer the "how to easily use OP_RETURN with Python using code" question. This answer is more self-contained I feel. Good catch on the PR BTW
Wizard Of Ozzie
CC BY-SA 3.0
2015-05-18T02:45:50.107Z
2017-06-22T20:47:39.837Z
2017-06-22T20:47:39.837Z
Wizard Of Ozzie
<p>The script is simply (so to speak) taking a raw transaction and splicing in a hexadecimal number which represents an additional Tx output. So it's looking for <em>ffffffff</em> (<em>sequence</em>) and appending <code>6a</code> <code>hex encoding of your msg</code> (up to 20 bytes)</p>

<p>The message needs converting to hex (which the code does).</p>

<p>The code works fine for me, although I've yet to try broadcasting. Based on your quoted error </p>

<blockquote>
  <p><em>I get the error (from the Blockchain.info broadcast API): Exception: None Standard Script Output OP_RETURN 594f4c4f53574147</em></p>
</blockquote>

<p>I'd have to say BCI is reacting to the<code>MessageLen</code> part of the code. There should be an <code>OP_PUSHDATA1</code> (<code>0x4c</code>) between <code>OP_RETURN</code> (<code>0x6a</code>) and the 1 byte msg length, <code>0x08</code>. </p>

<p>Try: <code>6a4c08594f4c4f53574147</code></p>

<p>Alternatively, try another service to push the raw Tx, eg:</p>

<ul>
<li><a href="https://insight.bitpay.com/tx/send" rel="nofollow">https://insight.bitpay.com/tx/send</a></li>
<li><a href="https://brainwallet.github.io/#tx" rel="nofollow">https://brainwallet.github.io/#tx</a></li>
</ul>

CC BY-SA 3.0
2015-02-09T19:25:16.423Z
Doesn't my line ```ID = binascii.hexlify(str(message))``` already take care of that?
rsmoz
CC BY-SA 3.0
2015-02-10T06:21:46.443Z
@rsmoz let me have a look for you. Can you confirm the URL for the script and the Python version you're on?
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-10T07:01:02.447Z
2.7. I'm about to update the code with some changes I've made.
rsmoz
CC BY-SA 3.0
2015-02-10T19:07:12.307Z
I added the push code, didn't work. Tried Bitpay's tx send, told me my transaction was dust, which I remembered meant the transaction was too small. Made it larger, then used the Bitpay send and it worked! https://blockchain.info/tx/e44d541fd4cc0c2390a91e7e4d35436524badddd314e905d9113f383ba2a0c62
rsmoz
CC BY-SA 3.0
2015-02-10T23:15:30.783Z
@rsmoz good stuff! I'm interested to know if it was the 550 Satoshi dust limit or the push op code that did it. I'll edit my answer to remove the unhelpful binascii comments I made
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-12T05:12:05.310Z
Hmm, still having some issues. Now that I've got a lot of inputs to deal with, the transaction isn't being assembled properly.
rsmoz
CC BY-SA 3.0
2015-02-25T09:25:25.190Z
I wanted to add a clarification: when assembling a 40 byte *OP_RETURN* Txn the code is: `2a6a28` `40 byte data`, where `2a` says push next 42 bytes, `6a` is OP_RETURN and `28` says push the 40 bytes. This is the required format for Bitcoincore.
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-09T10:04:21.870Z
2015-02-10T23:19:49.453Z
2015-02-10T23:19:49.453Z
Wizard Of Ozzie
<p>You might find our python-OP_RETURN library helpful, either to use out-of-the-box, or to look inside to see how we are building OP_RETURN transactions.</p>

<p><a href="https://github.com/coinspark/python-OP_RETURN" rel="nofollow">https://github.com/coinspark/python-OP_RETURN</a></p>

<p>It also has a neat feature to store arbitrary-sized data in the blockchain using multiple chained transactions with OP_RETURNs, and to retrieve that data using a single 12-digit reference number.</p>

CC BY-SA 3.0
2015-07-01T17:08:47.640Z
What does the reference number correspond to?
Nick ODell
CC BY-SA 3.0
2015-07-02T06:15:24.423Z
It's in the format: [estimated height]-[partial txid]

[estimated height] is the block where the first transaction may appear. When storing we set [estimated height] to 1+(current height). When retrieving we try that block then some later and a few from before in case of reorgs.

[partial txid] contains 2 adjacent bytes from the txid:

position=2*([partial txid] div 65536)
([partial txid] mod 256) is the byte at position
([partial txid] mod 65536) div 256) is the byte at (position+1)

You can't use a txid for the reference since most nodes don't index by txid.
Gideon Greenspan
CC BY-SA 3.0
2016-07-16T22:19:19.150Z
@Gideon Greenspan -- I tried to use your code but got this error: `'OP_RETURN.py", line 456, in OP_RETURN_bitcoin_cmd` `if not len(port):` `TypeError: object of type 'int' has no len()'`
moli
CC BY-SA 3.0
2016-07-19T07:53:52.077Z
Set `OP_RETURN_BITCOIN_PORT` using an integer in a string rather than an integer.
Gideon Greenspan
CC BY-SA 3.0
2015-07-01T08:05:57.463Z
2015-07-01T08:05:57.463Z
Gideon Greenspan
<p>Currently trying to figure out how to use Python to construct a transaction with OP_RETURN. I tried to encode the message myself, and had no luck. I found a function, OPReturn(), on the internet, but when I attempt to use it, I get the error (from the Blockchain.info broadcast API): <code>Exception: None Standard Script Output OP_RETURN 594f4c4f53574147</code> </p>

<p>Code:</p>

<pre><code># coding: utf-8

from bitcoin import *
import binascii
from test import *


priv = sha256('brain wallet')

pub = privtopub(priv)

addr = pubtoaddr(pub)

inputs = unspent(addr)

message = "YOLOSWAG"
FullLen = format(len(message)+2,'x').rjust(2,'0')
MessageLen = format(len(message),'x').rjust(2,'0')
ID = binascii.hexlify(str(message))
snd = "6a"+MessageLen+ID

outputs = [{'value': 50000, 'address': addr}, {'value': 0, 'script': snd}]

fee = 10000

tx = mksend(inputs, outputs, addr, fee)

dt = deserialize(tx)
ins = dt['ins']

#print addr
#print ins


for ind, elm in enumerate(ins):
    print elm
    tx = sign(tx, ind, priv)

#print tx



print(pushtx(tx))
</code></pre>

CC BY-SA 3.0
2015-02-09T07:17:46.440Z
Note that this code will only work on messages of 75 bytes or less.
Nick ODell
CC BY-SA 3.0
2015-02-09T09:43:13.760Z
@NickODell isn't it 40 bytes? (EDIT: you mean 75 bytes total I just realised?)
Wizard Of Ozzie
CC BY-SA 3.0
2015-02-09T09:48:52.090Z
What version of Python? I'm pretty sure you're trying to do this on Python 3.x where the *struct* *"q/Q"* field is no longer available. Pybitcointools will require Python 2.7 (in fact virtually all Bitcoin Python stuff is version 2.x due to the way it handles strings as bytes without *binascii* and such.
Wizard Of Ozzie
<transactions><blockchain><protocol><network><op-return>
How do I compose a transaction with OP_RETURN in Python?
rsmoz
# How do I compose a transaction with OP_RETURN in Python?

(Asked by: username_0 on 2015-02-09)

Currently trying to figure out how to use Python to construct a transaction with OP\_RETURN. I tried to encode the message myself, and had no luck. I found a function, OPReturn(), on the internet, but when I attempt to use it, I get the error (from the Blockchain.info broadcast API): `Exception: None Standard Script Output OP_RETURN 594f4c4f53574147`

Code:

```
# coding: utf-8

from bitcoin import *
import binascii
from test import *

priv = sha256('brain wallet')

pub = privtopub(priv)

addr = pubtoaddr(pub)

inputs = unspent(addr)

message = "YOLOSWAG"
FullLen = format(len(message)+2,'x').rjust(2,'0')
MessageLen = format(len(message),'x').rjust(2,'0')
ID = binascii.hexlify(str(message))
snd = "6a"+MessageLen+ID

outputs = [{'value': 50000, 'address': addr}, {'value': 0, 'script': snd}]

fee = 10000

tx = mksend(inputs, outputs, addr, fee)

dt = deserialize(tx)
ins = dt['ins']

#print addr
#print ins

for ind, elm in enumerate(ins):
    print elm
    tx = sign(tx, ind, priv)

#print tx

print(pushtx(tx))

```

**Tags:** transactions, blockchain, protocol, network, op-return

> **Comments:**
> **username_1**: Note that this code will only work on messages of 75 bytes or less.  
> **username_2**: @username_1 isn't it 40 bytes? (EDIT: you mean 75 bytes total I just realised?)  
> **username_2**: What version of Python? I'm pretty sure you're trying to do this on Python 3.x where the *struct* *"q/Q"* field is no longer available. Pybitcointools will require Python 2.7 (in fact virtually all Bitcoin Python stuff is version 2.x due to the way it handles strings as bytes without *binascii* and such.  

---

## 3 answers

### Answer by username_2 on 2015-05-18. Score: 3

I have forked the [pybitcointools library](https://github.com/vbuterin/pybitcointools "pybitcointools") to return a properly formatted `OP_RETURN` hex string, or insert the `OP_RETURN` into a raw hex transaction.

[My fork can be found here](https://github.com/simcity4242/pybitcointools). The code is as follows:

```
from bitcoin.pyspecials import safe_hexlify, from_string_to_bytes, from_int_to_byte, from_string_to_bytes

def mk_opreturn(msg, rawtx=None, json=0):
    def op_push(data):
        import struct
        if len(data) < 0x4c:
            return from_int_to_byte(len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xff:
            return from_int_to_byte(76) + struct.pack('<B', len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xffff:
            return from_int_to_byte(77) + struct.pack('<H', len(data)) + from_string_to_bytes(data)
        elif len(data) < 0xffffffff:
            return from_int_to_byte(78) + struct.pack('<I', len(data)) + from_string_to_bytes(data)
        else: raise Exception("Input data error. Rawtx must be hex chars" \
                            + "0xffffffff > len(data) > 0")

    orhex = safe_hexlify(b'\x6a' + op_push(msg))
    orjson = {'script' : orhex, 'value' : 0}
    if rawtx is not None:
        try:
            txo = deserialize(rawtx)
            if not 'outs' in txo.keys(): raise Exception("OP_Return cannot be the sole output!")
            txo['outs'].append(orjson)
            newrawtx = serialize(txo)
            return newrawtx
        except:
            raise Exception("Raw Tx Error!")
    return orhex if not json else orjson

```

Note that the module name has been changed to `btc` (from `bitcoin`) in my fork.

To run this, you'll use `os.chdir("c:/python/pybitcointools")` (or whatever directory it's been downloaded to. Then `from bitcoin import *`. Now, let's use `msg = 'The enemy of my enemy is my friend'` and `rawtx = "01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0168d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac00000000"`.

`mk_opreturn(msg, rawtx)` returns:

```
01000000016e3cd2b24fcf49259db29888ec5fe6521070041cb8c7bb2017537046f9e00f2b0000000000ffffffff0268d61100000000001976a91469bbbb16301e40b9fb67130e1aa53a2281d60af088ac0000000000000000246a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e6400000000

```

This is the raw Tx with the `OP_RETURN` properly inserted. Run the function without the `rawtx` parameter and it returns the string `6a2254686520656e656d79206f66206d7920656e656d79206973206d7920667269656e64`

> **Comments:**
> **username_1**: Did you mean to answer this twice? PS. I sent you a PR on github. :)  
> **username_2**: Yes and no :). I felt it needed a separate answer because I'm trying to answer the "how to easily use OP_RETURN with Python using code" question. This answer is more self-contained I feel. Good catch on the PR BTW  

---

### Answer by username_2 on 2015-02-09. Score: 1

The script is simply (so to speak) taking a raw transaction and splicing in a hexadecimal number which represents an additional Tx output. So it's looking for *ffffffff* (*sequence*) and appending `6a` `hex encoding of your msg` (up to 20 bytes)

The message needs converting to hex (which the code does).

The code works fine for me, although I've yet to try broadcasting. Based on your quoted error

> *I get the error (from the Blockchain.info broadcast API): Exception: None Standard Script Output OP\_RETURN 594f4c4f53574147*

I'd have to say BCI is reacting to the`MessageLen` part of the code. There should be an `OP_PUSHDATA1` (`0x4c`) between `OP_RETURN` (`0x6a`) and the 1 byte msg length, `0x08`.

Try: `6a4c08594f4c4f53574147`

Alternatively, try another service to push the raw Tx, eg:

* <https://insight.bitpay.com/tx/send>
* <https://brainwallet.github.io/#tx>

> **Comments:**
> **username_0**: Doesn't my line ```ID = binascii.hexlify(str(message))``` already take care of that?  
> **username_2**: @username_0 let me have a look for you. Can you confirm the URL for the script and the Python version you're on?  
> **username_0**: 2.7. I'm about to update the code with some changes I've made.  
> **username_0**: I added the push code, didn't work. Tried Bitpay's tx send, told me my transaction was dust, which I remembered meant the transaction was too small. Made it larger, then used the Bitpay send and it worked! https://blockchain.info/tx/e44d541fd4cc0c2390a91e7e4d35436524badddd314e905d9113f383ba2a0c62  
> **username_2**: @username_0 good stuff! I'm interested to know if it was the 550 Satoshi dust limit or the push op code that did it. I'll edit my answer to remove the unhelpful binascii comments I made  
> **username_0**: Hmm, still having some issues. Now that I've got a lot of inputs to deal with, the transaction isn't being assembled properly.  
> **username_2**: I wanted to add a clarification: when assembling a 40 byte *OP_RETURN* Txn the code is: `2a6a28` `40 byte data`, where `2a` says push next 42 bytes, `6a` is OP_RETURN and `28` says push the 40 bytes. This is the required format for Bitcoincore.  

---

### Answer by username_3 on 2015-07-01. Score: 0

You might find our python-OP\_RETURN library helpful, either to use out-of-the-box, or to look inside to see how we are building OP\_RETURN transactions.

<https://github.com/coinspark/python-OP_RETURN>

It also has a neat feature to store arbitrary-sized data in the blockchain using multiple chained transactions with OP\_RETURNs, and to retrieve that data using a single 12-digit reference number.

> **Comments:**
> **username_1**: What does the reference number correspond to?  
> **username_3**: It's in the format: [estimated height]-[partial txid]  [estimated height] is the block where the first transaction may appear. When storing we set [estimated height] to 1+(current height). When retrieving we try that block then some later and a few from before in case of reorgs.  [partial txid] contains 2 adjacent bytes from the txid:  position=2*([partial txid] div 65536) ([partial txid] mod 256) is the byte at position ([partial txid] mod 65536) div 256) is the byte at (position+1)  You can't use a txid for the reference since most nodes don't index by txid.  
> **username_4**: @username_3 -- I tried to use your code but got this error: `'OP_RETURN.py", line 456, in OP_RETURN_bitcoin_cmd` `if not len(port):` `TypeError: object of type 'int' has no len()'`  
> **username_3**: Set `OP_RETURN_BITCOIN_PORT` using an integer in a string rather than an integer.  

---

