@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
444 lines (438 loc) • 20.2 kB
JavaScript
"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;