uniderp-mcp
Version:
> A plug-and-play MCP tool server to **send ETH**, **transfer ERC-20 tokens**, **deploy tokens**, and **interact with smart contracts** on the **UNICHAIN** — built for **Claude Desktop**, **AI agents**, and **developers.**
135 lines (134 loc) • 5.08 kB
JavaScript
import { decodeEventLog, getContract, keccak256, parseEther, parseUnits, publicActions, toBytes, zeroAddress, } from "viem";
import { AddressConfig } from "../addressConfig.js";
import { unichain, unichainSepolia } from "viem/chains";
import { supportedChain } from "../chains/index.js";
import { uniMemeLauncherAbi } from "../lib/abi/uniMemeLauncherAbi.js";
import { publicClient, walletClient } from "../config.js";
import { Price, Token } from "@uniswap/sdk-core";
import { priceToClosestTick } from "@uniswap/v3-sdk";
import Decimal from "decimal.js";
import { genImg } from "./genImg.js";
import { uniderpLogin } from "../lib/uniderp.js";
export const UNIDERP_DOMAINS = {
[unichain.id]: "api.uniderp.fun",
[unichainSepolia.id]: "staging-capi.uniderp.fun",
};
const TICK_SPACING = 60;
const INITIAL_PRICE = 10130819768;
const DEPLOY_FEE = 0.00069;
const NATIVE_DECIMAL = 18;
const token = ({ sortOrder, decimals = 18, chainId = 1, }) => {
if (sortOrder > 9 || sortOrder % 1 !== 0)
throw new Error("invalid sort order");
return new Token(chainId, `0x${new Array(40).fill(`${sortOrder}`).join("")}`, decimals, `T${sortOrder}`, `token${sortOrder}`);
};
const getTokenAddressFromLogs = (logs) => {
let tokenAddress = "";
for (const log of logs) {
try {
const decoded = decodeEventLog({
abi: uniMemeLauncherAbi,
data: log.data,
topics: log.topics,
});
if (decoded.eventName === "TokenCreated") {
tokenAddress = decoded.args.tokenAddress;
}
}
catch {
console.log();
}
}
return tokenAddress;
};
export async function uniderpCreateMeme({ account, initialEthBuyAmount, tokenName, tokenSymbol, description, teleUrl, twitUrl, webUrl, discordUrl, youtubeUrl, imageUrl, }) {
const domain = UNIDERP_DOMAINS[supportedChain.id];
const uniderpToken = await uniderpLogin(account);
// Submit Onchain
const value = parseUnits(new Decimal(initialEthBuyAmount).add(DEPLOY_FEE).toFixed(NATIVE_DECIMAL), NATIVE_DECIMAL);
const client = walletClient(account).extend(publicActions);
const contract = getContract({
address: AddressConfig.UniMemeLauncherContract,
abi: uniMemeLauncherAbi,
client,
});
const token0 = token({ sortOrder: 0 });
const token1 = token({ sortOrder: 1 });
const tickIfToken0IsNewToken = Math.floor(priceToClosestTick(new Price(token0, token1, INITIAL_PRICE, 1)) /
TICK_SPACING) * TICK_SPACING;
const txHash = await contract.write.deployTokenWithEth([
{
initialBuyConfig: {
buyPairedAmount: parseEther(initialEthBuyAmount),
minTokenAmount: 0,
},
tokenConfig: {
name: tokenName,
symbol: tokenSymbol,
salt: keccak256(toBytes(tokenSymbol + Math.floor(1000 * Math.random()))),
},
poolConfig: {
pairedToken: zeroAddress,
tickIfToken0IsNewToken,
},
referrer: zeroAddress,
},
], {
value,
});
const receipt = await publicClient.waitForTransactionReceipt({
hash: txHash,
});
if (receipt.status !== "success") {
throw new Error("Deploy token failed");
}
const tokenAddress = getTokenAddressFromLogs(receipt.logs);
// Create Project
const formData = new FormData();
formData.append("tokenName", tokenName);
formData.append("tokenSymbol", tokenSymbol);
formData.append("description", description);
formData.append("tokenAddress", tokenAddress);
if (teleUrl) {
formData.append("teleUrl", teleUrl);
}
if (twitUrl) {
formData.append("twitUrl", twitUrl);
}
if (webUrl) {
formData.append("webUrl", webUrl);
}
if (discordUrl) {
formData.append("discordUrl", discordUrl);
}
if (youtubeUrl) {
formData.append("youtubeUrl", youtubeUrl);
}
if (imageUrl) {
formData.append("imageUrl", imageUrl);
}
else {
// Image
const prompt = `Logo for this token: name = ${tokenName}, description = ${description}`;
const base64String = await genImg(prompt);
const byteCharacters = atob(base64String);
const byteNumbers = new Array(byteCharacters.length)
.fill(0)
.map((_, i) => byteCharacters.charCodeAt(i));
const byteArray = new Uint8Array(byteNumbers);
const file = new File([byteArray], tokenName + ".jpeg", {
type: "image/jpeg",
});
formData.append("file", file);
}
const createProjectRes = await fetch(`https://${domain}/v1/projects`, {
method: "POST",
headers: {
Authorization: `Bearer ${uniderpToken}`,
},
body: formData,
}).then((res) => res.json());
if (createProjectRes.data && createProjectRes.data.tokenAddress) {
return createProjectRes.data.tokenAddress;
}
}