Skip to content

Instantly share code, notes, and snippets.

@tunnckoCore
Last active November 30, 2025 21:22
Show Gist options
  • Select an option

  • Save tunnckoCore/6a39727623337750455cd30a2ee31e0e to your computer and use it in GitHub Desktop.

Select an option

Save tunnckoCore/6a39727623337750455cd30a2ee31e0e to your computer and use it in GitHub Desktop.
WGW.black - social network for Ethscriptions

wgw.black

Protocol for social networking built on Ethscriptions. Cuz I'm sick and tired of Twitter.

Previously i had Likes For Ethscriptions, but it was very early and.. not that extensible.

There's also TIC meta protocol by Chopper, it's fine for comments & reactions.

Here's what i think and need:

  1. to react (like, laugh, etc) user creates ethscription to reacts inbox with specific mimetype (which includes ethscription id or number), and content be the reaction
  • that way we can index all likes/reactions globally,
  • all user reactions, filter by initial_owner=reactInbox + creator=user
  • all reactions for given ethscription, filter by mimetype аnd initial_owner=reactInboxinbox
  1. to repost user create ethscription to reposts inbox with specific mimetype, and content the ethscriptionId of post to be reposted
  • to repost, use the react action with content "repost"
  • that way we can track reposts by user, by ethscription (global reposts feed doesn't make much sense but still could be tracked)
  1. to quote user create ethscription to SELF with specific mimetype, and content a TIC payload with intent: "quote" and topic be the quoted post
  2. to post user create ethscription to SELF with specific mimetype, and content a TIC payload with intent: "post" and topic the posts inbox
  3. to comment/reply - user create ethscription to SELF with specific mimetype, and content TIC payload with intent: "reply" and topic be the post id, comment id, or ethscription
  4. end-to-end encrypted DMs - public key encryption, encrypt to the user's pubkey, post to the "DMs inbox"
  5. profiles, banners, links, avatars/PFPs - use existing application/vnd.esc.user.profile+json mimetype sent to 0x0 address
  6. to follow another user - send ethscription to that user with mimetype esc.vnd.user.follows+json
  • to get user followings- who he follows - search by creator
  • to get user followers - who is following him - search by initial_owner
  • sending to a user could trigger a "xyz followed you" notification

in general.. inbox addresses is just great.. what a breakthrough of Facet v1 that was..

Design choices

  • inbox addresses allow for easy indexing (even without full-blown ethscriptions indexer)
  • index addresses are suffixed with :user to be able to detect it - not used currenlty but useful, future-proofing ourselves
  • filter by initial_owner=reactsInbox to get all global likes in the whole network/protocol
  • filter by initial_owner=reactsInbox and creator=userAddressOrEns - to get reactions made by user
  • to get all reactions for given ethscription filter by mime_subtype, it intentionally includes the ethscription id or number there
  • comments, replies, sub-commenys, and posts are TIC-like payload but with own mimetype and tiny modifications "type" -> "intent"
  • banners, descriptions, links, PFP/avatar, could use the existing application/vnd.esc.user.profile+json
  • some actions are having its own inbox address, it's like different type of "feeds" - similarly we can make groups, communities and what not.
  • inbox addresses are encoding the type/action inside it - it's a dead address

Indexing / Filtering

Use any Ethscriptions API to filter (mime_subtype in Ethscriptions.com API):

  • /ethscriptions?mime_subtype=esc.vnd.user.reacts%2Bjson, for specific user append creator=0xUserAddress
  • /ethscriptions?mime_subtype=esc.vnd.user.intent%2Bjson, for specific user append creator
  • /ethscriptions?mime_subtype=esc.vnd.user.follows%2Bjson, for specific user append creator=0xUserAddress
  • /ethscriptions?mime_subtype=esc.vnd.user.posts%2Bjson, for specific user append creator=0xUserAddress
    • instead of custom posts inbox and mimetype, we can just use TIC for posts and comments
    • for that, use ?content_type=message/vnd.tic+json

Action Types

Follows

To follow another user, you ethscibe to self the following content.

  • users send this ethscription to user to follow
  • inbox: 0x0000000000000000666f6c6c6f77733a75736572 - that's hex encoded follows:user
  • alternative ENS address of above follows.wgw.lol
  • the content "" is empty JSON string - eith optional message in the string (like "hi, nice to meet you")
  • to get user followings - search/filter by creator
  • to get user followers - search/filter by initial_owner
data:application/vnd.esc.user.follows+json;rule=esip6,"optional message"

Likes

The content of the ethscription is intentionally just like, apps gotta visualize it however they like.

  • users send this ethscription to likes inbox
  • inbox: 0x000000000000000000006c696b65733a75736572 - that's hex encoded likes:user
  • alternative ENS address of above likes.wgw.lol
  • the content is JSON Array tuple of - 1) like/reaction, 2) topic id
    • like could be literal string "like" or emoji in a string "🥳"
    • topicId is whatever you want - ethscription id or user address / ENS / ethscription name
    • in case topic is user and the string is "notif" or "notifications", it could be treated as "noticications turned on"
data:application/vnd.esc.user.likes+json;rule=esip6,["topicId", "like"]

One minor problem is that you can "like" the same thing multiple times. It sould be fixed on indexing level.

Reposts / re-tweets

Reposts are just link to a post a user wants to show in their profile feed

The content of the ethscription is the ethscrition id (transaction hash) of ethsription to repost (can be anything, or protocol-post ethscription)

  • users send this ethscription to reposts inbox
  • inbox: 0x00000000000000007265706f7374733a75736572 - that's hex encoded reposts:user
  • alternative ENS address of above reposts.wgw.lol
data:application/vnd.esc.user.reposts+json;rule=esip6,"0x251c5eb4579a83d86e84d77548b00015492013accdaba63c5f3b545baff75bcf"

Quote posts / quote-tweet

Quote posts are posts that user wants to add text or image to another post

The content of the ethscription is text/image/anything and an ethscription id that is image or another post/repost/quote-post

  • users send this ethscription to quotes inbox
  • inbox: 0x00000000000000000071756f7465733a75736572 - that's hex encoded quotes:user
  • alternative ENS address of above quotes.wgw.lol

muti-lined JSON only for better visibility - just use JSON.stringify({obj})

data:application/vnd.esc.user.quotes+json;rule=esip6,{
  "ref_id":0x251c5eb4579a83d86e84d77548b00015492013accdaba63c5f3b545baff75bcf",
  "content": "Anything to share about the linked post"
  "type": "text/plain"
}

Posts

Any type of post

The content of the ethscription is anything like text/image/svg/json/html or anything that exists as ethscription

  • users send this ethscription to quotes inbox
  • inbox: 0x00000000000000000000706f7374733a75736572 - that's hex encoded posts:user
  • NOTE it could just be TIC payload sent to the posts inbox.

muti-lined JSON only for better visibility - just use JSON.stringify({obj})

data:application/vnd.esc.user.posts+json;rule=esip6,{
  "topic":unique id",
  "content": "image, text, or anything",
  "encoding": "utf-8",
  "type": "text/plain"
}

User profiles (collection profiles)

it is undocumented but already existing thing in Ethscriptions

  • you can see all profile changes with mime_subtype filter in the api - vnd.esc.user.profile+json (it should be encodeURIComponent when passed to HTTP request, as shown below, eg. + becomes %2B)
  • you can also use the more standardize, unified, normalized, and cache one at Calldata - it has media_subtype filter
  • ?media_subtype=vnd.esc.user.profile%2Bjson - to list all recent profile updates
  • or ?mime_subtype=vnd.esc.user.profile%2Bjson - all the same
  • to check most recent "state" of a user profile, you just add one more filter - creator=userAddress
  • Calldata API has direct /profiles/wgw endpoint using exactly these filters, and returns { latest: obj, previous: array }
  • Calldata API has so much more.. like /profiles/wgw/created for ethscriptions created by wgw, and /owned for collected/reeived/owned
  • All Calldata API endpoints support Ethscriptions Names, addresses, and ENS domains (or eventually with resolve=1 query param)

Users have to ethscribe to 0x0 address (the dead/null address) the following content (and it will be respected even in the Ethscriptions.com profiles):

data:application/vnd.esc.user.profile+json,{
  "name": "wgw.eth - wgw.lol - Sigma Wolf - tunnckoCore",
  "links": [
    {
      "title": "Twitter",
      "url": "https://twitter.com/wgw_eth"
    }
  ],
  "banner": "esc://ethscriptions/0xaa946bfb5d1739f40a6fec654272045902da1379e2d63cc9211ceab261cae1d4/data",
  "image": "esc://ethscriptions/0xfb60f6decca2422e50d4f84d573bc09702707aaab9fe1592aa00ea7997fda0b8/data",
  "description": "Creator of protocols, apps, tools, and software. \\n\\nFounder of WGW.lol - a multi-purpose ecosystem -- the best explorer, a blogging/social platform, ethscribe tools, auction house, and more.\\n \\nI'm Open Sourcer for a decade. I'm working from home for decade. Developing on Linux for a decade."
}

Utils

export type Mode = "likes" | "posts" | "reposts" | "quotes";

export function generateEthereumAddress(mode: Mode): string {
  const hex = Array.from(`${mode}:user`)
    .map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
    .join("");
  return "0x" + "0".repeat(40 - hex.length) + hex;
}

console.log("likes", generateEthereumAddress("likes"));
// => 0x000000000000000000006c696b65733a75736572
console.log("reposts", generateEthereumAddress("reposts"));
// => 0x00000000000000007265706f7374733a75736572
console.log("quotes", generateEthereumAddress("quotes"));
// => 0x00000000000000000071756f7465733a75736572
console.log("posts", generateEthereumAddress("posts"));
// => 0x00000000000000000000706f7374733a75736572
console.log("follows", generateEthereumAddress("follows"));
// => 0x0000000000000000666f6c6c6f77733a75736572
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment