UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

444 lines (438 loc) 20.2 kB
"use strict"; 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 metadata_utils_1 = require("./metadata_utils"); const client_utils_1 = require("./client_utils"); const swap_utils_1 = require("./swap_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. */ class FlaunchActionProvider extends actionProvider_1.ActionProvider { /** * Constructor for the FlaunchActionProvider. * */ constructor() { super("flaunch", []); } /** * Launches a new memecoin using the flaunch protocol. * * @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 transaction 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."); } // Validate that premineAmount does not exceed fairLaunchPercent if (args.preminePercent > args.fairLaunchPercent) { throw new Error(`premineAmount (${args.preminePercent}%) cannot exceed fairLaunchPercent (${args.fairLaunchPercent}%)`); } // Prepare launch parameters const initialMCapInUSDCWei = (0, viem_1.parseUnits)(args.initialMarketCapUSD.toString(), 6); const initialPriceParams = (0, viem_1.encodeAbiParameters)([{ type: "uint256" }], [initialMCapInUSDCWei]); const fairLaunchInBps = BigInt(args.fairLaunchPercent * 100); // Convert premine percentage to token amount and calculate ETH required const premineAmount = (constants_1.TOTAL_SUPPLY * BigInt(Math.floor(args.preminePercent * 100))) / 10000n; const ethRequired = args.preminePercent > 0 ? await (0, client_utils_1.ethRequiredToFlaunch)(walletProvider, { premineAmount, initialPriceParams, slippagePercent: 5, }) : 0n; // Check ETH balance if (ethRequired > 0n) { const ethBalance = await walletProvider.getBalance(); if (ethBalance < ethRequired) { throw new Error(`Insufficient ETH balance. Required: ${(0, viem_1.formatEther)(ethRequired)} ETH, Available: ${(0, viem_1.formatEther)(ethBalance)} ETH`); } } // Upload image & token uri to ipfs const tokenUri = await (0, metadata_utils_1.generateTokenUri)(args.name, args.symbol, { metadata: { image: args.image, description: args.description, websiteUrl: args.websiteUrl, discordUrl: args.discordUrl, twitterUrl: args.twitterUrl, telegramUrl: args.telegramUrl, }, }); // Fee split configuration const creatorFeeAllocationInBps = args.creatorFeeAllocationPercent * 100; let creatorShare = 10000000n; let recipientShares = []; if (args.creatorSplitPercent !== undefined && args.splitReceivers !== undefined) { const VALID_SHARE_TOTAL = 10000000n; // 5 decimals as BigInt, 100 * 10^5 creatorShare = (BigInt(args.creatorSplitPercent) * VALID_SHARE_TOTAL) / 100n; recipientShares = args.splitReceivers.map(receiver => { return { recipient: receiver.address, share: (BigInt(receiver.percent) * VALID_SHARE_TOTAL) / 100n, }; }); const totalRecipientShares = recipientShares.reduce((acc, curr) => acc + curr.share, 0n); const totalRecipientPercent = (totalRecipientShares * 100n) / VALID_SHARE_TOTAL; // Check that recipient shares add up to 100% if (totalRecipientPercent !== 100n) { throw new Error(`Recipient shares must add up to exactly 100%, but they add up to ${totalRecipientPercent}%`); } const remainderShares = VALID_SHARE_TOTAL - totalRecipientShares; creatorShare += remainderShares; } const initializeData = (0, viem_1.encodeAbiParameters)([ { type: "tuple", name: "params", components: [ { type: "uint256", name: "creatorShare" }, { type: "tuple[]", name: "recipientShares", components: [ { type: "address", name: "recipient" }, { type: "uint256", name: "share" }, ], }, ], }, ], [ { creatorShare, recipientShares, }, ]); const flaunchParams = { name: args.name, symbol: args.symbol, tokenUri, initialTokenFairLaunch: (constants_1.TOTAL_SUPPLY * fairLaunchInBps) / 10000n, fairLaunchDuration: BigInt(args.fairLaunchDuration * 60), premineAmount, creator: walletProvider.getAddress(), creatorFeeAllocation: creatorFeeAllocationInBps, flaunchAt: 0n, initialPriceParams, feeCalculatorParams: "0x", }; const treasuryManagerParams = { manager: constants_1.AddressFeeSplitManagerAddress[chainId], initializeData, depositData: "0x", }; const whitelistParams = { merkleRoot: viem_1.zeroHash, merkleIPFSHash: "", maxTokens: 0n, }; const airdropParams = { airdropIndex: 0n, airdropAmount: 0n, airdropEndTime: 0n, merkleRoot: viem_1.zeroHash, merkleIPFSHash: "", }; const data = (0, viem_1.encodeFunctionData)({ abi: constants_1.FLAUNCH_ZAP_ABI, functionName: "flaunch", args: [flaunchParams, whitelistParams, airdropParams, treasuryManagerParams], }); const hash = await walletProvider.sendTransaction({ to: constants_1.FlaunchZapAddress[chainId], data, value: ethRequired, }); const receipt = await walletProvider.waitForTransactionReceipt(hash); const memecoinAddress = (0, client_utils_1.getMemecoinAddressFromReceipt)(receipt, chainId); 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 (0, swap_utils_1.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 (0, swap_utils_1.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 quoteResult = await walletProvider.getPublicClient().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.FlaunchPositionManagerV1_1Address[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, swap_utils_1.getAmountWithSlippage)(quoteResult.result[0], // amountOut (args.slippagePercent / 100).toFixed(18).toString(), "EXACT_IN"); const { commands, inputs } = (0, swap_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, swap_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); } } 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 - image: Local image file path or URL to the token image - description: Description of the token - fairLaunchPercent: The percentage of tokens for fair launch (defaults to 60%) - fairLaunchDuration: The duration of the fair launch in minutes (defaults to 30 minutes) - initialMarketCapUSD: The initial market cap in USD (defaults to 10000 USD) - preminePercent: The percentage of total supply to premine (defaults to 0%, max is equal to fairLaunchPercent) - creatorFeeAllocationPercent: The percentage of fees allocated to creator and optional receivers (defaults to 80%) - creatorSplitPercent: The percentage of fees allocated to the creator (defaults to 100%), remainder goes to fee split recipients - splitReceivers: Array of fee split recipients with address and percentage (optional) - websiteUrl: URL to the token website (optional) - discordUrl: URL to the token Discord (optional) - twitterUrl: URL to the token Twitter (optional) - telegramUrl: URL to the token Telegram (optional) Note: - splitReceivers must add up to exactly 100% if provided. `, 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. * * @returns A new FlaunchActionProvider instance */ const flaunchActionProvider = () => new FlaunchActionProvider(); exports.flaunchActionProvider = flaunchActionProvider;