UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

520 lines (513 loc) 22.6 kB
"use strict"; /** * 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;