A comprehensive JavaScript/TypeScript SDK for working with Algorand ARC (Algorand Request for Comments) standards. Supports both Node.js and browser environments with utilities for creating, managing, and querying NFTs and blockchain data.
View the documentation here.
npm install arcraft
Traditional NFT standard where metadata is stored externally (typically on IPFS) and referenced via URL.
Enhanced NFT standard with template-based IPFS URIs that allow for more efficient storage and updatable metadata through reserve address manipulation.
NFT standard where metadata is embedded directly in transaction notes, eliminating the need for external storage.
URI scheme standard for querying application and asset data directly from the Algorand blockchain.
import { Arc3, IPFS } from 'arcraft';
import algosdk, { makeBasicAccountTransactionSigner } from 'algosdk';
async function createARC3NFT() {
// Initialize IPFS provider
const ipfs = new IPFS('pinata', {
provider: 'pinata',
jwt: 'YOUR_PINATA_JWT_TOKEN',
});
// Create account from mnemonic
const account = algosdk.mnemonicToSecretKey('your mnemonic phrase here');
// Create ARC-3 NFT
const result = await Arc3.create({
name: 'My First NFT',
unitName: 'MYNFT',
creator: {
address: account.addr,
signer: makeBasicAccountTransactionSigner(account),
},
ipfs,
image: {
file: './artwork.jpg', // Node.js: file path, Browser: File object
name: 'artwork.jpg',
},
properties: {
description: 'My first NFT using Arcraft',
collection: 'Test Collection',
traits: [
{ trait_type: 'Color', value: 'Blue' },
{ trait_type: 'Rarity', value: 'Common' },
],
},
network: 'testnet',
});
console.log(`NFT Created! Asset ID: ${result.assetId}`);
}
import { Arc19, IPFS } from 'arcraft';
async function createARC19NFT() {
const ipfs = new IPFS('filebase', {
provider: 'filebase',
token: 'YOUR_FILEBASE_TOKEN',
});
const account = algosdk.mnemonicToSecretKey('your mnemonic phrase here');
// Create ARC-19 NFT
const result = await Arc19.create({
name: 'Updatable NFT',
unitName: 'UPNFT',
creator: {
address: account.addr,
signer: makeBasicAccountTransactionSigner(account),
},
ipfs,
image: {
file: './artwork.jpg',
name: 'artwork.jpg',
},
properties: {
description: 'An NFT with updatable metadata',
version: '1.0.0',
},
network: 'testnet',
});
console.log(`ARC-19 NFT Created! Asset ID: ${result.assetId}`);
// Later, update the NFT metadata
await Arc19.update({
manager: {
address: account.addr,
signer: makeBasicAccountTransactionSigner(account),
},
properties: {
description: 'Updated NFT description',
version: '2.0.0',
},
assetId: result.assetId,
ipfs,
network: 'testnet',
});
}
import { Arc69, IPFS } from 'arcraft';
async function createARC69NFT() {
const ipfs = new IPFS('pinata', {
provider: 'pinata',
jwt: 'YOUR_PINATA_JWT_TOKEN',
});
const account = algosdk.mnemonicToSecretKey('your mnemonic phrase here');
// Create ARC-69 NFT (metadata stored in transaction notes)
const result = await Arc69.create({
name: 'Embedded Metadata NFT',
unitName: 'EMNFT',
creator: {
address: account.addr,
signer: makeBasicAccountTransactionSigner(account),
},
ipfs,
image: {
file: './image.png',
name: 'image.png',
},
properties: {
standard: 'arc69',
description: 'NFT with metadata in transaction notes',
external_url: 'https://example.com',
attributes: [
{ trait_type: 'Background', value: 'Sunset' },
{ trait_type: 'Character', value: 'Robot' },
],
},
network: 'testnet',
});
console.log(`ARC-69 NFT Created! Asset ID: ${result.assetId}`);
}
import { Arc82 } from 'arcraft';
async function queryBlockchainData() {
// Parse an ARC-82 URI
const uri = 'algorand://app/123456?box=YWNjb3VudA==&global=dG90YWw=';
const parsed = Arc82.parse(uri);
console.log('Parsed URI:', parsed);
// Query application data
const appResult = await Arc82.queryApplication(parsed, 'mainnet');
console.log('Application Data:', appResult);
// Query asset data
const assetUri = 'algorand://asset/789012?total=true&decimals=true&url=true';
const assetParsed = Arc82.parse(assetUri);
const assetResult = await Arc82.queryAsset(assetParsed, 'mainnet');
console.log('Asset Data:', assetResult);
// Build URIs programmatically
const newAppUri = Arc82.buildAppUri(123456, {
box: ['YWNjb3VudA=='],
global: ['dG90YWw='],
tealcode: true,
});
console.log('Built URI:', newAppUri);
}
import { IPFS, uploadToPinata } from 'arcraft';
// Using IPFS class
const ipfs = new IPFS('pinata', {
provider: 'pinata',
jwt: 'YOUR_PINATA_JWT_TOKEN',
});
// Direct upload function
const result = await uploadToPinata({
file: './image.jpg', // Node.js: path, Browser: File object
name: 'my-image.jpg',
token: 'YOUR_PINATA_JWT_TOKEN',
});
console.log(`IPFS Hash: ${result.IpfsHash}`);
import { IPFS, uploadToFilebase } from 'arcraft';
// Using IPFS class
const ipfs = new IPFS('filebase', {
provider: 'filebase',
token: 'YOUR_FILEBASE_TOKEN',
});
// Direct upload function
const result = await uploadToFilebase({
file: './document.pdf',
name: 'document.pdf',
token: 'YOUR_FILEBASE_TOKEN',
});
console.log(`IPFS CID: ${result.cid}`);
// Both providers implement the same interface
async function uploadWithProvider(ipfs) {
// Upload file
const imageCid = await ipfs.upload(file, 'filename.jpg');
// Upload JSON metadata
const metadataCid = await ipfs.uploadJson(
{
name: 'My NFT',
description: 'A beautiful NFT',
image: `ipfs://${imageCid}`,
},
'metadata.json'
);
return { imageCid, metadataCid };
}
// Create new ARC-3 NFT
const result = await Arc3.create(options);
// Load existing ARC-3 NFT
const nft = await Arc3.fromId(assetId, network);
// Check if asset is ARC-3 compliant
const isCompliant = nft.isArc3();
// Get metadata
const metadata = nft.getMetadata();
// Get image URL
const imageUrl = nft.getImageUrl();
// Get image as base64
const base64Image = await nft.getImageBase64();
// Create new ARC-19 NFT
const result = await Arc19.create(options);
// Load existing ARC-19 NFT
const nft = await Arc19.fromId(assetId, network);
// Update NFT metadata
await Arc19.update(updateOptions);
// Get all metadata versions
const versions = await Arc19.getMetadataVersions(assetId, network);
// Check if URL is valid ARC-19 format
const isValid = Arc19.hasValidUrl(url);
// Resolve template URL to actual IPFS URL
const resolvedUrl = Arc19.resolveUrl(templateUrl, reserveAddress);
// Create new ARC-69 NFT
const result = await Arc69.create(options);
// Load existing ARC-69 NFT
const nft = await Arc69.fromId(assetId, network);
// Update NFT metadata
await Arc69.update(updateOptions);
// Get all metadata versions
const versions = await Arc69.getMetadataVersions(assetId, network);
// Check if asset has valid ARC-69 metadata
const hasValidMetadata = await Arc69.hasValidMetadata(assetId, network);
// Parse ARC-82 URI
const parsed = Arc82.parse(uri);
// Query application data
const appData = await Arc82.queryApplication(parsed, network);
// Query asset data
const assetData = await Arc82.queryAsset(parsed, network);
// Build application URI
const appUri = Arc82.buildAppUri(appId, queryParams);
// Build asset URI
const assetUri = Arc82.buildAssetUri(assetId, queryParams);
// Validate URI format
const isValid = Arc82.isValidArc82Uri(uri);
// Extract ID from URI
const id = Arc82.extractId(uri);
// Extract type from URI
const type = Arc82.extractType(uri);
// Initialize IPFS with provider
const ipfs = new IPFS(provider, config);
// Upload file
const cid = await ipfs.upload(file, fileName);
// Upload JSON
const jsonCid = await ipfs.uploadJson(jsonObject, fileName);
// Load asset by ID
const asset = await CoreAsset.fromId(assetId, network);
// Get asset properties
const creator = asset.getCreator();
const manager = asset.getManager();
const reserve = asset.getReserve();
const freeze = asset.getFreeze();
const clawback = asset.getClawback();
const name = asset.getName();
const unitName = asset.getUnitName();
const url = asset.getUrl();
const total = asset.getTotalSupply();
const decimals = asset.getDecimals();
const defaultFrozen = asset.getDefaultFrozen();
const metadataHash = asset.getMetadataHash();
// Node.js Environment
import { uploadToPinata } from 'arcraft';
import path from 'path';
const nodeUpload = await uploadToPinata({
file: path.resolve('./assets/image.png'),
name: 'image.png',
token: 'YOUR_TOKEN',
});
// Browser Environment
const browserUpload = async (fileInput) => {
const file = fileInput.files[0];
const result = await uploadToPinata({
file: file,
name: file.name,
token: 'YOUR_TOKEN',
});
return result;
};
import { Arc3, IPFS } from 'arcraft';
async function createNFTCollection() {
const ipfs = new IPFS('pinata', {
provider: 'pinata',
jwt: 'YOUR_JWT',
});
const account = algosdk.mnemonicToSecretKey('your mnemonic');
const creator = {
address: account.addr,
signer: makeBasicAccountTransactionSigner(account),
};
const artworks = [
{
file: './art1.png',
name: 'Sunset Dreams',
traits: [{ trait_type: 'Theme', value: 'Nature' }],
},
{
file: './art2.png',
name: 'City Lights',
traits: [{ trait_type: 'Theme', value: 'Urban' }],
},
{
file: './art3.png',
name: 'Ocean Waves',
traits: [{ trait_type: 'Theme', value: 'Water' }],
},
];
const results = [];
for (const [index, artwork] of artworks.entries()) {
const result = await Arc3.create({
name: artwork.name,
unitName: `ART${index + 1}`,
creator,
ipfs,
image: artwork.file,
imageName: `${artwork.name.replace(/\s+/g, '_').toLowerCase()}.png`,
properties: {
description: `Artwork #${index + 1} from the Dreams Collection`,
collection: 'Dreams Collection',
edition: index + 1,
total_editions: artworks.length,
attributes: artwork.traits,
},
network: 'testnet',
});
results.push(result);
console.log(`Created NFT #${index + 1}: Asset ID ${result.assetId}`);
}
return results;
}
import { Arc82 } from 'arcraft';
async function complexBlockchainQuery() {
// Query multiple box keys and global state
const appUri = Arc82.buildAppUri(123456, {
box: [
Arc82.encodeBase64Url('user_balance'),
Arc82.encodeBase64Url('total_supply'),
Arc82.encodeBase64Url('admin_settings'),
],
global: [
Arc82.encodeBase64Url('contract_version'),
Arc82.encodeBase64Url('paused'),
],
local: [
{
key: Arc82.encodeBase64Url('user_score'),
algorandaddress: 'ALGORAND_ADDRESS_HERE',
},
],
tealcode: true,
});
console.log('Generated URI:', appUri);
const parsed = Arc82.parse(appUri);
const result = await Arc82.queryApplication(parsed, 'mainnet');
// Process results
if (result.success) {
console.log('Box storage results:', result.boxes);
console.log('Global state results:', result.global);
console.log('Local state results:', result.local);
console.log('TEAL code:', result.tealCode);
} else {
console.error('Query failed:', result.error);
}
}
import { Arc19, Arc69 } from 'arcraft';
async function trackMetadataVersions(assetId, network) {
try {
// Get ARC-19 metadata versions
const arc19Versions = await Arc19.getMetadataVersions(assetId, network);
console.log('ARC-19 Metadata Versions:', arc19Versions);
// Get ARC-69 metadata versions
const arc69Versions = await Arc69.getMetadataVersions(assetId, network);
console.log('ARC-69 Metadata Versions:', arc69Versions);
// Compare versions and show evolution
if (arc19Versions.length > 0) {
console.log('ARC-19 Evolution:');
arc19Versions.forEach((version, index) => {
console.log(`Version ${index + 1}:`, {
timestamp: version.timestamp,
metadataHash: version.metadataHash,
changes: version.properties,
});
});
}
} catch (error) {
console.error('Error tracking versions:', error);
}
}
Choose at least one IPFS provider:
This package is fully documented with TypeDoc comments. You can generate and view the documentation in several ways:
Visit our comprehensive online documentation: https://satishccy.github.io/arcraft/
# Clone the repository
git clone https://github.com/satishccy/arcraft.git
cd arcraft
# Install dependencies
npm install
# Generate TypeDoc documentation
npm run docs
# Open the generated documentation
open docs/index.html
If you're using VS Code or another TypeScript-aware editor, you'll get:
The generated TypeDoc documentation includes:
The package is organized into logical modules:
CoreAsset
: Base class for all Algorand Standard AssetsArc3
: ARC-3 NFT implementation with external metadataArc19
: ARC-19 NFT implementation with template IPFS URIsArc69
: ARC-69 NFT implementation with embedded metadataArc82
: ARC-82 blockchain data query implementationIPFS
: Universal IPFS integration supporting multiple providersutils
: Algorand client utilities and helper functionsmimeUtils
: Cross-platform MIME type detectionAssetFactory
: Smart factory for creating appropriate asset instancespinata
: Pinata IPFS service integrationfilebase
: Filebase IPFS service integrationFull TypeScript support with:
Comprehensive error handling with custom error classes:
Arc82ParseError
: Thrown when ARC-82 URIs cannot be parsedArc82QueryError
: Thrown when blockchain queries failWe welcome contributions! Here's how you can help:
git clone https://github.com/satishccy/arcraft.git
npm install
git checkout -b feature/amazing-feature
npm test
npm run lint:fix
npm run format
npm run docs
git commit -m 'Add amazing feature'
git push origin feature/amazing-feature
When contributing, please ensure all code is properly documented:
All public functions, classes, and interfaces must have:
/**
* Brief description of what the function does
* @param paramName - Description of the parameter
* @param optionalParam - Description (optional)
* @returns Description of return value
* @throws Error description when function can throw
* @example
* ```typescript
* // Usage example
* const result = await myFunction('example');
* ```
*/
MIT License - see the LICENSE file for details.
Built with ❤️ for the Algorand ecosystem