@irys/arweave
@irys/arweave is a TypeScript/JavaScript SDK for interacting with Arweave and uploading data to the permaweb. It works in latest browsers and Node JS.
The package is a fork of arweave-js that introduces better typing, Improved NodeJS compatibility, and better support for using streams by integrating code from arweave-stream-tx.
Note:
If you are planning to upload large batches of data transactions to Arweave, it is strongly advised that you use ArBundles or Irys instead of transactions with @irys/arweave
. You can read about bundles and their advantages on the Arwiki.
Installation
Npm
npm install @irys/arweave
Yarn
yarn add @irys/arweave
Bundles
Include the package via import statement when possible. If a script tag is required, use the examples below.
<script src="https://unpkg.com/@irys/arweave/build/web.bundle.js"></script>
<script src="https://unpkg.com/@irys/arweave/build/web.bundle.min.js"></script>
<script src="https://unpkg.com/@irys@1.0.0/arweave/build/web.bundle.js"></script>
<script src="https://unpkg.com/@irys@1.0.0/arweave/build/web.bundle.min.js"></script>
Initialization
import Arweave from "@irys/arweave";
const arweave = new Arweave({ url: "http://127.0.0.1:1984" });
const arweave = new Arweave({ url: "https://arweave.net" });
const arweave = new Arweave(["https://arweave.net", "https://arweave.dev", "https://your-gateway"], {
miners: ["http://127.0.0.1:1984", "http://52.38.214.72:1984"],
});
Web bundles
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello world</title>
<script src="https://unpkg.com/@irys/arweave/build/web.bundle.js"></script>
<script>
const arweave = new Arweave({ url: "https://arweave.net" })
arweave.network.getInfo().then(console.log);
</script>
</head>
<body>
</body>
</html>
Initialization options
(
gateways: string, URL, or ApiConfig - Array or single item
options: {
gateways?: same as above
crypto?: Custom implementation of CryptoInterface
miners?: same as gateways, but for miner nodes
}
)
Usage
Wallets and keys
Creating a new wallet and private key
To generate a new wallet address and private key (JWK) use the function arweave.wallets.generate()
. Once AR has been sent to the address for a new wallet, the key can then be used to sign outgoing transactions.
Don't expose private keys or make them public as anyone with the key can use the corresponding wallet.
Make sure your keys stored securely as they can never be recovered if lost.
arweave.wallets.generate().then((key) => {
console.log(key);
});
Getting the wallet address for a private key
arweave.wallets.jwkToAddress(key).then((address) => {
console.log(address);
});
Getting an address balance
All amounts are returned in Winston.
arweave.wallets.getBalance("1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY").then((balance) => {
let winston = balance;
let ar = arweave.utils.winstonToAr(balance);
console.log(winston);
console.log(ar);
});
Getting the last transaction ID from a wallet
arweave.wallets.getLastTransactionID("1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY").then((transactionId) => {
console.log(transactionId);
});
Transactions
Transactions are the building blocks of Arweave. They can send AR between wallet addresses or store data on Arweave.
The create transaction methods create and return an unsigned transaction object. You must sign the transaction and submit it separately using the transactions.sign()
and transactions.submit()
methods.
If you don't pass a key
argument when creating a transaction, @irys/arweave will attempt to use a browser-based wallet extension, such as ArConnect or Arweave.app to sign the transaction.
Modifying a transaction object after signing it will invalidate the signature, causing it to be rejected by the network if submitted in that state. Transaction prices are based on the size of the data field, so modifying the data field after a transaction has been created isn't recommended as you'll need to manually update the price.
The transaction ID is a hash of the transaction signature, so a transaction ID can't be known until its contents are finalized and it has been signed.
Creating a data transaction
Note: If you are planning to upload large batches of data transactions to Arweave, it is strongly advised that you use ArBundles instead of transactions with @irys/arweave
. You can read about bundles and their advantages on the Arwiki.
Data transactions are used to store data on Arweave. They can contain HTML or any arbitrary data and are served like webpages.
let key = await arweave.wallets.generate();
let transactionA = await arweave.createTransaction(
{
data: '<html><head><meta charset="UTF-8"><title>Hello world!</title></head><body></body></html>',
},
key,
);
let transactionB = await arweave.createTransaction(
{
data: Buffer.from("Some data", "utf8"),
},
key,
);
console.log(transactionA);
Creating a wallet to wallet transaction
Wallet to wallet transactions facilitate payments from one wallet to another. Supply a target wallet, and AR quantity in Winston.
let key = await arweave.wallets.generate();
let transaction = await arweave.createTransaction(
{
target: "1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY",
quantity: arweave.utils.arToWinston("10.5"),
},
key,
);
console.log(transaction);
Adding tags to a transaction
Metadata can be added to transactions through tags, these are simple key/value pairs that can be used to document the contents of a transaction or provide related data.
GraphQL uses tags when searching for transactions.
The Content-Type
tag is a reserved tag and is used to set the data content type. For example, a transaction with HTML data and a content type tag of text/html
will be served as a HTML page and render correctly in browsers, if the content type is set to text/plain
then it will be served as a plain text document and will not render in browsers.
If you omit the Content-Type
tag, browsers will be unable to render the content.
let key = await arweave.wallets.generate();
let transaction = await arweave.createTransaction(
{
data: '<html><head><meta charset="UTF-8"><title>Hello world!</title></head><body></body></html>',
},
key,
);
transaction.addTag("Content-Type", "text/html");
transaction.addTag("key2", "value2");
console.log(transaction);
Signing a transaction
let key = await arweave.wallets.generate();
let transaction = await arweave.createTransaction(
{
target: "1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY",
quantity: arweave.utils.arToWinston("10.5"),
},
key,
);
await arweave.transactions.sign(transaction, key);
console.log(transaction);
Submitting a transaction
The preferred method of submitting a data transaction is to use chunk uploading. This method allows larger transaction sizes, resuming an upload if it's interrupted, and give progress updates while uploading.
The following code has been simplified and ignores potential errors.
let data = fs.readFileSync("path/to/file.pdf");
let transaction = await arweave.createTransaction({ data: data }, key);
transaction.addTag("Content-Type", "application/pdf");
await arweave.transactions.sign(transaction, key);
let uploader = await arweave.transactions.getUploader(transaction);
while (!uploader.isComplete) {
await uploader.uploadChunk();
console.log(`${uploader.pctComplete}% complete, ${uploader.uploadedChunks}/${uploader.totalChunks}`);
}
You can also submit transactions using transactions.post()
which is suitable for small transactions or token transfers:
let key = await arweave.wallets.generate();
let transaction = await arweave.createTransaction(
{
target: "1seRanklLU_1VTGkEk7P0xAwMJfA7owA1JHW5KyZKlY",
quantity: arweave.ar.arToWinston("10.5"),
},
key,
);
await arweave.transactions.sign(transaction, key);
const response = await arweave.transactions.post(transaction);
console.log(response.status);
The 200
responses means a node has received your transaction, it does not mean that the transaction has mined and confirmed, and that a txid can be used as if it's immutable. See get a transaction status for more detail on how to correctly determine that your transaction has been mined and confirmed. This also applies to the uploader()
method.
Chunked uploading advanced options
You can resume an upload from a saved uploader object that you have persisted in storage some using
JSON.stringify(uploader)
at any stage of the upload. To resume, parse it back into an object and pass it to getUploader()
along with the transactions data:
let data = fs.readFileSync("path/to/file.pdf");
let resumeObject = JSON.parse(savedUploader);
let uploader = await arweave.transactions.getUploader(resumeObject, data);
while (!uploader.isComplete) {
await uploader.uploadChunk();
}
When resuming the upload, you must provide the same data as the original upload. When you serialize the uploader object with JSON.stringify()
to save it somewhere, it will not include the data.
You can also resume an upload from just the transaction ID and data, once it has been mined into a block. This can be useful if you didn't save the uploader somewhere but the upload got interrupted. This will re-upload all of the data from the beginning, since we don't know which parts have been uploaded:
let data = fs.readFileSync("path/to/file.pdf");
let resumeTxId = "mytxid";
let uploader = await arweave.transactions.getUploader(resumeTxId, data);
while (!uploader.isComplete) {
await uploader.uploadChunk();
console.log(`${uploader.pctComplete}% complete`);
}
Alternatively:
import Arweave from "./node/index.js";
import ArweaveTransaction from "./node/lib/transaction.js";
import fs from "fs";
const arweave = Arweave.init({
host: "arweave.net",
port: 443,
protocol: "https",
});
let missingData = fs.readFileSync("./myfile.mov");
let txHeaders = require("./txheaders.json");
(async () => {
const tx = new ArweaveTransaction.default(txHeaders);
let uploader = await arweave.transactions.getUploader(tx, missingData);
while (!uploader.isComplete) {
await uploader.uploadChunk();
}
})();
There is also an async iterator interface to chunk uploading, but this method means you'll need to ensure you are using a transpiler and polyfill for the asyncIterator symbol for some environments (Safari on iOS in particular). This method takes the same arguments for uploading/resuming a transaction as getUploader()
and just has a slightly shorter syntax:
for await (const uploader of arweave.transactions.upload(tx)) {
console.log(`${uploader.pctComplete}% Complete`);
}
Streaming uploads
@irys/arweave includes (for NodeJS) a modified version of arweave-stream-tx to allow for the
efficient uploading of transactions of arbitrary size.
import { pipeline } from "stream/promises";
const arweave = new Arweave(...)
const tx = await pipeline(createReadStream("./<path-to-file>"), createTransactionAsync({}))
await arweave.transactions.sign(tx)
await pipeline(createReadStream("./<path-to-file>"), uploadTransactionAsync(tx, arweave))
Getting transaction status
Just like other blockchains (including Bitcoin and Ethereum), you should always ensure that your transaction has received enough block confirmations before you assume that the transaction has been fully accepted by the network.
We strongly advise that you check the status and number of confirmations for a given txid before using it, even if you have received a ‘200’ status response.
arweave.transactions.getStatus("bNbA3TEQVL60xlgCcqdz4ZPHFZ711cZ3hmkpGttDt_U").then((res) => {
console.log(res);
});
Getting a transaction
Unlike arweave-js
the returned data and tags are automatically decoded from base64.
Due to how the API has evolved over time and with larger transaction support, the data
field is no longer guaranteed to be returned from the network as part of the transaction json, therefore, it is not recommended that you use this function for fetching data. You should use arweave.transactions.getData()
instead, as it will handle small transactions, as well as the reassembling of chunks for larger ones while benefiting from gateway optimisations.*
const transaction = arweave.transactions.get("hKMMPNh_emBf8v_at1tFzNYACisyMQNcKzeeE1QE9p8").then((transaction) => {
console.log(transaction);
});
Getting transaction data
You can get the transaction data from a transaction ID without having to get the entire transaction
arweave.transactions.getData("bNbA3TEQVL60xlgCcqdz4ZPHFZ711cZ3hmkpGttDt_U").then((data) => {
console.log(data);
});
arweave.transactions.getData("bNbA3TEQVL60xlgCcqdz4ZPHFZ711cZ3hmkpGttDt_U", { decode: true }).then((data) => {
console.log(data);
});
arweave.transactions.getData("bNbA3TEQVL60xlgCcqdz4ZPHFZ711cZ3hmkpGttDt_U", { decode: true, string: true }).then((data) => {
console.log(data);
});
Getting tags from transactions
Unlike arweave-js
, tags are automatically decoded:
const transaction = arweave.transactions.get("bNbA3TEQVL60xlgCcqdz4ZPHFZ711cZ3hmkpGttDt_U").then((transaction) => {
transaction["tags"].forEach(({ name, value }) => {
console.log(`${name} : ${value}`);
});
});
Blocks
Blocks are base elements of Arweave's blockweave data structure.
Each block is linked to two prior blocks: the previous block in the "chain" (as with traditional blockchain
protocols), and a block from the previous history of the blockchain (the "recall block"). Each block contains
a list of zero to many transactions.
Getting a block by indep_hash
Gets block data for given independent hash.
const result = await arweave.blocks.getByHash("zbUPQFA4ybnd8h99KI9Iqh4mogXJibr0syEwuJPrFHhOhld7XBMOUDeXfsIGvYDp");
console.log(result);
Getting block by height
const result = await arweave.blocks.getByHeight(711150);
console.log(result);
Getting current block
Gets a block data for current block, i.e., block with indep_hash:
const { current } = await arweave.network.getInfo();
const result = await arweave.blocks.getCurrent();
console.log(result);
GraphQL
GraphQL (GQL) provides flexible querying and allows you to search for transactions by tags, wallet address, block height, and more.
Please see the GQL Guide for further details.
License
This software is released under MIT license. See LICENSE.md for full license details.