@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
227 lines (226 loc) • 8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPriceInfo = createPriceInfo;
exports.getPoolInfo = getPoolInfo;
exports.exactInputSingle = exactInputSingle;
exports.getUniswapQuote = getUniswapQuote;
exports.getHasGraduated = getHasGraduated;
exports.getPoolAddress = getPoolAddress;
const viem_1 = require("viem");
const constants_1 = require("../constants");
const constants_2 = require("./constants");
/**
* Creates a PriceInfo object from wei amount and ETH price.
*
* @param weiAmount - Amount in wei
* @param ethPriceInUsd - Current ETH price in USD
* @returns A PriceInfo object containing the amount in ETH and USD
*/
function createPriceInfo(weiAmount, ethPriceInUsd) {
const amountInEth = (0, viem_1.formatEther)(BigInt(weiAmount));
const usd = Number(amountInEth) * ethPriceInUsd;
return {
eth: weiAmount,
usd,
};
}
/**
* Gets pool info for a given uniswap v3 pool address.
*
* @param wallet - The wallet provider to use for contract calls
* @param poolAddress - Uniswap v3 pool address
* @returns A PoolInfo object containing pool details
*/
async function getPoolInfo(wallet, poolAddress) {
try {
const results = await Promise.all([
wallet.readContract({
address: poolAddress,
functionName: "token0",
args: [],
abi: constants_2.UNISWAP_V3_ABI,
}),
wallet.readContract({
address: poolAddress,
functionName: "token1",
args: [],
abi: constants_2.UNISWAP_V3_ABI,
}),
wallet.readContract({
address: poolAddress,
functionName: "fee",
args: [],
abi: constants_2.UNISWAP_V3_ABI,
}),
wallet.readContract({
address: poolAddress,
functionName: "liquidity",
args: [],
abi: constants_2.UNISWAP_V3_ABI,
}),
wallet.readContract({
address: poolAddress,
functionName: "slot0",
args: [],
abi: constants_2.UNISWAP_V3_ABI,
}),
]);
const [token0Result, token1Result, fee, liquidity, slot0] = results;
const [balance0, balance1] = await Promise.all([
wallet.readContract({
address: token0Result,
functionName: "balanceOf",
args: [poolAddress],
abi: constants_1.WOW_ABI,
}),
wallet.readContract({
address: token1Result,
functionName: "balanceOf",
args: [poolAddress],
abi: constants_1.WOW_ABI,
}),
]);
return {
token0: token0Result,
balance0: Number(balance0),
token1: token1Result,
balance1: Number(balance1),
fee: Number(fee),
liquidity: Number(liquidity),
sqrtPriceX96: Number(slot0[0]),
};
}
catch (error) {
throw new Error(`Failed to fetch pool information: ${error}`);
}
}
/**
* Gets exact input quote from Uniswap.
*
* @param wallet - The wallet provider to use for contract calls
* @param tokenIn - Token address to swap from
* @param tokenOut - Token address to swap to
* @param amountIn - Amount of tokens to swap (in Wei)
* @param fee - Fee for the swap
* @returns Amount of tokens to receive (in Wei)
*/
async function exactInputSingle(wallet, tokenIn, tokenOut, amountIn, fee) {
try {
const networkId = wallet.getNetwork().networkId;
const amount = await wallet.readContract({
address: constants_1.ADDRESSES[networkId].UniswapQuoter,
functionName: "quoteExactInputSingle",
args: [
{
tokenIn: (0, viem_1.getAddress)(tokenIn),
tokenOut: (0, viem_1.getAddress)(tokenOut),
fee,
amountIn,
sqrtPriceLimitX96: 0,
},
],
abi: constants_2.UNISWAP_QUOTER_ABI,
});
return Number(amount);
}
catch (error) {
console.error("Quoter error:", error);
return 0;
}
}
/**
* Gets Uniswap quote for buying or selling tokens.
*
* @param wallet - The wallet provider to use for contract calls
* @param tokenAddress - Token address
* @param amount - Amount of tokens (in Wei)
* @param quoteType - 'buy' or 'sell'
* @returns A Quote object containing quote details
*/
async function getUniswapQuote(wallet, tokenAddress, amount, quoteType) {
let pool = null;
let tokens = null;
let balances = null;
let quoteResult = null;
const utilization = 0;
const networkId = wallet.getNetwork().networkId;
const poolAddress = await getPoolAddress(wallet, tokenAddress);
const invalidPoolError = !poolAddress ? "Invalid pool address" : null;
try {
pool = await getPoolInfo(wallet, poolAddress);
const { token0, token1, balance0, balance1, fee } = pool;
tokens = [token0, token1];
balances = [balance0, balance1];
const isToken0Weth = token0.toLowerCase() === constants_1.ADDRESSES[networkId].WETH.toLowerCase();
const tokenIn = (quoteType === "buy" && isToken0Weth) || (quoteType === "sell" && !isToken0Weth)
? token0
: token1;
const [tokenOut, balanceOut] = tokenIn === token0 ? [token1, balance1] : [token0, balance0];
const isInsufficientLiquidity = quoteType === "buy" && amount > balanceOut;
if (!isInsufficientLiquidity) {
quoteResult = await exactInputSingle(wallet, tokenIn, tokenOut, String(amount), String(fee));
}
}
catch (error) {
console.error("Error fetching quote:", error);
}
const insufficientLiquidity = (quoteType === "sell" && pool && !quoteResult) || false;
let errorMsg = null;
if (!pool) {
errorMsg = "Failed fetching pool";
}
else if (insufficientLiquidity) {
errorMsg = "Insufficient liquidity";
}
else if (!quoteResult && utilization >= 0.9) {
errorMsg = "Price impact too high";
}
else if (!quoteResult) {
errorMsg = "Failed fetching quote";
}
const balanceResult = tokens && balances
? {
erc20z: String(balances[tokens[0].toLowerCase() === constants_1.ADDRESSES[networkId].WETH.toLowerCase() ? 1 : 0]),
weth: String(balances[tokens[0].toLowerCase() === constants_1.ADDRESSES[networkId].WETH.toLowerCase() ? 0 : 1]),
}
: null;
return {
amountIn: Number(amount),
amountOut: quoteResult || 0,
balance: balanceResult,
fee: pool?.fee ? pool.fee / 1000000 : null,
error: invalidPoolError || errorMsg,
};
}
/**
* Checks if a token has graduated from the Zora Wow protocol.
*
* @param wallet - The wallet provider to use for contract calls
* @param tokenAddress - Token address
* @returns True if the token has graduated, false otherwise
*/
async function getHasGraduated(wallet, tokenAddress) {
const marketType = await wallet.readContract({
address: tokenAddress,
functionName: "marketType",
args: [],
abi: constants_1.WOW_ABI,
});
return marketType === 1;
}
/**
* Fetches the uniswap v3 pool address for a given token.
*
* @param wallet - The wallet provider to use for contract calls
* @param tokenAddress - The address of the token contract
* @returns The uniswap v3 pool address associated with the token
*/
async function getPoolAddress(wallet, tokenAddress) {
const poolAddress = await wallet.readContract({
address: tokenAddress,
functionName: "poolAddress",
args: [],
abi: constants_1.WOW_ABI,
});
return poolAddress;
}