Query package

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, arweave)

The Query class defaults to querying Irys' mainnet.

Irys mainnet

// Either will work
const myQuery = new Query();
const myQuery = new Query({ network: "mainnet" });

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.

Functionirys:transactionsarweave:transactionsarweave:blocks
search()YesYesYes
tags()YesYesNo
ids()YesYesYes
from()YesYesNo
to()NoYesNo
token()YesNoNo
fromTimestamp()YesNoYes
toTimestamp()YesNoYes
minHeight()NoNoYes
maxHeight()NoNoYes
sort()YesYesYes
limit()YesYesYes
stream()YesYesYes
fields()YesYesYes

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,
})