Gas Optimizations for the Rest of Us
The basics of optimizing Solidity contracts, explained for regular coders.Writing smart contracts is hard. Not only do you get a single chance to write bug-free code, but depending on exactly how you write, it’ll cost your users more or less to interact with it. When you compile a smart contract, every line of Solidity gets converted into a series of operations (called opcodes), which have a set gas cost. Your goal is to write your program using as little opcodes as possible (or replace the m...
Decentralized Comments for Mirror
You can now comment on my Mirror entries! Here’s how I implemented it in a decentralized way.For a while now I’ve been maintaining a custom Mirror client. This allows me to use a custom domain (m1guelpf.blog instead of miguel.mirror.xyz), provide an RSS feed and retain some control over the design. So, when I came across The Convo Space, a “conversation protocol” built on top of IPFS & Libp2p, I decided to try my hand at building a decentralized commenting system for the client.Follow the Con...
How to Learn (Crypto)
Outlining my approach to quickly learning new subjects, and how you too can become a crypto expert in less than you think.In the past few months, I went from knowing almost nothing about crypto to minting NFTs, launching my own token, and getting a job at a crypto startup (TBA). While part of this was definitely being in the right place at the right time, the rest is most likely thanks to a learning strategy I’ve developed over the years, and that helped me structure my dive into the crypto w...
<100 subscribers
Gas Optimizations for the Rest of Us
The basics of optimizing Solidity contracts, explained for regular coders.Writing smart contracts is hard. Not only do you get a single chance to write bug-free code, but depending on exactly how you write, it’ll cost your users more or less to interact with it. When you compile a smart contract, every line of Solidity gets converted into a series of operations (called opcodes), which have a set gas cost. Your goal is to write your program using as little opcodes as possible (or replace the m...
Decentralized Comments for Mirror
You can now comment on my Mirror entries! Here’s how I implemented it in a decentralized way.For a while now I’ve been maintaining a custom Mirror client. This allows me to use a custom domain (m1guelpf.blog instead of miguel.mirror.xyz), provide an RSS feed and retain some control over the design. So, when I came across The Convo Space, a “conversation protocol” built on top of IPFS & Libp2p, I decided to try my hand at building a decentralized commenting system for the client.Follow the Con...
How to Learn (Crypto)
Outlining my approach to quickly learning new subjects, and how you too can become a crypto expert in less than you think.In the past few months, I went from knowing almost nothing about crypto to minting NFTs, launching my own token, and getting a job at a crypto startup (TBA). While part of this was definitely being in the right place at the right time, the rest is most likely thanks to a learning strategy I’ve developed over the years, and that helped me structure my dive into the crypto w...
Share Dialog
Share Dialog

Over the past few weeks, I built a few apps that integrated with Mirror, to try and understand how their protocol worked. I started using their internal APIs and, from there, worked my way to more decentralized sources. Here’s how I did it (and how you can too).
The idea for The Actual Write Race was to build a list of all existing Mirror publications, and rank them based on the number of articles they had written (simulating Mirror’s $WRITE race) to add a fun touch.
To get the list of publications, we can query the Mirror GraphQL API (live at https://mirror-api.com/graphql) with the following query:
query FetchPublications {
publications {
ensLabel
displayName
avatarURL
contributor {
displayName
avatarURL
address
}
}
}
The publications query will give us most of the data we need, and if I were to build a simple listing I could have stopped here, but I also need the number of entries to rank them. Ideally, I should just be able to fetch the entries key on the above query, but due to how Mirror has structured their API, the entries are set to null when querying the publication list. Instead, we can use a second query to fetch the entries for each publication and check the length of those.
query PublicationEntries($ensLabel: String!) {
publication(ensLabel: $ensLabel) {
ensLabel
entries {
digest
}
}
}
It’s not the best system, and it has a blatant N+1 issue, but it’s the best I could get for this project, and the data is only fetched once a day, so it didn’t end up being an issue. If you want to learn more about this project, the source is available on GitHub.
The Mirror interface is beautiful, and I wanted to take a chance at recreating it with Tailwind CSS, so I decided to build a custom Mirror client with Next.js. The hard part of this project turned out to be retrieving Mirror entries in a decentralized way (querying the blockweave instead of Mirror’s API).
To start, we need to know the wallet address of the publication owner. Since Mirror subdomains are ENS names, we can do this by resolving {publication}.mirror.xyz with any ENS resolver. With this information, we can query the blockweave (which conveniently offers a GraphQL API hosted at https://arweave.net/graphql) by retrieving transactions created by Mirror and signed by that wallet address:
query FetchTransactions($address: String!) {
transactions(first: 100, tags: [{ name: "App-Name", values: ["MirrorXYZ"] }, { name: "Contributor", values: [$address] }]) {
edges {
node {
id
tags {
name
value
}
}
}
}
}
Since Mirror supports editing entries by pushing additional transactions, we need to check the Original-Content-Digest tag to make sure we only take the latest edition of each entry into account. We’ll use that original digest as the slug for the post (emulating Mirror) and the node ID to fetch the entry from the blockweave using the Arweave NPM library.
const getPaths = async () => {
const {
data: {
transactions: { edges },
},
} = await queryGraphQL()
edges.map(({ node }) => {
const tags = Object.fromEntries(node.tags.map(tag => [tag.name, tag.value]))
return { slug: tags['Original-Content-Digest'], path: node.id }
}).filter(entry => entry.slug && entry.slug !== '').reduce((acc, current) => {
const x = acc.find(entry => entry.slug === current.slug)
if (!x) return acc.concat([current])
else return acc
}, [])
}
const getEntries = async () => {
const paths = await getPaths()
return Promise.all(
paths.map(async entry => JSON.parse(
await arweave.transactions.getData(entry.path, { decode: true, string: true }), entry.slug)
)
)
}
This will get you an array of entries following this format, you can then just parse the markdown bodies and render your entries.
Then, to fetch the contents of a single entry, you can query by the original content digest (which we’re using as a slug).
query FetchTransaction($digest: String!) {
transactions(tags: { name: "Original-Content-Digest", values: [$digest] }) {
edges {
node {
id
}
}
}
}
Keep in mind in this example we’re not verifying the signature of any of these entries, so anyone could add new entries with a random string as the signature. Ideally, you’d use a library like eth-sig-util to make sure all entries are authentic.
If you’re curious about the source of my Mirror client, it’s available on GitHub. You can also see it live at m1guelpf.blog.
With these two data sources (Mirror’s GraphQL API & the blockweave), you can build anything on top of Mirror. Here are a few ideas:
RSS feeds for Mirror blogs (my Mirror client already includes an RSS feed, you’d just need to make it work with any other publication.
A list of all the available crowdfunds throughout Mirror publications, as well as some stats on finished ones.
A substack-like app that watches for new entries on a specific publication (or maybe all of them?) and emails subscribers the content.
A interface allowing people who don’t yet have a Mirror publication to publish their articles on Arweave using the same format Mirror uses.
Make sure to send me any cool apps you build with Mirror! You can find me at @m1guelpf on Twitter.

Over the past few weeks, I built a few apps that integrated with Mirror, to try and understand how their protocol worked. I started using their internal APIs and, from there, worked my way to more decentralized sources. Here’s how I did it (and how you can too).
The idea for The Actual Write Race was to build a list of all existing Mirror publications, and rank them based on the number of articles they had written (simulating Mirror’s $WRITE race) to add a fun touch.
To get the list of publications, we can query the Mirror GraphQL API (live at https://mirror-api.com/graphql) with the following query:
query FetchPublications {
publications {
ensLabel
displayName
avatarURL
contributor {
displayName
avatarURL
address
}
}
}
The publications query will give us most of the data we need, and if I were to build a simple listing I could have stopped here, but I also need the number of entries to rank them. Ideally, I should just be able to fetch the entries key on the above query, but due to how Mirror has structured their API, the entries are set to null when querying the publication list. Instead, we can use a second query to fetch the entries for each publication and check the length of those.
query PublicationEntries($ensLabel: String!) {
publication(ensLabel: $ensLabel) {
ensLabel
entries {
digest
}
}
}
It’s not the best system, and it has a blatant N+1 issue, but it’s the best I could get for this project, and the data is only fetched once a day, so it didn’t end up being an issue. If you want to learn more about this project, the source is available on GitHub.
The Mirror interface is beautiful, and I wanted to take a chance at recreating it with Tailwind CSS, so I decided to build a custom Mirror client with Next.js. The hard part of this project turned out to be retrieving Mirror entries in a decentralized way (querying the blockweave instead of Mirror’s API).
To start, we need to know the wallet address of the publication owner. Since Mirror subdomains are ENS names, we can do this by resolving {publication}.mirror.xyz with any ENS resolver. With this information, we can query the blockweave (which conveniently offers a GraphQL API hosted at https://arweave.net/graphql) by retrieving transactions created by Mirror and signed by that wallet address:
query FetchTransactions($address: String!) {
transactions(first: 100, tags: [{ name: "App-Name", values: ["MirrorXYZ"] }, { name: "Contributor", values: [$address] }]) {
edges {
node {
id
tags {
name
value
}
}
}
}
}
Since Mirror supports editing entries by pushing additional transactions, we need to check the Original-Content-Digest tag to make sure we only take the latest edition of each entry into account. We’ll use that original digest as the slug for the post (emulating Mirror) and the node ID to fetch the entry from the blockweave using the Arweave NPM library.
const getPaths = async () => {
const {
data: {
transactions: { edges },
},
} = await queryGraphQL()
edges.map(({ node }) => {
const tags = Object.fromEntries(node.tags.map(tag => [tag.name, tag.value]))
return { slug: tags['Original-Content-Digest'], path: node.id }
}).filter(entry => entry.slug && entry.slug !== '').reduce((acc, current) => {
const x = acc.find(entry => entry.slug === current.slug)
if (!x) return acc.concat([current])
else return acc
}, [])
}
const getEntries = async () => {
const paths = await getPaths()
return Promise.all(
paths.map(async entry => JSON.parse(
await arweave.transactions.getData(entry.path, { decode: true, string: true }), entry.slug)
)
)
}
This will get you an array of entries following this format, you can then just parse the markdown bodies and render your entries.
Then, to fetch the contents of a single entry, you can query by the original content digest (which we’re using as a slug).
query FetchTransaction($digest: String!) {
transactions(tags: { name: "Original-Content-Digest", values: [$digest] }) {
edges {
node {
id
}
}
}
}
Keep in mind in this example we’re not verifying the signature of any of these entries, so anyone could add new entries with a random string as the signature. Ideally, you’d use a library like eth-sig-util to make sure all entries are authentic.
If you’re curious about the source of my Mirror client, it’s available on GitHub. You can also see it live at m1guelpf.blog.
With these two data sources (Mirror’s GraphQL API & the blockweave), you can build anything on top of Mirror. Here are a few ideas:
RSS feeds for Mirror blogs (my Mirror client already includes an RSS feed, you’d just need to make it work with any other publication.
A list of all the available crowdfunds throughout Mirror publications, as well as some stats on finished ones.
A substack-like app that watches for new entries on a specific publication (or maybe all of them?) and emails subscribers the content.
A interface allowing people who don’t yet have a Mirror publication to publish their articles on Arweave using the same format Mirror uses.
Make sure to send me any cool apps you build with Mirror! You can find me at @m1guelpf on Twitter.
No comments yet