Reference Implementation Guide — DogeTag Offers and ÐWhisper
This guide gives minimal implementation sketches. It is intentionally small: production wallets still need full transaction construction, fee selection, encryption review, reorg handling, and secure key storage.
For parser conformance vectors and runtime debugging workflow, see:
Send a ÐMP DogeTag Offer
A DogeTag Offer has two parts:
- A 43-byte
Ð:𝕏OP_RETURN payload, or a 40-byteÐ:Xfallback payload when space is tight. - A spendable DOGE output to the intended recipient, called the attention amount.
Encode the Payload
import hashlib
import struct
DXD_MARKER_BRAND = "Ð:𝕏".encode("utf-8")
DXD_MARKER_FALLBACK = "Ð:X".encode("utf-8")
ERA2_LIMIT = 76
def choose_dxd_marker(body: bytes) -> bytes:
if len(DXD_MARKER_BRAND + body) <= ERA2_LIMIT:
return DXD_MARKER_BRAND
return DXD_MARKER_FALLBACK
def dogetag_payload(asset_id: str, asset_type: str, price_koinu: int, expiry_delta: int, nonce: int,
full_offer_intended: bool = True, sound_hint: bool = False,
dogewhisper_link: bool = False) -> bytes:
flags = 0
if full_offer_intended:
flags |= 1 << 0
if sound_hint:
flags |= 1 << 1
if dogewhisper_link:
flags |= 1 << 2
asset_type_byte = {
"inscription": 0x01,
"collection": 0x02,
}[asset_type]
asset_hash16 = hashlib.sha256(asset_id.encode("utf-8")).digest()[:16]
body = bytes([
0x01, # version
0x01, # kind: buy offer
flags,
asset_type_byte,
]) + asset_hash16 + struct.pack("<QII", price_koinu, expiry_delta, nonce)
return choose_dxd_marker(body) + bodyBuild the Transaction
def build_dogetag_transaction(wallet, recipient_address, payload, attention_koinu):
tx = wallet.new_transaction()
tx.add_op_return(payload)
tx.add_output(recipient_address, attention_koinu)
tx.fund_and_sign()
return txWallets SHOULD enforce a configurable minimum attention amount and SHOULD warn that price_koinu is not escrow.
Decode the Payload
def decode_dogetag(payload: bytes) -> dict:
if payload.startswith(DXD_MARKER_BRAND):
marker = "Ð:𝕏"
offset = len(DXD_MARKER_BRAND)
elif payload.startswith(DXD_MARKER_FALLBACK):
marker = "Ð:X"
offset = len(DXD_MARKER_FALLBACK)
else:
raise ValueError("not a DXD DogeTag payload")
body = payload[offset:]
if len(body) != 36:
raise ValueError("invalid DogeTag body length")
version, kind, flags, asset_type = body[0], body[1], body[2], body[3]
price_koinu, expiry_delta, nonce = struct.unpack("<QII", body[20:36])
return {
"marker": marker,
"version": version,
"kind": "buy_offer" if kind == 0x01 else "unknown",
"flags": {
"full_dmp_offer_intended": bool(flags & 0x01),
"sound_hint": bool(flags & 0x02),
"dogewhisper_link": bool(flags & 0x04),
},
"asset_type": "inscription" if asset_type == 0x01 else "collection",
"asset_hash16": body[4:20].hex(),
"price_koinu": str(price_koinu),
"expiry_height_delta": expiry_delta,
"nonce": nonce,
}Decoded objects SHOULD validate against ../../protocols/dmp/schemas/dmp-offer-signal-decoded.json.
Create a ÐWhisper Message
ÐWhisper has:
- A
Ð:WOP_RETURN signal for discovery, short messages, room announcements, and notification hints. - An encrypted inscription payload for longer content, room metadata, member envelopes, and media references.
Prepare Recipient Keys
def recipient_key_id(compressed_pubkey_bytes: bytes) -> str:
return "sha256:" + hashlib.sha256(compressed_pubkey_bytes).hexdigest()Wallets SHOULD discover recipient chat keys from verified ÐMS records, ÐWhisper key announcements, or a local address book.
Encrypt the Message
This pseudocode omits library-specific details:
from base64 import b64encode
def to_b64(value: bytes) -> str:
return "base64:" + b64encode(value).decode("utf-8")
def create_dogewhisper_message(sender_chat_key, recipient_pubkey, plaintext: bytes, context: dict) -> dict:
content_key = random_bytes(32)
nonce = random_bytes(24)
aad = "Ð:W-1.0".encode("utf-8")
ciphertext = xchacha20poly1305_encrypt(content_key, nonce, plaintext, aad)
epk, shared_secret = secp256k1_ecdh(sender_chat_key.ephemeral(), recipient_pubkey)
wrapping_key = hkdf_sha256(shared_secret, info=b"dogewhisper-wrap-v1")
wrapped_key = wrap_content_key(wrapping_key, content_key, aad)
canonical_encrypted_bytes = ciphertext
return {
"p": "Ð:W",
"v": "1.0",
"op": "message",
"chain": "dogecoin",
"context": context,
"cipher": "xchacha20poly1305",
"kem": "secp256k1-ecdh-hkdf-sha256",
"recipients": [
{
"kid": recipient_key_id(recipient_pubkey),
"epk": epk.hex(),
"wrapped_key": to_b64(wrapped_key),
}
],
"nonce": to_b64(nonce),
"aad": to_b64(aad),
"ciphertext": to_b64(ciphertext),
"content_hash": "sha256:" + hashlib.sha256(canonical_encrypted_bytes).hexdigest(),
}The resulting object SHOULD validate against ../../protocols/dwhisper/schemas/dogewhisper-message.json.
Emit a ÐWhisper Signal
DOGEWHISPER_SIGNAL_MAGIC = "Ð:W".encode("utf-8")
def dogewhisper_signal(recipient_pubkey_bytes: bytes, ciphertext_or_content_id: bytes, flags: int,
expiry_delta: int, nonce: int) -> bytes:
recipient_hash16 = hashlib.sha256(recipient_pubkey_bytes).digest()[:16]
content_hash16 = hashlib.sha256(ciphertext_or_content_id).digest()[:16]
return (
DOGEWHISPER_SIGNAL_MAGIC
+ bytes([0x01, 0x01, flags, 0x00])
+ recipient_hash16
+ content_hash16
+ struct.pack("<II", expiry_delta, nonce)
)Decrypt a Message
def decrypt_dogewhisper_message(local_chat_key, message: dict) -> bytes | None:
my_kid = recipient_key_id(local_chat_key.public_key_bytes())
envelope = next((r for r in message["recipients"] if r["kid"] == my_kid), None)
if envelope is None:
return None
shared_secret = secp256k1_ecdh(local_chat_key.private_key, bytes.fromhex(envelope["epk"]))
wrapping_key = hkdf_sha256(shared_secret, info=b"dogewhisper-wrap-v1")
content_key = unwrap_content_key(wrapping_key, decode_b64(envelope["wrapped_key"]), aad=decode_b64(message["aad"]))
return xchacha20poly1305_decrypt(
content_key,
nonce=decode_b64(message["nonce"]),
ciphertext=decode_b64(message["ciphertext"]),
aad=decode_b64(message["aad"]),
)Never render decrypted content as active HTML, SVG, or script. Treat decrypted bytes as untrusted user content.
Implementation Checklist
- Use separate chat keys and spend keys.
- Validate payload lengths before parsing.
- Treat OP_RETURN signals as hints until matched against wallet inventory or indexed state.
- Keep DogeTags, ÐWhisper messages, and ÐMP offers as separate state machines.
- Make sound hints opt-in and rate-limited.
- Roll back indexed state on reorgs.