ZigZag Exchange Arweave Bridge - Pay with zkSync stablecoins (USDC/USDT/DAI) for permanent Arweave storage...
The Arweave Bridge is a service built by ZigZag Exchange that enables zkSync transactions to access permanent storage on Arweave. It provides a seamless way for Layer 2 users to store data permanently without needing to acquire AR tokens directly.
Core Value Proposition: Access Arweave permanent storage at $1/MB by paying directly on zkSync with stablecoins.
This skill should be triggered when:
https://zigzag-arweave-bridge.herokuapp.com/
0xcb7aca0cdea76c5bd5946714083c559e34627607
1 MB per $1 of stablecoin deposited
Query remaining storage bytes for an address.
Endpoint:
GET /allocation/zksync?address={wallet_address}
Response:
{
"remaining_bytes": 1048576
}
Example:
curl "https://zigzag-arweave-bridge.herokuapp.com/allocation/zksync?address=0xYourWalletAddress"
Get current server timestamp for signature generation.
Endpoint:
GET /time
Response:
{
"timestamp": 1640000000000
}
Example:
curl "https://zigzag-arweave-bridge.herokuapp.com/time"
Upload a file to Arweave permanent storage.
Endpoint:
POST /arweave/upload
Content-Type: multipart/form-data
Required Fields:
| Field | Type | Description |
|---|---|---|
sender |
string | Ethereum wallet address |
file |
file | The file to upload |
timestamp |
number | Current server timestamp (ms) |
signature |
string | ECDSA signature of {sender}:{timestamp} |
Response:
{
"arweave_tx_id": "abc123...",
"remaining_bytes": 1000000
}
All uploads require cryptographic signature verification:
/time{sender_address}:{timestamp}Message Format:
0xYourAddress:1640000000000
import { FormData, fileFromPath } from "formdata-node";
import fetch from "node-fetch";
import { ethers } from "ethers";
import dotenv from "dotenv";
dotenv.config();
const BASE_URL = "https://zigzag-arweave-bridge.herokuapp.com";
async function uploadToArweave(filePath) {
// 1. Get current server time
const timeResponse = await fetch(`${BASE_URL}/time`);
const { timestamp } = await timeResponse.json();
// 2. Create wallet and sign message
const wallet = new ethers.Wallet(process.env.ETH_PRIVKEY);
const sender = wallet.address;
const message = `${sender}:${timestamp}`;
const signature = await wallet.signMessage(message);
// 3. Prepare form data
const formData = new FormData();
formData.append("sender", sender);
formData.append("timestamp", timestamp.toString());
formData.append("signature", signature);
formData.append("file", await fileFromPath(filePath));
// 4. Upload file
const response = await fetch(`${BASE_URL}/arweave/upload`, {
method: "POST",
body: formData,
});
const result = await response.json();
console.log("Arweave TX ID:", result.arweave_tx_id);
console.log("Remaining bytes:", result.remaining_bytes);
return result;
}
// Usage
uploadToArweave("./my-file.json");
{
"dependencies": {
"formdata-node": "^4.0.0",
"node-fetch": "^3.0.0",
"ethers": "^5.0.0",
"dotenv": "^10.0.0"
}
}
# Required for signing uploads
ETH_PRIVKEY=your_private_key_here
Send stablecoins on zkSync to the bridge address:
Address: 0xcb7aca0cdea76c5bd5946714083c559e34627607
Network: zkSync
Tokens: USDC, USDT, or DAI
Rate: $1 = 1 MB storage
Credits typically appear within 1-2 minutes.
const response = await fetch(
`${BASE_URL}/allocation/zksync?address=${yourAddress}`
);
const { remaining_bytes } = await response.json();
console.log(`Available storage: ${remaining_bytes / 1024 / 1024} MB`);
Use the complete upload example above, ensuring:
Once uploaded, your file is permanently stored on Arweave:
https://arweave.net/{arweave_tx_id}
// Store NFT metadata permanently
const metadata = {
name: "My NFT",
description: "A permanent NFT",
image: "https://arweave.net/previous_image_tx_id",
attributes: [...]
};
// Write to temp file and upload
fs.writeFileSync("/tmp/metadata.json", JSON.stringify(metadata));
const result = await uploadToArweave("/tmp/metadata.json");
// Use Arweave URL as NFT tokenURI
const tokenURI = `https://arweave.net/${result.arweave_tx_id}`;
// Store token pair metadata for DEX listing
const pairMetadata = {
baseToken: "0x...",
quoteToken: "0x...",
icon: "base64_image_data",
description: "Trading pair info"
};
const result = await uploadToArweave(pairMetadataPath);
// Metadata now permanently accessible and verifiable
// Archive important documents permanently
const documents = ["contract.pdf", "agreement.pdf", "records.json"];
for (const doc of documents) {
const result = await uploadToArweave(doc);
console.log(`${doc} archived: https://arweave.net/${result.arweave_tx_id}`);
}
| Error | Cause | Solution |
|---|---|---|
| Invalid signature | Wrong private key or message format | Verify {sender}:{timestamp} format |
| Timestamp expired | Request took too long | Get fresh timestamp and retry |
| Insufficient allocation | Not enough storage credits | Send more stablecoins to bridge |
| Invalid sender | Address doesn't match signature | Ensure sender matches signing wallet |
{
"error": "Invalid signature",
"message": "The provided signature does not match the sender address"
}
ETH_PRIVKEY in client-side codeβββββββββββββββ βββββββββββββββββββββββ βββββββββββββββ
β User ββββββΆβ Arweave Bridge ββββββΆβ Arweave β
β (zkSync) β β (Heroku Server) β β (Storage) β
βββββββββββββββ βββββββββββββββββββββββ βββββββββββββββ
β β
β USDC/USDT/DAI β Manages allocations
βΌ β Validates signatures
βββββββββββββββ β Uploads to Arweave
β Bridge ββββββββββββββββ
β Address β
βββββββββββββββ