@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
520 lines (513 loc) • 22.6 kB
JavaScript
;
/**
* Flaunch Action Provider
*
* This file contains the implementation of the FlaunchActionProvider,
* which provides actions for flaunch operations.
*
* @module flaunch
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.flaunchActionProvider = exports.FlaunchActionProvider = void 0;
const zod_1 = require("zod");
const actionProvider_1 = require("../actionProvider");
const network_1 = require("../../network");
const actionDecorator_1 = require("../actionDecorator");
const wallet_providers_1 = require("../../wallet-providers");
const viem_1 = require("viem");
const chains_1 = require("viem/chains");
const schemas_1 = require("./schemas");
const utils_1 = require("./utils");
const constants_1 = require("./constants");
const SUPPORTED_NETWORKS = ["base-mainnet", "base-sepolia"];
/**
* FlaunchActionProvider provides actions for flaunch operations.
*
* @description
* This provider is designed to work with EvmWalletProvider for blockchain interactions.
* It supports all evm networks.
*/
class FlaunchActionProvider extends actionProvider_1.ActionProvider {
/**
* Constructor for the FlaunchActionProvider.
*
* @param config - The configuration options for the FlaunchActionProvider.
*/
constructor(config = {}) {
super("flaunch", []);
const pinataJwt = config.pinataJwt || process.env.PINATA_JWT;
if (!pinataJwt) {
throw new Error("PINATA_JWT is not configured.");
}
this.pinataJwt = pinataJwt;
}
/**
* Example action implementation.
* Replace or modify this with your actual action.
*
* @description
* This is a template action that demonstrates the basic structure.
* Replace it with your actual implementation.
*
* @param walletProvider - The wallet provider instance for blockchain interactions
* @param args - Arguments defined by FlaunchSchema
* @returns A promise that resolves to a string describing the action result
*/
async flaunch(walletProvider, args) {
try {
const network = walletProvider.getNetwork();
const networkId = network.networkId;
const chainId = network.chainId;
if (!chainId || !networkId) {
throw new Error("Chain ID is not set.");
}
// upload image & token uri to ipfs
const tokenUri = await (0, utils_1.generateTokenUri)(args.name, {
pinataConfig: { jwt: this.pinataJwt },
metadata: {
imageUrl: args.imageUrl,
description: args.description,
websiteUrl: args.websiteUrl,
discordUrl: args.discordUrl,
twitterUrl: args.twitterUrl,
telegramUrl: args.telegramUrl,
},
});
const data = (0, viem_1.encodeFunctionData)({
abi: constants_1.FAST_FLAUNCH_ZAP_ABI,
functionName: "flaunch",
args: [
{
name: args.name,
symbol: args.symbol,
tokenUri,
creator: walletProvider.getAddress(),
},
],
});
const hash = await walletProvider.sendTransaction({
to: constants_1.FastFlaunchZapAddress[chainId],
data,
});
const receipt = await walletProvider.waitForTransactionReceipt(hash);
const filteredPoolCreatedEvent = receipt.logs
.map(log => {
try {
if (log.address.toLowerCase() !== constants_1.FlaunchPositionManagerAddress[chainId].toLowerCase()) {
return null;
}
const event = (0, viem_1.decodeEventLog)({
abi: constants_1.POSITION_MANAGER_ABI,
data: log.data,
topics: log.topics,
});
return event.eventName === "PoolCreated" ? event.args : null;
}
catch {
return null;
}
})
.filter((event) => event !== null)[0];
const memecoinAddress = filteredPoolCreatedEvent._memecoin;
const chainSlug = Number(chainId) === chains_1.base.id ? "base" : "base-sepolia";
return `Flaunched\n ${JSON.stringify({
coinSymbol: `$${args.symbol}`,
coinName: args.name,
coinAddress: memecoinAddress,
flaunchCoinUrl: `https://flaunch.gg/${chainSlug}/coin/${memecoinAddress}`,
transactionHash: hash,
transactionUrl: `${network_1.NETWORK_ID_TO_VIEM_CHAIN[networkId].blockExplorers?.default.url}/tx/${hash}`,
})}`;
}
catch (error) {
return `Error launching coin: ${error}`;
}
}
/**
* Buys a flaunch coin using ETH input.
*
* @param walletProvider - The wallet provider instance for blockchain interactions
* @param args - Arguments defined by BuyCoinSchema
* @returns A promise that resolves to a string describing the transaction result
*/
async buyCoinWithETHInput(walletProvider, args) {
return this._buyFlaunchCoin(walletProvider, args.coinAddress, "EXACT_IN", { amountIn: args.amountIn }, args.slippagePercent);
}
/**
* Buys a flaunch coin using Coin input.
*
* @param walletProvider - The wallet provider instance for blockchain interactions
* @param args - Arguments defined by BuyCoinSchema
* @returns A promise that resolves to a string describing the transaction result
*/
async buyCoinWithCoinInput(walletProvider, args) {
return this._buyFlaunchCoin(walletProvider, args.coinAddress, "EXACT_OUT", { amountOut: args.amountOut }, args.slippagePercent);
}
/**
* Sells a flaunch coin into ETH.
*
* @param walletProvider - The wallet provider instance for blockchain interactions
* @param args - Arguments defined by SellCoinSchema
* @returns A promise that resolves to a string describing the transaction result
*/
async sellCoin(walletProvider, args) {
const network = walletProvider.getNetwork();
const chainId = network.chainId;
const networkId = network.networkId;
if (!chainId || !networkId) {
throw new Error("Chain ID is not set.");
}
try {
const amountIn = (0, viem_1.parseEther)(args.amountIn);
// fetch permit2 allowance
const [allowance, nonce] = await walletProvider.readContract({
address: constants_1.Permit2Address[chainId],
abi: constants_1.PERMIT2_ABI,
functionName: "allowance",
args: [
walletProvider.getAddress(),
args.coinAddress,
constants_1.UniversalRouterAddress[chainId],
],
});
let signature;
let permitSingle;
// approve
if (allowance < amountIn) {
// 10 years in seconds
const defaultDeadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 365 * 10);
const domain = {
name: "Permit2",
chainId: Number(chainId),
verifyingContract: constants_1.Permit2Address[chainId],
};
const message = {
details: {
token: args.coinAddress,
amount: viem_1.maxUint160,
expiration: Number(defaultDeadline),
nonce,
},
spender: constants_1.UniversalRouterAddress[chainId],
sigDeadline: defaultDeadline,
};
const typedData = {
primaryType: "PermitSingle",
domain,
types: constants_1.PERMIT_TYPES,
message,
};
signature = await walletProvider.signTypedData(typedData);
permitSingle = message;
}
const viemPublicClient = (0, viem_1.createPublicClient)({
chain: network_1.NETWORK_ID_TO_VIEM_CHAIN[networkId],
transport: (0, viem_1.http)(),
});
const quoteResult = await viemPublicClient.simulateContract({
address: constants_1.QuoterAddress[chainId],
abi: constants_1.QUOTER_ABI,
functionName: "quoteExactInput",
args: [
{
exactAmount: amountIn,
exactCurrency: args.coinAddress,
path: [
{
fee: 0,
tickSpacing: 60,
hooks: constants_1.FlaunchPositionManagerAddress[chainId],
hookData: "0x",
intermediateCurrency: constants_1.FLETHAddress[chainId],
},
{
fee: 0,
tickSpacing: 60,
hookData: "0x",
hooks: constants_1.FLETHHooksAddress[chainId],
intermediateCurrency: viem_1.zeroAddress,
},
],
},
],
});
const ethOutMin = (0, utils_1.getAmountWithSlippage)(quoteResult.result[0], // amountOut
(args.slippagePercent / 100).toFixed(18).toString(), "EXACT_IN");
const { commands, inputs } = (0, utils_1.memecoinToEthWithPermit2)({
chainId: Number(chainId),
memecoin: args.coinAddress,
amountIn,
ethOutMin,
permitSingle,
signature,
referrer: viem_1.zeroAddress,
});
const data = (0, viem_1.encodeFunctionData)({
abi: constants_1.UNIVERSAL_ROUTER_ABI,
functionName: "execute",
args: [commands, inputs],
});
const hash = await walletProvider.sendTransaction({
to: constants_1.UniversalRouterAddress[chainId],
data,
});
const receipt = await walletProvider.waitForTransactionReceipt(hash);
const swapAmounts = (0, utils_1.getSwapAmountsFromReceipt)({
receipt,
coinAddress: args.coinAddress,
chainId: Number(chainId),
});
const coinSymbol = await walletProvider.readContract({
address: args.coinAddress,
abi: constants_1.ERC20_ABI,
functionName: "symbol",
});
return `Sold ${(0, viem_1.formatEther)(swapAmounts.coinsSold)} $${coinSymbol} for ${(0, viem_1.formatEther)(swapAmounts.ethBought)} ETH\n
Tx hash: [${hash}](${network_1.NETWORK_ID_TO_VIEM_CHAIN[networkId].blockExplorers?.default.url}/tx/${hash})`;
}
catch (error) {
return `Error selling coin: ${error}`;
}
}
/**
* Checks if this provider supports the given network.
*
* @param network - The network to check support for
* @returns True if the network is supported
*/
supportsNetwork(network) {
// all protocol networks
return network.protocolFamily === "evm" && SUPPORTED_NETWORKS.includes(network.networkId);
}
/**
* Handles the process of buying a flaunch coin with ETH.
*
* @param walletProvider - The wallet provider instance
* @param coinAddress - The address of the flaunch coin
* @param swapType - The type of swap (EXACT_IN or EXACT_OUT)
* @param swapParams - Parameters specific to the swap type
* @param swapParams.amountIn - The amount of ETH to spend (for EXACT_IN)
* @param swapParams.amountOut - The amount of coins to buy (for EXACT_OUT)
* @param slippagePercent - The slippage percentage
* @returns A promise that resolves to a string describing the transaction result
*/
async _buyFlaunchCoin(walletProvider, coinAddress, swapType, swapParams, slippagePercent) {
const network = walletProvider.getNetwork();
const chainId = network.chainId;
const networkId = network.networkId;
if (!chainId || !networkId) {
throw new Error("Chain ID is not set.");
}
try {
const viemPublicClient = (0, viem_1.createPublicClient)({
chain: network_1.NETWORK_ID_TO_VIEM_CHAIN[networkId],
transport: (0, viem_1.http)(),
});
let amountIn;
let amountOutMin;
let amountOut;
let amountInMax;
if (swapType === "EXACT_IN") {
amountIn = (0, viem_1.parseEther)(swapParams.amountIn);
const quoteResult = await viemPublicClient.simulateContract({
address: constants_1.QuoterAddress[chainId],
abi: constants_1.QUOTER_ABI,
functionName: "quoteExactInput",
args: [
{
exactAmount: amountIn,
exactCurrency: viem_1.zeroAddress, // ETH
path: [
{
fee: 0,
tickSpacing: 60,
hookData: "0x",
hooks: constants_1.FLETHHooksAddress[chainId],
intermediateCurrency: constants_1.FLETHAddress[chainId],
},
{
fee: 0,
tickSpacing: 60,
hooks: constants_1.FlaunchPositionManagerAddress[chainId],
hookData: "0x",
intermediateCurrency: coinAddress,
},
],
},
],
});
amountOutMin = (0, utils_1.getAmountWithSlippage)(quoteResult.result[0], // amountOut
(slippagePercent / 100).toFixed(18).toString(), swapType);
}
else {
// EXACT_OUT
amountOut = (0, viem_1.parseEther)(swapParams.amountOut);
const quoteResult = await viemPublicClient.simulateContract({
address: constants_1.QuoterAddress[chainId],
abi: constants_1.QUOTER_ABI,
functionName: "quoteExactOutput",
args: [
{
path: [
{
intermediateCurrency: viem_1.zeroAddress,
fee: 0,
tickSpacing: 60,
hookData: "0x",
hooks: constants_1.FLETHHooksAddress[chainId],
},
{
intermediateCurrency: constants_1.FLETHAddress[chainId],
fee: 0,
tickSpacing: 60,
hooks: constants_1.FlaunchPositionManagerAddress[chainId],
hookData: "0x",
},
],
exactCurrency: coinAddress,
exactAmount: amountOut,
},
],
});
amountInMax = (0, utils_1.getAmountWithSlippage)(quoteResult.result[0], // amountIn
(slippagePercent / 100).toFixed(18).toString(), swapType);
}
const { commands, inputs } = (0, utils_1.ethToMemecoin)({
sender: walletProvider.getAddress(),
memecoin: coinAddress,
chainId: Number(chainId),
referrer: viem_1.zeroAddress,
swapType,
amountIn,
amountOutMin,
amountOut,
amountInMax,
});
const data = (0, viem_1.encodeFunctionData)({
abi: constants_1.UNIVERSAL_ROUTER_ABI,
functionName: "execute",
args: [commands, inputs],
});
const hash = await walletProvider.sendTransaction({
to: constants_1.UniversalRouterAddress[chainId],
data,
value: swapType === "EXACT_IN" ? amountIn : amountInMax,
});
const receipt = await walletProvider.waitForTransactionReceipt(hash);
const swapAmounts = (0, utils_1.getSwapAmountsFromReceipt)({
receipt,
coinAddress: coinAddress,
chainId: Number(chainId),
});
const coinSymbol = await walletProvider.readContract({
address: coinAddress,
abi: constants_1.ERC20_ABI,
functionName: "symbol",
});
return `Bought ${(0, viem_1.formatEther)(swapAmounts.coinsBought)} $${coinSymbol} for ${(0, viem_1.formatEther)(swapAmounts.ethSold)} ETH\n
Tx hash: [${hash}](${network_1.NETWORK_ID_TO_VIEM_CHAIN[networkId].blockExplorers?.default.url}/tx/${hash})`;
}
catch (error) {
return `Error buying coin: ${error}`;
}
}
}
exports.FlaunchActionProvider = FlaunchActionProvider;
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "flaunch",
description: `
This tool allows launching a new memecoin using the flaunch protocol.
It takes:
- name: The name of the token
- symbol: The symbol of the token
- imageUrl: URL to the token image
- description: Description of the token
- websiteUrl: (optional) URL to the token website
- discordUrl: (optional) URL to the token Discord
- twitterUrl: (optional) URL to the token Twitter
- telegramUrl: (optional) URL to the token Telegram
Note:
- If the optional fields are not provided, don't include them in the call.
`,
schema: schemas_1.FlaunchSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], FlaunchActionProvider.prototype, "flaunch", null);
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "buyCoinWithETHInput",
description: `
This tool allows buying a flaunch coin using ETH, when the user has specified the ETH amount to spend.
It takes:
- coinAddress: The address of the flaunch coin to buy
- amountIn: The quantity of ETH to spend on the flaunch coin, in whole units
Examples:
- 0.001 ETH
- 0.01 ETH
- 1 ETH
- slippagePercent: (optional) The slippage percentage. Default to 5%
`,
schema: schemas_1.BuyCoinWithETHInputSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], FlaunchActionProvider.prototype, "buyCoinWithETHInput", null);
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "buyCoinWithCoinInput",
description: `
This tool allows buying a flaunch coin using ETH, when the user has specified the Coin amount to buy.
It takes:
- coinAddress: The address of the flaunch coin to buy
- amountOut: The quantity of the flaunch coin to buy, in whole units
Examples:
- 1000 coins
- 1_000_000 coins
- slippagePercent: (optional) The slippage percentage. Default to 5%
`,
schema: schemas_1.BuyCoinWithCoinInputSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], FlaunchActionProvider.prototype, "buyCoinWithCoinInput", null);
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "sellCoin",
description: `
This tool allows selling a flaunch coin into ETH, when the user has specified the Coin amount to sell.
It takes:
- coinAddress: The address of the flaunch coin to sell
- amountIn: The quantity of the flaunch coin to sell, in whole units
Examples:
- 1000 coins
- 1_000_000 coins
- slippagePercent: (optional) The slippage percentage. Default to 5%
`,
schema: schemas_1.SellCoinSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], FlaunchActionProvider.prototype, "sellCoin", null);
/**
* Factory function to create a new FlaunchActionProvider instance.
*
* @param config - Configuration options for the FlaunchActionProvider
* @returns A new FlaunchActionProvider instance
*/
const flaunchActionProvider = (config) => new FlaunchActionProvider(config);
exports.flaunchActionProvider = flaunchActionProvider;