Archemist Docs
Back to App
v1.2.0 • Arc Network

Introduction

Welcome to the Archemist Protocol documentation. Archemist is the premier fair-launch platform on Arc Network, utilizing advanced bonding curves to guarantee instant liquidity and transparent pricing for the next generation of meme tokens.

Automated Liquidity

Smart contracts handle all trading. No order books, no manual liquidity provision required.

Anti-Rug Protection

Liquidity is algorithmically locked and automatically migrated to the DEX upon graduation.


How it Works

Archemist implements a Constant Product Bonding Curve (x * y = k). Prices are deterministic based on supply.

Graduation Config

GRADUATION GOAL
5,000 USDC

Once the bonding curve collects 5,000 USDC, liquidity is migrated to the DEX. The curve uses a 2,000 USDC virtual reserve.

  • Minting: Buying tokens mints them directly from the contract.
  • Burning: Selling tokens burns them and returns USDC from the reserve.
  • Fees: A 1% fee is applied. 50% goes to the creator instantly.

Launch Guide

Agents can launch without the web UI by uploading the token image through the API, mining a CREATE2 salt, calling the Factory contract, then posting metadata with the receipt addresses or the salt miner's predicted addresses.

Agent Launch via Contract + API

Recommended flow

Upload image first so the returned url can be attached to the token metadata after createBonding confirms. The on-chain contract does not store the image or description; display metadata is stored through POST /api/tokens.

POST /api/tokens body
{
  "bondingAddress": "0x...",
  "tokenAddress": "0x...",
  "creatorAddress": "0x...",
  "name": "Agent Coin",
  "symbol": "AGENT",
  "description": "Launched by an autonomous agent.",
  "imageUrl": "https://..."
}
agent_launch.js
// ---------------------------------------------
// Agent Launch Example (Node.js + ethers)
// ---------------------------------------------
const { ethers } = require("ethers");
const fs = require("fs");
const path = require("path");

const API_URL = process.env.ARCHEMIST_API_URL || "https://api.archemist.fun";
const RPC_URL = "https://rpc.testnet.arc.network";
const FACTORY_ADDRESS = "0xFd52Cf5A6B4B167aBf83D6781B5B4dfccb52Fd4E";

const FACTORY_ABI = [
  "function createBonding(string name,string symbol,bytes32 salt) payable returns (address)",
  "event TokenCreated(address indexed bondingContract,address indexed tokenAddress,string name,string symbol,address creator)"
];

const MIME_BY_EXT = {
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".png": "image/png",
  ".gif": "image/gif",
  ".webp": "image/webp"
};

function imageMimeType(imagePath) {
  const ext = path.extname(imagePath).toLowerCase();
  const mime = MIME_BY_EXT[ext];
  if (!mime) throw new Error("Image must be JPEG, PNG, GIF, or WebP");
  return mime;
}

async function uploadImage(imagePath, ticker) {
  const file = new Blob([fs.readFileSync(imagePath)], {
    type: imageMimeType(imagePath)
  });
  const form = new FormData();
  form.append("image", file, path.basename(imagePath));
  form.append("ticker", ticker);

  const res = await fetch(`${API_URL}/api/tokens/upload-image`, {
    method: "POST",
    body: form
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json(); // { url, cid, fileId }
}

async function mineSalt(name, symbol) {
  const res = await fetch(`${API_URL}/api/tokens/mine-salt?network=arc-testnet`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name, symbol })
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json(); // { success, salt, bondingAddress, tokenAddress, iterations, elapsedMs }
}

async function launchToken() {
  const token = {
    name: "Agent Coin",
    symbol: "AGENT",
    description: "Launched by an autonomous agent.",
    imagePath: "./agent.png",
    initialBuy: "0"
  };

  const provider = new ethers.JsonRpcProvider(RPC_URL);
  // Never expose PRIVATE_KEY in client-side code, logs, or public repositories.
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
  const factory = new ethers.Contract(FACTORY_ADDRESS, FACTORY_ABI, wallet);

  const image = await uploadImage(token.imagePath, token.symbol);
  const mined = await mineSalt(token.name, token.symbol);

  const tx = await factory.createBonding(token.name, token.symbol, mined.salt, {
    value: ethers.parseEther(token.initialBuy)
  });
  const receipt = await tx.wait();

  const event = receipt.logs
    .map((log) => {
      try { return factory.interface.parseLog(log); }
      catch { return null; }
    })
    .find((log) => log?.name === "TokenCreated");

  const bondingAddress = event?.args?.bondingContract || mined.bondingAddress;
  const tokenAddress = event?.args?.tokenAddress || mined.tokenAddress;

  if (!bondingAddress || !tokenAddress) {
    throw new Error("Missing bondingAddress/tokenAddress from receipt and salt miner response");
  }

  const metadataRes = await fetch(`${API_URL}/api/tokens`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      bondingAddress,
      tokenAddress,
      creatorAddress: wallet.address,
      name: token.name,
      symbol: token.symbol,
      description: token.description,
      imageUrl: image.url
    })
  });
  if (!metadataRes.ok) throw new Error(await metadataRes.text());

  return metadataRes.json();
}

launchToken().then(console.log).catch(console.error);

Image Upload API

Token images are uploaded as multipart form data to POST https://api.archemist.fun/api/tokens/upload-image. The API accepts JPEG, PNG, GIF, and WebP files up to 5MB and returns an IPFS-backed url when Pinata is configured.

upload_image.sh
curl -X POST "https://api.archemist.fun/api/tokens/upload-image" \
  -F "image=@./agent.png" \
  -F "ticker=AGENT"

Developer Reference

Arc Network

LIVE
Archemist Factory0xFd52Cf5A6B4B167aBf83D6781B5B4dfccb52Fd4E
DEX Router0xf88C80C61Ea67083Cec0A73ae061dfF7DFE0A6De
DEX Factory0x9442cb5b2bBF2009b1933c762f5B89eDCD3eaE08
Wrapped USDC (WUSDC)0x911b4000D3422F482F4062a913885f7b035382Df

Contracts ABI

Below are the essential ABIs to interact with the Factory and Bonding Curve contracts. Arc uses native USDC as the value asset; in ethers examples this is still passed through the transaction value field.

Factory ABI

BondingCurveFactory.json
[
  {
    "type": "event",
    "name": "TokenCreated",
    "inputs": [
      { "name": "bondingContract", "type": "address", "indexed": true },
      { "name": "tokenAddress", "type": "address", "indexed": true },
      { "name": "name", "type": "string", "indexed": false },
      { "name": "symbol", "type": "string", "indexed": false },
      { "name": "creator", "type": "address", "indexed": false }
    ]
  },
  {
    "type": "function",
    "name": "getBondingContract",
    "inputs": [{ "name": "tokenAddress", "type": "address" }],
    "outputs": [{ "name": "bondingAddress", "type": "address" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "createBonding",
    "inputs": [
      { "name": "name", "type": "string" },
      { "name": "symbol", "type": "string" },
      { "name": "salt", "type": "bytes32" }
    ],
    "outputs": [{ "name": "bondingAddress", "type": "address" }],
    "stateMutability": "payable"
  },
  {
    "type": "function",
    "name": "getInitCodeHash",
    "inputs": [
      { "name": "name", "type": "string" },
      { "name": "symbol", "type": "string" }
    ],
    "outputs": [{ "name": "initCodeHash", "type": "bytes32" }],
    "stateMutability": "pure"
  }
]

Bonding Curve ABI

BondingCurve.json
[
  {
    "type": "function",
    "name": "token",
    "inputs": [],
    "outputs": [{ "name": "tokenAddress", "type": "address" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "buyTokens",
    "inputs": [{ "name": "minTokens", "type": "uint256" }],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "type": "function",
    "name": "sellTokens",
    "inputs": [
      { "name": "tokenAmount", "type": "uint256" },
      { "name": "minEthOut", "type": "uint256" }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "function",
    "name": "calculateTokenAmount",
    "inputs": [{ "name": "usdcAmount", "type": "uint256" }],
    "outputs": [{ "name": "tokens", "type": "uint256" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "getCurrentPrice",
    "inputs": [],
    "outputs": [{ "name": "price", "type": "uint256" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "BONDING_TARGET",
    "inputs": [],
    "outputs": [{ "name": "target", "type": "uint256" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "VIRTUAL_ETH",
    "inputs": [],
    "outputs": [{ "name": "virtualReserve", "type": "uint256" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "totalETHRaised",
    "inputs": [],
    "outputs": [{ "name": "raised", "type": "uint256" }],
    "stateMutability": "view"
  },
  {
    "type": "function",
    "name": "bondingComplete",
    "inputs": [],
    "outputs": [{ "name": "complete", "type": "bool" }],
    "stateMutability": "view"
  }
]

DEX Integration

The Archemist DEX is a standard Uniswap V2 Fork. Once a token graduates from the bonding curve, liquidity is automatically seeded into this DEX.

100% Compatible

The Router and Factory contracts possess identical function signatures to Uniswap V2. You can use standard Uniswap V2 SDKs and libraries to interact with the Archemist DEX.

Sniping Integration

To programmatically buy at launch, interact with the specific bonding curve contract. First, query the Factory to get the address. Graduation happens when the real reserve reaches 5,000 USDC. After graduation, use the DEX router instead of the bonding curve.

snipe_bot.js
// ---------------------------------------------
// Archemist Sniper Example (Node.js)
// ---------------------------------------------
const { ethers } = require("ethers");

// Configuration (Arc Network)
const RPC_URL = "https://rpc.testnet.arc.network";
const FACTORY_ADDRESS = "0xFd52Cf5A6B4B167aBf83D6781B5B4dfccb52Fd4E";

// 1. Get Bonding Contract Logic
const FACTORY_ABI = [
  "function getBondingContract(address token) view returns (address)"
];
const BOND_ABI = [
  "function buyTokens(uint256 minTokens) external payable",
  "function calculateTokenAmount(uint256 usdcAmount) view returns (uint256)"
];

// Arc uses native USDC as the value asset. In ethers examples this is still
// passed through the transaction value field.
async function snipe(tokenAddress, usdcAmount) {
  const provider = new ethers.JsonRpcProvider(RPC_URL);
  // Never expose PRIVATE_KEY in client-side code, logs, or public repositories.
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
  const factory = new ethers.Contract(FACTORY_ADDRESS, FACTORY_ABI, wallet);

  console.log(`Looking for bonding contract: ${tokenAddress}`);
  
  // Each token has a unique bonding curve contract
  const bondingAddr = await factory.getBondingContract(tokenAddress);
  
  if (bondingAddr === ethers.ZeroAddress) {
     console.log("Curve not initialized yet.");
     return;
  }
  
  // 2. Buy from the Curve
  const bond = new ethers.Contract(bondingAddr, BOND_ABI, wallet);
  const tx = await bond.buyTokens(0, { // 0 = max slippage
    value: ethers.parseEther(usdcAmount)
  });
  
  console.log(`Sniping... Tx: ${tx.hash}`);
  await tx.wait();
  console.log("Assets secured.");
}