Irys Query Package
The query package enables users to search Irys and Arweave through an intuitive JavaScript package. It is easily implemented in a few lines of code.
The query package is for searching transaction metadata on Irys and Arweave, and also Arweave block information. Once you've found transactions, use the transaction ID to download the associated data.
Installation
Install via npm:
npm install @irys/query
and yarn:
yarn add @irys/query
Imports
Import with:
import Query from "@irys/query";
Creating a Query
object
Start by instantiating a new Query
object, this is a shared instance you can reuse each time you want to execute a new query.
const myQuery = new Query();
Then execute a query by chaining together a series of functions that collaboratively narrow down the results returned.
To retrieve the 20 latest transactions with the tag Content-Type
set to image/png
on Irys:
const results = await myQuery
.search("irys:transactions")
.tags([{ name: "Content-Type", values: ["image/png"] }])
.sort("ASC")
.limit(20);
Query location (mainnet, devnet, arweave)
The Query
class defaults to querying Irys' mainnet. To search Irys' devnet or Arweave, use the network
parameter.
Irys mainnet
// Either will work
const myQuery = new Query();
const myQuery = new Query({ network: "mainnet" });
Irys devnet
const myQuery = new Query({ network: "devnet" });
Arweave
const myQuery = new Query({ network: "arweave" });
Overriding default Endpoint
You can override the default GraphQL endpoint using the url
parameter. Most users will not need this feature.
const myQuery = new Query({ url: "https://arweave-search.goldsky.com/graphql" });
Query type
Using the Query
class users can search any of:
- Irys transactions
- Arweave transactions
- Arweave blocks
The search location is specified by passing a parameter to the search()
function.
const results = await myQuery.search("irys:transactions");
The selected search type influences the returned fields and the availability of specific query functions.
Function | irys:transactions | arweave:transactions | arweave:blocks |
---|---|---|---|
search() | Yes | Yes | Yes |
tags() | Yes | Yes | No |
ids() | Yes | Yes | Yes |
from() | Yes | Yes | No |
to() | No | Yes | No |
token() | Yes | No | No |
fromTimestamp() | Yes | No | Yes |
toTimestamp() | Yes | No | Yes |
minHeight() | No | No | Yes |
maxHeight() | No | No | Yes |
sort() | Yes | Yes | Yes |
limit() | Yes | Yes | Yes |
stream() | Yes | Yes | Yes |
fields() | Yes | Yes | Yes |
Timestamp
Use the fromTimestamp()
and toTimestamp()
functions to search for transactions by timestamp. Results returned are >= fromTimestamp
and < toTimestamp
.
You can search by passing Date
objects to the functions:
const results = await myQuery
.search("irys:transactions")
.fromTimestamp(new Date("2023-07-01"))
.toTimestamp(new Date("2023-07-03"));
Or by using UNIX timestamps in millisecond format:
const results = await myQuery
.search("irys:transactions")
.fromTimestamp(1688144401000)
.toTimestamp(1688317201000);
Irys timestamps are accurate to the millisecond, so you need to provide a timestamp in millisecond format. You can convert from human-readable time to UNIX timestamp using websites like Epoch101 (opens in a new tab), be sure to convert in millisecond format, not second.
Tags
Use the tags()
function to search metadata tags attached to transactions during upload.
Search for a single tag name / value pair:
const results = await myQuery
.search("irys:transactions")
.tags([{ name: "Content-Type", values: ["image/png"] }]);
Search for a single tag name with a list of possible values. The search uses OR logic and returns transactions tagged with ANY provided value.
const results = await myQuery
.search("irys:transactions")
.tags([{ name: "Content-Type", values: ["image/png", "image/jpg"] }]);
Search for multiple tags. The search uses AND logic and returns transactions tagged with ALL provided values.
const results = await myQuery.search("irys:transactions")
.tags([
{ name: "Content-Type", values: ["image/png"] },
{ name: "Application-ID", values: ["myApp"] },
]);
You can also search Arweave by tags:
const results = await myQuery
.search("arweave:transactions")
.tags([{ name: "Content-Type", values: ["image/png", "image/jpg"] }]);
Transaction id
Use the ids()
function to by transaction ID. The search uses OR logic and returns transactions tagged with ANY provided value:
const results = await myQuery
.search("irys:transactions")
.ids(["xXyv3u9nHHWGiMJl_DMgLwwRdOTlIlQZyqaK_rOkNZw", "_xE7tG1kl2FgCUDgJ5jNJeVA6R5kuys7A6f1qfh9_Kw"]);
You can also search Arweave by transaction ID.
const results = await myQuery
.search("arweave:transactions")
.ids(["xXyv3u9nHHWGiMJl_DMgLwwRdOTlIlQZyqaK_rOkNZw", "_xE7tG1kl2FgCUDgJ5jNJeVA6R5kuys7A6f1qfh9_Kw"]);
Transaction sender
Use the from()
function to search by wallet addresses used when signing and paying for the upload. Addresses from any of Irys' supported chains are accepted.
The search employs OR logic, returning transactions tagged with ANY provided value:
const results = await myQuery
.search("irys:transactions")
.from(["UsWPlOBHRyfWcbrlC5sV3-pNUjOQEI5WmDxLnypc93I", "0x4adDE0b3C686B4453e007994edE91A7832CF3c99"]);
When searching Arweave by transaction sender, only Arweave addresses are accepted:
const results = await myQuery
.search("arweave:transactions")
.from(["TrnCnIGq1tx8TV8NA7L2ejJJmrywtwRfq9Q7yNV6g2A"]);
Transaction recipient
Use the to()
function to search for the wallet address of the transaction recipient. This works on Arweave only and is used when there's a fund transfer.
const results = await myQuery
.search("arweave:transactions")
.to("TrnCnIGq1tx8TV8NA7L2ejJJmrywtwRfq9Q7yNV6g2A");
Token
Use the token()
function to search based on the token name used to pay for the upload. Any of these values are acceptable.
const results = await myQuery
.search("irys:transactions")
.token("solana");
Block id
Use the ids()
function to search for Arweave blocks with the specified IDs.
const results = await myQuery
.search("arweave:blocks")
.ids(["R0ZLe4RvHxLJLzI1Z9ppyYVWFyHW4D1YrxXKuA9PGrwkk2QAuXCnD1xOJe-QOz4l"]);
Block height
Use the mixHeight()
and maxHeight()
functions to search for blocks within the specified block height range. Results are >= minHeight and < maxHeight
.
const results = await myQuery
.search("arweave:blocks")
.minHeight(1188272)
.maxHeight(1188279);
Or for transactions within the specified block height range. Results are >= minHeight and < maxHeight
.
const results = await myQuery
.search("arweave:transactions")
.minHeight(1188272)
.maxHeight(1188279);
Converting timestamp to block height
Arweave block time is approximately 2 minutes. Use this code to find the closest block height for a given timestamp.
import Arweave from "@irys/arweave";
const arweave = new Arweave();
export const getBlock = async (height) => {
const timestamp = (await arweave.blocks.getByHeight(height)).timestamp;
if (typeof timestamp === "number" && isFinite(timestamp) && !isNaN(timestamp) && timestamp > 0)
return timestamp * 1000;
throw new Error(`Illegal block timestamp: ${timestamp}`);
};
export async function getBlockHeightFromTime(time: number, aimFor?: "before" | "after"): Promise<number> {
const currentHeight = (await arweave.network.getInfo()).height;
const avgBlockTime = 2 * 60 * 1000;
const estimateHeightDelta = Math.ceil((Date.now() - time) / avgBlockTime);
const estimateHeight = currentHeight - estimateHeightDelta;
// Get blocks from around the estimate
const height = estimateHeight;
let wobble = 0;
let closestDelta = Infinity;
let closestHeight = 0;
let twoClosest = 0; // Below will flip flop between two values at mimimum
for (let i = 0; i < 30; i++) {
const testHeight = height + wobble;
const timestamp = await getBlock(testHeight);
const cDelta = timestamp - time;
console.log(`Delta: ${cDelta / 1000}s, height: ${testHeight}, Date: ${new Date(timestamp)}`);
if (cDelta === twoClosest) break;
if (i % 2 === 0) twoClosest = cDelta;
if (Math.abs(cDelta) > 20 * 60 * 1000) {
wobble += Math.floor((cDelta / avgBlockTime) * 0.75) * -1;
} else {
wobble += cDelta > 0 ? -1 : 1;
}
if (Math.abs(cDelta) < Math.abs(closestDelta)) {
closestDelta = cDelta;
closestHeight = testHeight;
}
}
// Before will have -ve delta, after will have +ve delta
if (aimFor === "before") {
if (closestDelta > 0) {
closestHeight -= 1;
}
} else if (aimFor === "after") {
if (closestDelta < 0) {
closestHeight += 1;
}
}
return closestHeight;
}
Sorting
Use the sort()
function to sort results by timestamp in ascending order:
const results = await myQuery
.search("irys:transactions")
.token("ethereum")
.sort("ASC");
or descending order:
const results = await myQuery
.search("irys:transactions")
.token("ethereum")
.sort("DESC");
First result
Use the first()
function to return only the first result:
const results = await myQuery
.search("irys:transactions")
.tags([{ name: "Content-Type", values: ["image/png"] }])
.first();
Limiting search results
Use the limit()
function to limit the maximum number of results returned. This overrides the default value of 1000 results when searching Irys and 100 when searching Arweave directly.
const results = await myQuery
.search("irys:transactions")
.ids(["xXyv3u9nHHWGiMJl_DMgLwwRdOTlIlQZyqaK_rOkNZw", "_xE7tG1kl2FgCUDgJ5jNJeVA6R5kuys7A6f1qfh9_Kw"])
.limit(20);
Pagination / streaming
Use the stream()
function to manage large results sets. This function returns an iterable stream that continuously yields results as long as your query keeps producing them.
// Create the stream
const stream = await myQuery
.search("irys:transactions")
.token("solana")
.stream();
// Iterate over the results
for await (const result of stream) {
console.log(result);
}
Limiting fields returned
Use the fields()
function to limit the fields returned. To limit the results, set a field's value to false
or omit it entirely.
The fields available for retrieval depend on the search type, when searching irys:transactions
, the following fields are available:
.fields({
id: true, // Transaction ID
token: true, // Token used for payment
address: true, // Cross-chain address used for signing and payment
receipt: {
deadlineHeight: true, // The block number by which the transaction must be finalized on Arweave
signature: true, // A signed deep hash of the JSON receipt
timestamp: true, // Timestamp, millisecond accurate, of the time the uploaded was verified
version: true, // The receipt version, currently 1.0.0
},
tags: { // An array of tags associated with the upload
name: true,
value: true,
},
signature: true, // A signed deep hash of the JSON receipt
timestamp: true, // Timestamp, millisecond accurate, of the time the uploaded was verified
})
When searching by arweave:transactions
the following fields are available:
.fields({
id: true, // Transaction ID
tags: {
// Tags associated with the upload
name: true,
value: true,
},
anchor: true,
block: {
height: true, // Block height
id: true, // Block ID
previous: true, // Todo
timestamp: true, // Block timestamp
},
bundledIn: {
id: true,
},
data: {
size: true, // Data size
type: true, // Date type
},
fee: {
ar: true, // Fee paid in AR
winston: true, // Fee paid in Winston
},
owner: {
address: true, // Transation originator
key: true, // Public key
},
quantity: {
ar: true, // Amount of AR transferred (for token transfers)
winston: true, // Amount of AR transferred (for token transfers)
},
recipient: true, // Transfer recipient (for token transfers)
signature: true, // Transaction signature
})
When searching by arweave:blocks
the following fields are available:
.fields({
height: true,
id: true,
previous: true,
timestamp: true,
})