From 868ffb6d2c1e8786562946bc882ba8762f10cfba Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:56:37 -0400 Subject: [PATCH 01/10] network related utils --- .../src/action-providers/magiceden/utils.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 typescript/agentkit/src/action-providers/magiceden/utils.ts diff --git a/typescript/agentkit/src/action-providers/magiceden/utils.ts b/typescript/agentkit/src/action-providers/magiceden/utils.ts new file mode 100644 index 000000000..993e61b93 --- /dev/null +++ b/typescript/agentkit/src/action-providers/magiceden/utils.ts @@ -0,0 +1,88 @@ +import { Network, NETWORK_ID_TO_CHAIN_ID } from "../../network"; +import { Blockchain } from "@temp-magiceden/magiceden-sdk"; + +/** + * Maps a network ID to the corresponding MagicEden blockchain. + * + * @param networkId - The network ID to map. + * @returns The corresponding MagicEden blockchain. + */ +export const NETWORK_ID_TO_MAGICEDEN_CHAIN: Record = { + "solana-mainnet": Blockchain.SOLANA, + "ethereum-mainnet": Blockchain.ETHEREUM, + "polygon-mainnet": Blockchain.POLYGON, + "base-mainnet": Blockchain.BASE, + "arbitrum-mainnet": Blockchain.ARBITRUM, +}; + +/** + * Maps a chain ID to the corresponding MagicEden blockchain. + * + * @param chainId - The chain ID to map. + * @returns The corresponding MagicEden blockchain. + */ +export const CHAIN_ID_TO_MAGICEDEN_CHAIN: Record = { + "1": Blockchain.ETHEREUM, + "137": Blockchain.POLYGON, + "8453": Blockchain.BASE, + "42161": Blockchain.ARBITRUM, + "1329": Blockchain.SEI, + "33139": Blockchain.APECHAIN, + "80094": Blockchain.BERACHAIN, + "10143": Blockchain.MONAD_TESTNET, + "56": Blockchain.BSC, + "2741": Blockchain.ABSTRACT, +}; + +/** + * Checks if the given network is supported by MagicEden. + * + * @param network - The network to check. + * @returns True if the network is supported, false otherwise. + */ +export const isSupportedNetwork = (network: Network): boolean => { + // Check if the network is supported by MagicEden + // Currently only supports EVM and Solana + const isSupportedProtocol = + network.protocolFamily === "evm" || network.protocolFamily === "svm"; + + // Check if the network is supported by MagicEden + const isSupportedNetwork = + network.networkId !== undefined && NETWORK_ID_TO_MAGICEDEN_CHAIN[network.networkId] !== undefined; + + // Check if the chain ID is supported by MagicEden + const isSupportedChain = + network.chainId !== undefined && CHAIN_ID_TO_MAGICEDEN_CHAIN[network.chainId] !== undefined; + + return isSupportedProtocol && (isSupportedNetwork || isSupportedChain); +}; + +/** + * Gets the MagicEden blockchain from a network ID. + * + * @param networkId - The network ID to get the blockchain from. + * @returns The corresponding MagicEden blockchain. + */ +export const getMagicEdenChainFromNetworkId = (networkId: string): Blockchain => { + // First we check the known network IDs, and if they map to a chain, we return that chain + const chainFromNetworkId = NETWORK_ID_TO_MAGICEDEN_CHAIN[networkId]; + if (chainFromNetworkId) { + return chainFromNetworkId; + } + + // If the chain is not found in the network ID, try to get the chain from the chain ID + // Chain IDs always stay the same + // If Coinbase Agentkit supports a new chain, it will be supported by MagicEden through the chain ID + // (currently there are some chains MagicEden supports which Coinbase Agentkit does not) + const chainId = NETWORK_ID_TO_CHAIN_ID[networkId]; + if (!chainId) { + throw new Error(`Could not find chain ID for network ID: ${networkId}`); + } + + const chainFromChainId = CHAIN_ID_TO_MAGICEDEN_CHAIN[chainId]; + if (chainFromChainId) { + return chainFromChainId; + } + + throw new Error(`Unsupported network ID on MagicEden: ${networkId}`); +}; From 47a8e2d37b6ff0b10d67fabf57440c86b3a94989 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 01:31:02 -0400 Subject: [PATCH 02/10] add magic eden action provider --- typescript/agentkit/package.json | 1 + .../src/action-providers/magiceden/index.ts | 1 + .../magiceden/magicEdenActionProvider.ts | 105 +++++++++ .../src/action-providers/magiceden/utils.ts | 6 +- typescript/package-lock.json | 214 ++++++++++++++++++ 5 files changed, 324 insertions(+), 3 deletions(-) create mode 100644 typescript/agentkit/src/action-providers/magiceden/index.ts create mode 100644 typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json index 1d42e6477..0f52171f6 100644 --- a/typescript/agentkit/package.json +++ b/typescript/agentkit/package.json @@ -43,6 +43,7 @@ "@alloralabs/allora-sdk": "^0.1.0", "@coinbase/coinbase-sdk": "^0.20.0", "@jup-ag/api": "^6.0.39", + "@magiceden/magiceden-sdk": "1.0.0-beta.2", "@privy-io/server-auth": "^1.18.4", "@solana/spl-token": "^0.4.12", "@solana/web3.js": "^1.98.0", diff --git a/typescript/agentkit/src/action-providers/magiceden/index.ts b/typescript/agentkit/src/action-providers/magiceden/index.ts new file mode 100644 index 000000000..43b64051b --- /dev/null +++ b/typescript/agentkit/src/action-providers/magiceden/index.ts @@ -0,0 +1 @@ +export * from "./magicEdenActionProvider"; \ No newline at end of file diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts new file mode 100644 index 000000000..c7b8c6bfe --- /dev/null +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -0,0 +1,105 @@ +import { ActionProvider } from "../actionProvider"; +import { CreateAction } from "../actionDecorator"; +import { Network } from "../../network"; +import { Blockchain, MagicEdenClient, MagicEdenSDK, BuyParams, SolanaNftService } from "@magiceden/magiceden-sdk"; +import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; +import { Keypair } from "@solana/web3.js"; +import bs58 from "bs58"; +import { EvmNftService } from "@magiceden/magiceden-sdk/dist/services/nft/evm"; + +/** + * Configuration options for the MagicEdenActionProvider. + */ +export interface MagicEdenActionProviderConfig { + /** + * The Magic Eden API key. + */ + apiKey?: string; + + /** + * The network ID to use for the MagicEdenActionProvider. + */ + networkId?: string; + + /** + * The private key to use for the MagicEdenActionProvider. + */ + privateKey?: string; + + /** + * The RPC URL to use for the MagicEdenActionProvider (if Solana network). + */ + rpcUrl?: string; +} + +/** + * MagicEdenActionProvider provides functionality to interact with Magic Eden's marketplace. + */ +export class MagicEdenActionProvider extends ActionProvider { + private readonly solClient?: MagicEdenClient; + private readonly evmClient?: MagicEdenClient; + + /** + * Constructor for the MagicEdenActionProvider class. + */ + constructor(config: MagicEdenActionProviderConfig) { + super("magicEden", []); + + const apiKey = config.apiKey || process.env.MAGICEDEN_API_KEY; + if (!apiKey) { + throw new Error("MAGICEDEN_API_KEY is not configured."); + } + + const chain = getMagicEdenChainFromNetworkId(config.networkId || "base-mainnet"); + switch (chain) { + case Blockchain.SOLANA: + this.solClient = MagicEdenSDK.v1.createSolanaKeypairClient( + apiKey, + Keypair.fromSecretKey(bs58.decode(config.privateKey!)), + { + rpcUrl: config.rpcUrl, + }, + ); + break; + case Blockchain.BITCOIN: + throw new Error("Bitcoin is not a supported chain for MagicEdenActionProvider"); + // If not Bitcoin or Solana, default to viem EVM client + default: + this.evmClient = MagicEdenSDK.v1.createViemEvmClient( + apiKey, + config.privateKey! as `0x${string}`, + chain, + ); + break; + } + } + + /** + * Buys an NFT from the Magic Eden marketplace. + * + * @param walletProvider - The wallet provider for executing the buy. + * @param args - Input parameters conforming to the BuySchema. + * @returns A success message or error string. + */ + @CreateAction({ + name: "buy", + description: ` + + `, + schema: BuyParams, + }) + public async buy(args: BuyParams): Promise { + throw new Error("Not implemented"); + } + + /** + * Determines if the provider supports the given network. + * + * @param network - The network to check. + * @returns True if supported, false otherwise. + */ + public supportsNetwork = (network: Network): boolean => isSupportedNetwork(network); +} + +export const magicEdenActionProvider = (config: MagicEdenActionProviderConfig) => + new MagicEdenActionProvider(config); diff --git a/typescript/agentkit/src/action-providers/magiceden/utils.ts b/typescript/agentkit/src/action-providers/magiceden/utils.ts index 993e61b93..5329e02c5 100644 --- a/typescript/agentkit/src/action-providers/magiceden/utils.ts +++ b/typescript/agentkit/src/action-providers/magiceden/utils.ts @@ -70,10 +70,10 @@ export const getMagicEdenChainFromNetworkId = (networkId: string): Blockchain => return chainFromNetworkId; } - // If the chain is not found in the network ID, try to get the chain from the chain ID + // If the chain is not found from the network ID, try to get the chain from the chain ID // Chain IDs always stay the same - // If Coinbase Agentkit supports a new chain, it will be supported by MagicEden through the chain ID - // (currently there are some chains MagicEden supports which Coinbase Agentkit does not) + // If Coinbase Agentkit supports a new EVM chain, it will be supported by MagicEden through the chain ID + // (currently there are some EVM chains MagicEden supports which Coinbase Agentkit does not) const chainId = NETWORK_ID_TO_CHAIN_ID[networkId]; if (!chainId) { throw new Error(`Could not find chain ID for network ID: ${networkId}`); diff --git a/typescript/package-lock.json b/typescript/package-lock.json index b05d182fc..c3befd0ab 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -51,6 +51,7 @@ "@alloralabs/allora-sdk": "^0.1.0", "@coinbase/coinbase-sdk": "^0.20.0", "@jup-ag/api": "^6.0.39", + "@magiceden/magiceden-sdk": "1.0.0-beta.2", "@privy-io/server-auth": "^1.18.4", "@solana/spl-token": "^0.4.12", "@solana/web3.js": "^1.98.0", @@ -3102,6 +3103,127 @@ "@lit-labs/ssr-dom-shim": "^1.0.0" } }, + "node_modules/@magiceden/magiceden-sdk": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@magiceden/magiceden-sdk/-/magiceden-sdk-1.0.0-beta.2.tgz", + "integrity": "sha512-WahfCKK5sIGFFC03p5RxwEPcYXrZf3cY4LFYlfHU4sjf3ztkYMXescyD81AOuAhJJN/rUl1fxlw1ifeb/ff0iA==", + "dependencies": { + "@reservoir0x/reservoir-sdk": "2.5.4", + "@solana/web3.js": "^1.98.0", + "axios": "^1.6.7", + "bs58": "^6.0.0", + "ts-retry-promise": "^0.8.1", + "tweetnacl": "^1.0.3", + "viem": "^2.22.19", + "zod": "^3.24.2" + } + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/@reservoir0x/reservoir-sdk": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@reservoir0x/reservoir-sdk/-/reservoir-sdk-2.5.4.tgz", + "integrity": "sha512-QCDMFo8hfHkWopxeLk//beqTargCJYxPV/rKPfUbSPAuSWRt4b7eoniq+zD65zaWwTaUyoYNvuB6Wemd/G3mXg==", + "dependencies": { + "axios": "^1.6.7" + }, + "peerDependencies": { + "viem": "~2.22.8" + } + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==" + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "dependencies": { + "base-x": "^5.0.0" + } + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/viem": { + "version": "2.22.23", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.22.23.tgz", + "integrity": "sha512-MheOu+joowphTCfCgdQ9BGU/z1IeHa6/ZIYNVc6KTwDklj671YS87cGv5kRCSU0vAfzN+5wjWyIffM8000KGkQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "dependencies": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@magiceden/magiceden-sdk/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@manypkg/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", @@ -17944,6 +18066,14 @@ } } }, + "node_modules/ts-retry-promise": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/ts-retry-promise/-/ts-retry-promise-0.8.1.tgz", + "integrity": "sha512-+AHPUmAhr5bSRRK5CurE9kNH8gZlEHnCgusZ0zy2bjfatUBDX0h6vGQjiT0YrGwSDwRZmU+bapeX6mj55FOPvg==", + "engines": { + "node": ">=6" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -20119,6 +20249,7 @@ "@alloralabs/allora-sdk": "^0.1.0", "@coinbase/coinbase-sdk": "^0.20.0", "@jup-ag/api": "^6.0.39", + "@magiceden/magiceden-sdk": "1.0.0-beta.2", "@privy-io/server-auth": "^1.18.4", "@solana/spl-token": "^0.4.12", "@solana/web3.js": "^1.98.0", @@ -21298,6 +21429,84 @@ "@lit-labs/ssr-dom-shim": "^1.0.0" } }, + "@magiceden/magiceden-sdk": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@magiceden/magiceden-sdk/-/magiceden-sdk-1.0.0-beta.2.tgz", + "integrity": "sha512-WahfCKK5sIGFFC03p5RxwEPcYXrZf3cY4LFYlfHU4sjf3ztkYMXescyD81AOuAhJJN/rUl1fxlw1ifeb/ff0iA==", + "requires": { + "@reservoir0x/reservoir-sdk": "2.5.4", + "@solana/web3.js": "^1.98.0", + "axios": "^1.6.7", + "bs58": "^6.0.0", + "ts-retry-promise": "^0.8.1", + "tweetnacl": "^1.0.3", + "viem": "^2.22.19", + "zod": "^3.24.2" + }, + "dependencies": { + "@reservoir0x/reservoir-sdk": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@reservoir0x/reservoir-sdk/-/reservoir-sdk-2.5.4.tgz", + "integrity": "sha512-QCDMFo8hfHkWopxeLk//beqTargCJYxPV/rKPfUbSPAuSWRt4b7eoniq+zD65zaWwTaUyoYNvuB6Wemd/G3mXg==", + "requires": { + "axios": "^1.6.7" + } + }, + "base-x": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.1.tgz", + "integrity": "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==" + }, + "bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "requires": { + "base-x": "^5.0.0" + } + }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "ox": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.6.7.tgz", + "integrity": "sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==", + "requires": { + "@adraffy/ens-normalize": "^1.10.1", + "@noble/curves": "^1.6.0", + "@noble/hashes": "^1.5.0", + "@scure/bip32": "^1.5.0", + "@scure/bip39": "^1.4.0", + "abitype": "^1.0.6", + "eventemitter3": "5.0.1" + } + }, + "viem": { + "version": "2.22.23", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.22.23.tgz", + "integrity": "sha512-MheOu+joowphTCfCgdQ9BGU/z1IeHa6/ZIYNVc6KTwDklj671YS87cGv5kRCSU0vAfzN+5wjWyIffM8000KGkQ==", + "requires": { + "@noble/curves": "1.8.1", + "@noble/hashes": "1.7.1", + "@scure/bip32": "1.6.2", + "@scure/bip39": "1.5.4", + "abitype": "1.0.8", + "isows": "1.0.6", + "ox": "0.6.7", + "ws": "8.18.0" + } + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} + } + } + }, "@manypkg/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", @@ -31626,6 +31835,11 @@ "yn": "3.1.1" } }, + "ts-retry-promise": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/ts-retry-promise/-/ts-retry-promise-0.8.1.tgz", + "integrity": "sha512-+AHPUmAhr5bSRRK5CurE9kNH8gZlEHnCgusZ0zy2bjfatUBDX0h6vGQjiT0YrGwSDwRZmU+bapeX6mj55FOPvg==" + }, "tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", From 69e793278ced8c69c7dcebdae12e61454fc58ad9 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 01:36:36 -0400 Subject: [PATCH 03/10] fix imports --- typescript/agentkit/package.json | 2 +- .../src/action-providers/magiceden/magicEdenActionProvider.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json index b0d314ffd..34f2e1a46 100644 --- a/typescript/agentkit/package.json +++ b/typescript/agentkit/package.json @@ -43,7 +43,7 @@ "@alloralabs/allora-sdk": "^0.1.0", "@coinbase/coinbase-sdk": "^0.20.0", "@jup-ag/api": "^6.0.39", - "@magiceden/magiceden-sdk": "1.0.0-beta.2", + "@magiceden/magiceden-sdk": "1.0.0-beta.3", "@privy-io/public-api": "^2.18.5", "@privy-io/server-auth": "^1.18.4", "@solana/spl-token": "^0.4.12", diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index c7b8c6bfe..ffa528861 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -1,11 +1,10 @@ import { ActionProvider } from "../actionProvider"; import { CreateAction } from "../actionDecorator"; import { Network } from "../../network"; -import { Blockchain, MagicEdenClient, MagicEdenSDK, BuyParams, SolanaNftService } from "@magiceden/magiceden-sdk"; +import { Blockchain, MagicEdenClient, MagicEdenSDK, BuyParams, SolanaNftService, EvmNftService } from "@magiceden/magiceden-sdk"; import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; import { Keypair } from "@solana/web3.js"; import bs58 from "bs58"; -import { EvmNftService } from "@magiceden/magiceden-sdk/dist/services/nft/evm"; /** * Configuration options for the MagicEdenActionProvider. From c2c17a1ca511da5f97efee90352f450852b7aae3 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 02:55:11 -0400 Subject: [PATCH 04/10] fixes, setup buy functionality and some basic tests --- .../magiceden/magicEdenActionProvider.test.ts | 212 ++++++++++++++++++ .../magiceden/magicEdenActionProvider.ts | 36 ++- .../src/action-providers/magiceden/utils.ts | 2 +- 3 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts new file mode 100644 index 000000000..bdd90a39a --- /dev/null +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts @@ -0,0 +1,212 @@ +import { Blockchain, EvmBuyParams, MagicEdenSDK } from "@magiceden/magiceden-sdk"; +import { magicEdenActionProvider } from "./magicEdenActionProvider"; +import { Network } from "../../network"; + +jest.mock("@magiceden/magiceden-sdk", () => ({ + ...jest.requireActual("@magiceden/magiceden-sdk"), + MagicEdenSDK: { + v1: { + createSolanaKeypairClient: jest.fn(), + createViemEvmClient: jest.fn(), + }, + }, +})); + +describe("MagicEden Action Provider", () => { + const MOCK_API_KEY = "test-api-key"; + const MOCK_SOLANA_PRIVATE_KEY = "3CCF7x1YckEPTx8QnwQdtUYcABtmQCDkd26UpJBNNfnSnsko6b4uEKTn44FvdL9yKPHkGLjco6yPgaFL79szmV7c"; + const MOCK_EVM_PRIVATE_KEY = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; + const MOCK_CONTRACT = "0x1234567890123456789012345678901234567890"; + const MOCK_TOKEN_ID = "1"; + const MOCK_RPC_URL = "https://api.mainnet-beta.solana.com"; + + let actionProvider: ReturnType; + + beforeEach(() => { + jest.clearAllMocks(); + + // Mock default client implementations + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: jest.fn(), + }, + })); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: jest.fn(), + }, + })); + }); + + describe("buy", () => { + it("should successfully buy an NFT on Solana", async () => { + const mockBuyResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockBuy = jest.fn().mockResolvedValue(mockBuyResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: mockBuy, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args = { + token: "solana-mint-address", + seller: "seller-wallet-address", + price: "0.5", + }; + + const response = await actionProvider.buy(args); + + expect(mockBuy).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully bought NFT.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully buy an NFT on EVM", async () => { + const mockBuyResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockBuy = jest.fn().mockResolvedValue(mockBuyResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: mockBuy, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmBuyParams = { + chain: Blockchain.BASE, + items: [{ + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }], + }; + + const response = await actionProvider.buy(args); + + expect(mockBuy).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully bought NFT.\nTransactions: [evm-tx-id]"); + }); + + it("should handle failed transactions", async () => { + const mockBuyResponse = [{ status: "failed", error: "Insufficient funds" }]; + const mockBuy = jest.fn().mockResolvedValue(mockBuyResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: mockBuy, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmBuyParams = { + chain: Blockchain.BASE, + items: [{ + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }], + }; + + const response = await actionProvider.buy(args); + + expect(mockBuy).toHaveBeenCalledWith(args); + expect(response).toBe("Failed to buy NFT: Insufficient funds"); + }); + + it("should handle errors during buy operation", async () => { + const error = new Error("API error"); + const mockBuy = jest.fn().mockRejectedValue(error); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + buy: mockBuy, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmBuyParams = { + chain: Blockchain.BASE, + items: [{ + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }], + }; + + const response = await actionProvider.buy(args); + + expect(mockBuy).toHaveBeenCalledWith(args); + expect(response).toBe("Error buying NFT: Error: API error"); + }); + }); + + describe("supportsNetwork", () => { + it("should return true for supported EVM networks", () => { + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const baseNetwork: Network = { + protocolFamily: "evm", + networkId: "base-mainnet", + chainId: "8453", + }; + + expect(actionProvider.supportsNetwork(baseNetwork)).toBe(true); + }); + + it("should return true for supported Solana networks", () => { + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const solanaNetwork: Network = { + protocolFamily: "svm", + networkId: "solana-mainnet", + }; + + expect(actionProvider.supportsNetwork(solanaNetwork)).toBe(true); + }); + + it("should return false for unsupported networks", () => { + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const unsupportedNetwork: Network = { + protocolFamily: "evm", + networkId: "base_sepolia", + chainId: "84532", + }; + + expect(actionProvider.supportsNetwork(unsupportedNetwork)).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index ffa528861..ffc27bad7 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -1,7 +1,17 @@ import { ActionProvider } from "../actionProvider"; import { CreateAction } from "../actionDecorator"; import { Network } from "../../network"; -import { Blockchain, MagicEdenClient, MagicEdenSDK, BuyParams, SolanaNftService, EvmNftService } from "@magiceden/magiceden-sdk"; +import { + Blockchain, + MagicEdenClient, + MagicEdenSDK, + BuyParams, + SolanaNftService, + EvmNftService, + EvmBuyParams, + SolanaBuyParams, + TransactionResponse, +} from "@magiceden/magiceden-sdk"; import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; import { Keypair } from "@solana/web3.js"; import bs58 from "bs58"; @@ -37,7 +47,7 @@ export interface MagicEdenActionProviderConfig { export class MagicEdenActionProvider extends ActionProvider { private readonly solClient?: MagicEdenClient; private readonly evmClient?: MagicEdenClient; - + /** * Constructor for the MagicEdenActionProvider class. */ @@ -47,7 +57,7 @@ export class MagicEdenActionProvider extends ActionProvider { const apiKey = config.apiKey || process.env.MAGICEDEN_API_KEY; if (!apiKey) { throw new Error("MAGICEDEN_API_KEY is not configured."); - } + } const chain = getMagicEdenChainFromNetworkId(config.networkId || "base-mainnet"); switch (chain) { @@ -74,9 +84,8 @@ export class MagicEdenActionProvider extends ActionProvider { } /** - * Buys an NFT from the Magic Eden marketplace. + * Buys one or more NFTs from the Magic Eden marketplace. * - * @param walletProvider - The wallet provider for executing the buy. * @param args - Input parameters conforming to the BuySchema. * @returns A success message or error string. */ @@ -88,7 +97,22 @@ export class MagicEdenActionProvider extends ActionProvider { schema: BuyParams, }) public async buy(args: BuyParams): Promise { - throw new Error("Not implemented"); + try { + const response = this.solClient + ? await this.solClient?.nft.buy(args as SolanaBuyParams) + : await this.evmClient?.nft.buy(args as EvmBuyParams); + + const failures = response?.filter((r) => r.status === "failed" || r.error); + if (failures?.length) { + return `Failed to buy NFT: ${failures.map((f) => f.error).join(", ")}`; + } + + const transactionResponse = response?.map((r) => r as TransactionResponse).filter((r) => r !== undefined); + + return `Successfully bought NFT.\nTransactions: [${transactionResponse?.map((r) => r.txId).join(", ")}]`; + } catch (error) { + return `Error buying NFT: ${error}`; + } } /** diff --git a/typescript/agentkit/src/action-providers/magiceden/utils.ts b/typescript/agentkit/src/action-providers/magiceden/utils.ts index 5329e02c5..c694bee20 100644 --- a/typescript/agentkit/src/action-providers/magiceden/utils.ts +++ b/typescript/agentkit/src/action-providers/magiceden/utils.ts @@ -1,5 +1,5 @@ import { Network, NETWORK_ID_TO_CHAIN_ID } from "../../network"; -import { Blockchain } from "@temp-magiceden/magiceden-sdk"; +import { Blockchain } from "@magiceden/magiceden-sdk"; /** * Maps a network ID to the corresponding MagicEden blockchain. From 3477a2646369590cf88cd54963481d21bdbaf6e4 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:07:30 -0400 Subject: [PATCH 05/10] add remaining actions and remaining tests --- .../magiceden/magicEdenActionProvider.test.ts | 570 +++++++++++++++++- .../magiceden/magicEdenActionProvider.ts | 325 +++++++++- 2 files changed, 872 insertions(+), 23 deletions(-) diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts index bdd90a39a..f132d054c 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts @@ -1,4 +1,25 @@ -import { Blockchain, EvmBuyParams, MagicEdenSDK } from "@magiceden/magiceden-sdk"; +import { + Blockchain, + EvmBuyParams, + EvmCreateLaunchpadParams, + MagicEdenSDK, + SolanaCreateLaunchpadParams, + EvmProtocolType, + SolProtocolType, + MintStageKind, + EvmUpdateLaunchpadParams, + SolanaUpdateLaunchpadParams, + EvmListParams, + SolanaListParams, + EvmCancelListingParams, + SolanaCancelListingParams, + EvmMakeItemOfferParams, + SolanaMakeItemOfferParams, + EvmTakeItemOfferParams, + SolanaTakeItemOfferParams, + EvmCancelItemOfferParams, + SolanaCancelItemOfferParams, +} from "@magiceden/magiceden-sdk"; import { magicEdenActionProvider } from "./magicEdenActionProvider"; import { Network } from "../../network"; @@ -14,7 +35,8 @@ jest.mock("@magiceden/magiceden-sdk", () => ({ describe("MagicEden Action Provider", () => { const MOCK_API_KEY = "test-api-key"; - const MOCK_SOLANA_PRIVATE_KEY = "3CCF7x1YckEPTx8QnwQdtUYcABtmQCDkd26UpJBNNfnSnsko6b4uEKTn44FvdL9yKPHkGLjco6yPgaFL79szmV7c"; + const MOCK_SOLANA_PRIVATE_KEY = + "3CCF7x1YckEPTx8QnwQdtUYcABtmQCDkd26UpJBNNfnSnsko6b4uEKTn44FvdL9yKPHkGLjco6yPgaFL79szmV7c"; const MOCK_EVM_PRIVATE_KEY = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; const MOCK_CONTRACT = "0x1234567890123456789012345678901234567890"; const MOCK_TOKEN_ID = "1"; @@ -24,14 +46,14 @@ describe("MagicEden Action Provider", () => { beforeEach(() => { jest.clearAllMocks(); - + // Mock default client implementations (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ nft: { buy: jest.fn(), }, })); - + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ nft: { buy: jest.fn(), @@ -39,6 +61,514 @@ describe("MagicEden Action Provider", () => { })); }); + describe("createLaunchpad", () => { + it("should successfully create a launchpad on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockCreateLaunchpad = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + createLaunchpad: mockCreateLaunchpad, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaCreateLaunchpadParams = { + chain: Blockchain.SOLANA, + protocol: SolProtocolType.METAPLEX_CORE, + creator: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + name: "TestCollection", + symbol: "TEST", + description: "This is a test collection created with the Magic Eden self-serve API", + nftMetadataUrl: + "https://bafybeic3rs6wmnnhqachwxsiizlblignek6aitc5b3ooenhhtkez3onmwu.ipfs.w3s.link", + royaltyBps: 500, + royaltyRecipients: [ + { + address: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + share: 100, + }, + ], + payoutRecipient: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + social: { + discordUrl: "https://discord.gg/magiceden", + twitterUsername: "magiceden", + externalUrl: "https://magiceden.io", + }, + mintStages: { + maxSupply: 10000, + stages: [ + { + kind: MintStageKind.Public, + price: { + currency: { + chain: Blockchain.SOLANA, + assetId: "So11111111111111111111111111111111111111112", + }, + raw: "1", + }, + startTime: "2025-03-28T00:00:00.000Z", + endTime: "2030-03-30T00:00:00.000Z", + walletLimit: 10, + }, + ], + }, + isOpenEdition: false, + }; + + const response = await actionProvider.createLaunchpad(args); + + expect(mockCreateLaunchpad).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully created launchpad.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully create a launchpad on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockCreateLaunchpad = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + createLaunchpad: mockCreateLaunchpad, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmCreateLaunchpadParams = { + chain: Blockchain.BASE, + protocol: EvmProtocolType.ERC1155, + creator: "0x1234567890123456789012345678901234567890", + name: "TestCollection", + symbol: "TEST", + description: "This is a test collection created with the Magic Eden self-serve API", + nftMetadataUrl: + "https://bafybeic3rs6wmnnhqachwxsiizlblignek6aitc5b3ooenhhtkez3onmwu.ipfs.w3s.link", + royaltyBps: 500, + royaltyRecipients: [ + { + address: "0x1234567890123456789012345678901234567890", + share: 100, + }, + ], + payoutRecipient: "0x1234567890123456789012345678901234567890", + mintStages: { + tokenId: 0, + maxSupply: 10000, + walletLimit: 10, + stages: [ + { + kind: MintStageKind.Public, + price: { + currency: { + chain: Blockchain.BASE, + assetId: "0x0000000000000000000000000000000000000000", + }, + raw: "1", + }, + startTime: "2025-03-28T00:00:00.000Z", + endTime: "2030-03-30T00:00:00.000Z", + walletLimit: 10, + }, + ], + }, + }; + + const response = await actionProvider.createLaunchpad(args); + + expect(mockCreateLaunchpad).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully created launchpad.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("updateLaunchpad", () => { + it("should successfully update a launchpad on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockUpdateLaunchpad = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + updateLaunchpad: mockUpdateLaunchpad, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaUpdateLaunchpadParams = { + chain: Blockchain.SOLANA, + protocol: SolProtocolType.METAPLEX_CORE, + collectionId: "collection123", + owner: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + payer: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + symbol: "TEST2", + newSymbol: "TEST", + candyMachineId: "candy123", + name: "TestCollection", + payoutRecipient: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + royaltyRecipients: [ + { + address: "DRnGhQzbhxB8FsKdkTZRNqkJhGzPFhxGtxVXkqgXVGZv", + share: 100, + }, + ], + }; + + const response = await actionProvider.updateLaunchpad(args); + + expect(mockUpdateLaunchpad).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully updated launchpad.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully update a launchpad on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockUpdateLaunchpad = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + updateLaunchpad: mockUpdateLaunchpad, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmUpdateLaunchpadParams = { + chain: Blockchain.BASE, + protocol: EvmProtocolType.ERC1155, + tokenId: 0, + collectionId: "0x949de1b4d4cc4a8e63b7565b6dc525d8eb5dd15a", + owner: "0x1234567890123456789012345678901234567890", + name: "TestCollection2", + payoutRecipient: "0x1234567890123456789012345678901234567890", + }; + + const response = await actionProvider.updateLaunchpad(args); + + expect(mockUpdateLaunchpad).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully updated launchpad.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("listNft", () => { + it("should successfully list an NFT on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockList = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + list: mockList, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaListParams = { + token: "7um9nU7CDhss1fepFMRpjHhB3qm7exfQf47cdbRSUGuS", + price: "1000000000", // 1 SOL in lamports + }; + + const response = await actionProvider.listNft(args); + + expect(mockList).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully listed NFT.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully list an NFT on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockList = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + list: mockList, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmListParams = { + chain: Blockchain.BASE, + params: [ + { + token: "0x949de1b4d4cc4a8e63b7565b6dc525d8eb5dd15a:0", + price: "10000000012", + }, + ], + }; + + const response = await actionProvider.listNft(args); + + expect(mockList).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully listed NFT.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("cancelListing", () => { + it("should successfully cancel a listing on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockCancelListing = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + cancelListing: mockCancelListing, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaCancelListingParams = { + token: "7um9nU7CDhss1fepFMRpjHhB3qm7exfQf47cdbRSUGuS", + price: "1000000000", // 1 SOL in lamports + }; + + const response = await actionProvider.cancelListing(args); + + expect(mockCancelListing).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully canceled listing.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully cancel a listing on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockCancelListing = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + cancelListing: mockCancelListing, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmCancelListingParams = { + chain: Blockchain.BASE, + orderIds: ["0xc34124b0276f92ca985c2b7e25e9a5c3164c5aa45a2fe1ff1ac6c33b4665649c"], + }; + + const response = await actionProvider.cancelListing(args); + + expect(mockCancelListing).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully canceled listing.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("makeItemOffer", () => { + it("should successfully make an offer on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockMakeOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + makeItemOffer: mockMakeOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaMakeItemOfferParams = { + token: "7YCrxt8Ux9dym832BKLDQQWJYZ2uziXgbF6cYfZaChdP", + price: "900000", // 0.0009 SOL in lamports + }; + + const response = await actionProvider.makeItemOffer(args); + + expect(mockMakeOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully made item offer.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully make an offer on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockMakeOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + makeItemOffer: mockMakeOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmMakeItemOfferParams = { + chain: Blockchain.BASE, + params: [ + { + token: "0x1195cf65f83b3a5768f3c496d3a05ad6412c64b7:304163", + price: "9000", + }, + ], + }; + + const response = await actionProvider.makeItemOffer(args); + + expect(mockMakeOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully made item offer.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("takeItemOffer", () => { + it("should successfully take an offer on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockTakeOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + takeItemOffer: mockTakeOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaTakeItemOfferParams = { + token: "7um9nU7CDhss1fepFMRpjHhB3qm7exfQf47cdbRSUGuS", + buyer: "4H2bigFBsMoTAwkn7THDnThiRQuLCrFDGUWHf4YDpf14", + price: "1500000", // Original offer price + newPrice: "1000000", // Accepted price + }; + + const response = await actionProvider.takeItemOffer(args); + + expect(mockTakeOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully took item offer.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully take an offer on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockTakeOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + takeItemOffer: mockTakeOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmTakeItemOfferParams = { + chain: Blockchain.BASE, + items: [ + { + token: "0x949de1b4d4cc4a8e63b7565b6dc525d8eb5dd15a:0", + quantity: 1, + orderId: "0x18fc51e19bc96bc07b9bdd804eb055a691e46e3cd2c37a5d7e53daedebae70c4", + }, + ], + }; + + const response = await actionProvider.takeItemOffer(args); + + expect(mockTakeOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully took item offer.\nTransactions: [evm-tx-id]"); + }); + }); + + describe("cancelItemOffer", () => { + it("should successfully cancel an offer on Solana", async () => { + const mockResponse = [{ status: "success", txId: "solana-tx-id" }]; + const mockCancelOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createSolanaKeypairClient as jest.Mock).mockImplementation(() => ({ + nft: { + cancelItemOffer: mockCancelOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_SOLANA_PRIVATE_KEY, + networkId: "solana-mainnet", + rpcUrl: MOCK_RPC_URL, + }); + + const args: SolanaCancelItemOfferParams = { + token: "7YCrxt8Ux9dym832BKLDQQWJYZ2uziXgbF6cYfZaChdP", + price: "900000", // 0.0009 SOL in lamports + }; + + const response = await actionProvider.cancelItemOffer(args); + + expect(mockCancelOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully canceled item offer.\nTransactions: [solana-tx-id]"); + }); + + it("should successfully cancel an offer on EVM", async () => { + const mockResponse = [{ status: "success", txId: "evm-tx-id" }]; + const mockCancelOffer = jest.fn().mockResolvedValue(mockResponse); + + (MagicEdenSDK.v1.createViemEvmClient as jest.Mock).mockImplementation(() => ({ + nft: { + cancelItemOffer: mockCancelOffer, + }, + })); + + actionProvider = magicEdenActionProvider({ + apiKey: MOCK_API_KEY, + privateKey: MOCK_EVM_PRIVATE_KEY, + networkId: "base-mainnet", + }); + + const args: EvmCancelItemOfferParams = { + chain: Blockchain.BASE, + orderIds: ["0x18fc51e19bc96bc07b9bdd804eb055a691e46e3cd2c37a5d7e53daedebae70c4"], + }; + + const response = await actionProvider.cancelItemOffer(args); + + expect(mockCancelOffer).toHaveBeenCalledWith(args); + expect(response).toBe("Successfully canceled item offer.\nTransactions: [evm-tx-id]"); + }); + }); + describe("buy", () => { it("should successfully buy an NFT on Solana", async () => { const mockBuyResponse = [{ status: "success", txId: "solana-tx-id" }]; @@ -87,10 +617,12 @@ describe("MagicEden Action Provider", () => { const args: EvmBuyParams = { chain: Blockchain.BASE, - items: [{ - token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, - quantity: 1, - }], + items: [ + { + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }, + ], }; const response = await actionProvider.buy(args); @@ -117,10 +649,12 @@ describe("MagicEden Action Provider", () => { const args: EvmBuyParams = { chain: Blockchain.BASE, - items: [{ - token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, - quantity: 1, - }], + items: [ + { + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }, + ], }; const response = await actionProvider.buy(args); @@ -147,10 +681,12 @@ describe("MagicEden Action Provider", () => { const args: EvmBuyParams = { chain: Blockchain.BASE, - items: [{ - token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, - quantity: 1, - }], + items: [ + { + token: `${MOCK_CONTRACT}:${MOCK_TOKEN_ID}`, + quantity: 1, + }, + ], }; const response = await actionProvider.buy(args); @@ -209,4 +745,4 @@ describe("MagicEden Action Provider", () => { expect(actionProvider.supportsNetwork(unsupportedNetwork)).toBe(false); }); }); -}); \ No newline at end of file +}); diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index ffc27bad7..71453b645 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -11,6 +11,29 @@ import { EvmBuyParams, SolanaBuyParams, TransactionResponse, + SolanaCreateLaunchpadParams, + CreateLaunchpadParams, + ListParams, + CancelListingParams, + MakeItemOfferParams, + TakeItemOfferParams, + CancelItemOfferParams, + EvmCancelItemOfferParams, + SolanaCancelItemOfferParams, + SolanaTakeItemOfferParams, + EvmTakeItemOfferParams, + SolanaMakeItemOfferParams, + EvmMakeItemOfferParams, + SolanaCancelListingParams, + EvmCancelListingParams, + SolanaListParams, + EvmListParams, + SolanaUpdateLaunchpadParams, + EvmUpdateLaunchpadParams, + UpdateLaunchpadParams, + EvmCreateLaunchpadParams, + PublishLaunchpadParams, + SolanaPublishLaunchpadParams, } from "@magiceden/magiceden-sdk"; import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; import { Keypair } from "@solana/web3.js"; @@ -83,6 +106,292 @@ export class MagicEdenActionProvider extends ActionProvider { } } + // Create launchpad + + /** + * Creates a new launchpad. + * + * @param args - Input parameters conforming to the CreateLaunchpadSchema. + * @returns A success message or error string. + */ + @CreateAction({ + name: "createLaunchpad", + description: ` + + `, + schema: CreateLaunchpadParams, + }) + public async createLaunchpad(args: CreateLaunchpadParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.createLaunchpad(args as SolanaCreateLaunchpadParams) + : await this.evmClient?.nft.createLaunchpad(args as EvmCreateLaunchpadParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to create launchpad: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully created launchpad.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error creating launchpad: ${error}`; + } + } + + /** + * Publishes a launchpad (only for Solana) + * + * @param args - Input parameters conforming to the PublishLaunchpadParams. + * @returns A success message or error string. + */ + @CreateAction({ + name: "publishLaunchpad", + description: ` + Publishes a launchpad (only for Solana) + `, + schema: PublishLaunchpadParams, + }) + public async publishLaunchpad(args: PublishLaunchpadParams): Promise { + try { + if (!this.solClient) { + return `Solana client not initialized. Publish launchpad is only supported on Solana.`; + } + + const response = await this.solClient?.nft.publishLaunchpad( + args as SolanaPublishLaunchpadParams, + ); + if (!response) { + return `Failed to publish launchpad`; + } + + return `Successfully published launchpad.`; + } catch (error) { + return `Error publishing launchpad: ${error}`; + } + } + + /** + * Updates an existing launchpad. + * + * @param args - Input parameters for updating the launchpad. + * @returns A success message or error string. + */ + @CreateAction({ + name: "updateLaunchpad", + description: ` + + `, + schema: UpdateLaunchpadParams, + }) + public async updateLaunchpad(args: UpdateLaunchpadParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.updateLaunchpad(args as SolanaUpdateLaunchpadParams) + : await this.evmClient?.nft.updateLaunchpad(args as EvmUpdateLaunchpadParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to update launchpad: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully updated launchpad.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error updating launchpad: ${error}`; + } + } + + /** + * Lists an NFT for sale. + * + * @param args - Input parameters for listing the NFT. + * @returns A success message or error string. + */ + @CreateAction({ + name: "listNft", + description: ` + + `, + schema: ListParams, + }) + public async listNft(args: ListParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.list(args as SolanaListParams) + : await this.evmClient?.nft.list(args as EvmListParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to list NFT: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully listed NFT.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error listing NFT: ${error}`; + } + } + + /** + * Cancels an existing NFT listing. + * + * @param args - Input parameters for canceling the listing. + * @returns A success message or error string. + */ + @CreateAction({ + name: "cancelListing", + description: ` + + `, + schema: CancelListingParams, + }) + public async cancelListing(args: CancelListingParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.cancelListing(args as SolanaCancelListingParams) + : await this.evmClient?.nft.cancelListing(args as EvmCancelListingParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to cancel listing: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully canceled listing.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error canceling listing: ${error}`; + } + } + + /** + * Makes an offer on an NFT. + * + * @param args - Input parameters for making the offer. + * @returns A success message or error string. + */ + @CreateAction({ + name: "makeItemOffer", + description: ` + + `, + schema: MakeItemOfferParams, + }) + public async makeItemOffer(args: MakeItemOfferParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.makeItemOffer(args as SolanaMakeItemOfferParams) + : await this.evmClient?.nft.makeItemOffer(args as EvmMakeItemOfferParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to make item offer: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully made item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error making item offer: ${error}`; + } + } + + /** + * Accepts an existing offer on an NFT. + * + * @param args - Input parameters for accepting the offer. + * @returns A success message or error string. + */ + @CreateAction({ + name: "takeItemOffer", + description: ` + + `, + schema: TakeItemOfferParams, + }) + public async takeItemOffer(args: TakeItemOfferParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.takeItemOffer(args as SolanaTakeItemOfferParams) + : await this.evmClient?.nft.takeItemOffer(args as EvmTakeItemOfferParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to take item offer: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully took item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error taking item offer: ${error}`; + } + } + + /** + * Cancels an existing offer on an NFT. + * + * @param args - Input parameters for canceling the offer. + * @returns A success message or error string. + */ + @CreateAction({ + name: "cancelItemOffer", + description: ` + + `, + schema: CancelItemOfferParams, + }) + public async cancelItemOffer(args: CancelItemOfferParams): Promise { + try { + const response = this.solClient + ? await this.solClient?.nft.cancelItemOffer(args as SolanaCancelItemOfferParams) + : await this.evmClient?.nft.cancelItemOffer(args as EvmCancelItemOfferParams); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); + if (failures?.length) { + return `Failed to cancel item offer: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully canceled item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + } catch (error) { + return `Error canceling item offer: ${error}`; + } + } + /** * Buys one or more NFTs from the Magic Eden marketplace. * @@ -101,15 +410,19 @@ export class MagicEdenActionProvider extends ActionProvider { const response = this.solClient ? await this.solClient?.nft.buy(args as SolanaBuyParams) : await this.evmClient?.nft.buy(args as EvmBuyParams); - - const failures = response?.filter((r) => r.status === "failed" || r.error); + + const failures = response?.filter( + r => r.status === "failed" || r.status === undefined || r.error, + ); if (failures?.length) { - return `Failed to buy NFT: ${failures.map((f) => f.error).join(", ")}`; + return `Failed to buy NFT: ${failures.map(f => f.error).join(", ")}`; } - const transactionResponse = response?.map((r) => r as TransactionResponse).filter((r) => r !== undefined); - - return `Successfully bought NFT.\nTransactions: [${transactionResponse?.map((r) => r.txId).join(", ")}]`; + const transactionResponse = response + ?.map(r => r as TransactionResponse) + .filter(r => r !== undefined); + + return `Successfully bought NFT.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; } catch (error) { return `Error buying NFT: ${error}`; } From 5e06a9b9a6104eeb0fa530b86a47b9276a83982a Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:18:55 -0400 Subject: [PATCH 06/10] add descriptions --- .../magiceden/magicEdenActionProvider.ts | 326 +++++++++++++++++- 1 file changed, 323 insertions(+), 3 deletions(-) diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index 71453b645..7af672b23 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -34,6 +34,11 @@ import { EvmCreateLaunchpadParams, PublishLaunchpadParams, SolanaPublishLaunchpadParams, + SOL_MAX_NAME_LENGTH, + MIN_ROYALTY_BPS, + MAX_ROYALTY_BPS, + MAX_SYMBOL_LENGTH, + MAX_NAME_LENGTH, } from "@magiceden/magiceden-sdk"; import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; import { Keypair } from "@solana/web3.js"; @@ -106,8 +111,6 @@ export class MagicEdenActionProvider extends ActionProvider { } } - // Create launchpad - /** * Creates a new launchpad. * @@ -117,7 +120,42 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "createLaunchpad", description: ` + This tool will create a new NFT launchpad on Magic Eden. Both Solana and EVM chains are supported. + + It takes the following inputs: + - chain: The blockchain to deploy on (e.g., "solana", "base", "ethereum") + - protocol: The NFT standard to use (Solana: "metaplex", EVM: "erc721" or "erc1155") + - creator: The wallet address that will be the creator of the collection + - name: The name of the collection + - symbol: The symbol for the collection (max ${MAX_SYMBOL_LENGTH} characters) + - imageUrl: (Optional) URL pointing to the collection's image + - description: (Optional) Description of the collection + - royaltyBps: Royalty basis points (between ${MIN_ROYALTY_BPS} and ${MAX_ROYALTY_BPS}) + - royaltyRecipients: Array of recipients and their shares (must sum to 100%) + - payoutRecipient: Wallet address to receive mint proceeds + - nftMetadataUrl: (Optional) URL to metadata JSON files + - tokenImageUrl: (Optional) URL for token image in open editions + - mintStages: Configuration for the minting phases + + Solana-specific requirements: + - creator: Must be a valid Solana address + - payoutRecipient: Must be a valid Solana address + - social: (Optional) Social media links (Discord, Twitter, etc.) + - isOpenEdition: Whether the collection is an open edition + - Maximum 4 royalty recipients + - Name must be max ${SOL_MAX_NAME_LENGTH} characters + + EVM-specific requirements: + - Uses contract:tokenId format for token identifiers + - Supports both ERC721 and ERC1155 standards + - Prices in wei + Important notes: + - Creating a launchpad requires approval transactions and will incur gas fees + - For Solana, a separate 'publishLaunchpad' action is required after creation + - Royalty recipients' shares must sum to exactly 100% + - All URLs should be publicly accessible + - For non-open editions, metadata JSON files should follow the 0.json, 1.json naming pattern `, schema: CreateLaunchpadParams, }) @@ -153,7 +191,22 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "publishLaunchpad", description: ` - Publishes a launchpad (only for Solana) + This tool will publish a previously created launchpad, making it visible to the public on Magic Eden. Currently, this action is only required and supported for Solana launchpads. + + It takes the following inputs: + - chain: The blockchain to publish on (must be "solana") + - candyMachineId: The Solana address of the candy machine + - symbol: The symbol of the collection/launchpad + + Important notes: + - This action is only required for Solana launchpads + - Must be called after successfully creating a launchpad + - The candyMachineId is provided in the response of the createLaunchpad action + - The symbol must match the one used in createLaunchpad + - Publishing a launchpad requires an on-chain transaction and will incur gas fees + - Once published, the launchpad cannot be unpublished + - The launchpad must be published before minting can begin + - EVM launchpads are automatically published during creation and do not need this step `, schema: PublishLaunchpadParams, }) @@ -185,7 +238,45 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "updateLaunchpad", description: ` + This tool will update an existing NFT launchpad on Magic Eden. Both Solana and EVM chains are supported. + + It takes the following required inputs: + - chain: The blockchain where the collection is deployed + - protocol: The NFT standard (Solana: "metaplex", EVM: "erc721" or "erc1155") + - collectionId: The collection address/ID to update + - owner: The owner wallet address + + Optional parameters for both chains: + - name: Collection name (max ${MAX_NAME_LENGTH} chars, ${SOL_MAX_NAME_LENGTH} for Solana) + - imageUrl: URL pointing to collection image + - description: Collection description + - royaltyBps: Royalty basis points (${MIN_ROYALTY_BPS}-${MAX_ROYALTY_BPS}) + - royaltyRecipients: Array of {address, share} (shares must sum to 100%) + - payoutRecipient: Wallet to receive mint proceeds + - nftMetadataUrl: URL to metadata JSON files + - tokenImageUrl: URL for token image (open editions) + - mintStages: Configuration for minting phases + Solana-specific parameters: + - payer: Address paying for transaction fees + - candyMachineId: The Candy Machine address + - symbol: Current collection symbol + - newSymbol: (Optional) New symbol to update to + - social: Optional social media links (Discord, Twitter, etc.) + - Maximum 4 royalty recipients + + EVM-specific parameters: + - tokenId: Required for ERC1155 collections + - collectionId must be a valid EVM address + - Uses contract:tokenId format for tokens + + Important notes: + - Updates require approval transactions and will incur gas fees + - All URLs must be publicly accessible + - For non-open editions, metadata JSONs should follow 0.json, 1.json pattern + - Some parameters may be immutable depending on the chain and protocol + - All addresses must be valid for the specified chain + - Royalty recipient shares must sum to exactly 100% `, schema: UpdateLaunchpadParams, }) @@ -221,7 +312,43 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "listNft", description: ` + This tool will list an NFT for sale on the Magic Eden marketplace. Both Solana and EVM chains are supported. + + Required inputs for all chains: + - token: The NFT identifier + • For EVM: Use format "contractAddress:tokenId" + • For Solana: Use the NFT's mint address + - price: The listing price + • For EVM: Amount in wei (smallest unit) + • For Solana: Amount in lamports + - expiry: (Optional) Unix timestamp in seconds for when the listing should expire + + EVM-specific parameters: + - chain: The blockchain to list on (e.g., "base", "ethereum") + - params: Array of listings, each containing: + • token: Contract and token ID (e.g., "0x1234...abcd:123") + • price: Amount in wei + • expiry: (Optional) Expiration timestamp + Solana-specific parameters: + - auctionHouseAddress: (Optional) Custom auction house address + - tokenAccount: (Optional) Required only for legacy NFTs + - splPrice: (Optional) Details for SPL token pricing + - sellerReferral: (Optional) Referral address + - prioFeeMicroLamports: (Optional) Priority fee in micro lamports + - maxPrioFeeLamports: (Optional) Maximum priority fee + - exactPrioFeeLamports: (Optional) Exact priority fee + - txFeePayer: (Optional) Address paying for transaction fees + + Important notes: + - The wallet must own the NFT being listed + - First-time listings require an approval transaction + • This will incur a one-time gas fee + • Subsequent listings will be gasless + - Prices must be in the chain's smallest unit (wei/lamports) + - For EVM chains, multiple NFTs can be listed in one transaction + - For Solana, priority fees can be adjusted to speed up transactions + - Legacy Solana NFTs require additional token account information `, schema: ListParams, }) @@ -257,7 +384,42 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "cancelListing", description: ` + This tool will cancel an existing NFT listing on the Magic Eden marketplace. Both Solana and EVM chains are supported. + + Required inputs differ by chain: + + EVM-specific parameters: + - chain: The blockchain where the listing exists (e.g., "base", "ethereum") + - orderIds: Array of order IDs to cancel + • Each ID represents a specific listing to cancel + • Multiple listings can be canceled in one transaction + + Solana-specific parameters: + Required: + - token: The NFT's mint address + - price: The listing price that was used (in lamports) + Optional: + - tokenAccount: Required only for legacy NFTs + - auctionHouseAddress: Custom auction house address + - sellerReferral: Referral address + - expiry: Original listing expiration timestamp + - prioFeeMicroLamports: Priority fee in micro lamports + - maxPrioFeeLamports: Maximum priority fee + - exactPrioFeeLamports: Exact priority fee + + Important notes: + - Only the wallet that created the listing can cancel it + - Canceling a listing requires an on-chain transaction + • This will incur gas fees + • Gas fees are typically lower than listing fees + - For Solana: + • Legacy NFTs require the tokenAccount parameter + • Priority fees can be adjusted to speed up transactions + • The price must match the original listing exactly + - For EVM: + • Multiple listings can be canceled in one transaction + • Order IDs can be found in the original listing response `, schema: CancelListingParams, }) @@ -293,7 +455,46 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "makeItemOffer", description: ` + This tool will make an offer on an NFT listed on the Magic Eden marketplace. Both Solana and EVM chains are supported. + + Required inputs for all chains: + - token: The NFT identifier + • For EVM: Use format "contractAddress:tokenId" + • For Solana: Use the NFT's mint address + - price: The offer amount + • For EVM: Amount in wei (smallest unit) + • For Solana: Amount in lamports + - expiry: (Optional) Unix timestamp in seconds for when the offer should expire + + EVM-specific parameters: + - chain: The blockchain to make the offer on (e.g., "base", "ethereum") + - params: Array of offers, each containing: + • quantity: (Optional) Number of NFTs to bid on + • automatedRoyalties: (Optional) Auto-set royalty amounts and recipients + • royaltyBps: (Optional) Maximum royalty basis points to pay (1 BPS = 0.01%) + • currency: (Optional) Token address for payment (defaults to wrapped native token) + + Solana-specific parameters: + - auctionHouseAddress: (Optional) Custom auction house address + - buyerReferral: (Optional) Referral address + - useBuyV2: (Optional) Whether to use buy v2 protocol + - buyerCreatorRoyaltyPercent: (Optional) Buyer's share of creator royalties + - prioFeeMicroLamports: (Optional) Priority fee in micro lamports + - maxPrioFeeLamports: (Optional) Maximum priority fee + - exactPrioFeeLamports: (Optional) Exact priority fee + Important notes: + - The wallet must have sufficient funds to cover the offer amount + - Making an offer requires an approval transaction for the currency + • First-time approval will incur a gas fee + • Subsequent offers using the same currency will be gasless + - For EVM: + • Multiple offers can be made in one transaction + • Custom currencies (tokens) can be used instead of native currency + • Royalties can be configured or automated + - For Solana: + • Priority fees can be adjusted to speed up transactions + • Creator royalties can be split between buyer and seller `, schema: MakeItemOfferParams, }) @@ -329,7 +530,48 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "takeItemOffer", description: ` + This tool will accept an existing offer on an NFT listed on the Magic Eden marketplace. Both Solana and EVM chains are supported. + + Required inputs for all chains: + - token: The NFT identifier + • For EVM: Use format "contractAddress:tokenId" + • For Solana: Use the NFT's mint address + + EVM-specific parameters: + - chain: The blockchain where the offer exists (e.g., "base", "ethereum") + - items: Array of offers to accept, each containing: + • quantity: (Optional) Number of tokens to sell + • orderId: (Optional) Specific order ID to accept + + Solana-specific parameters: + Required: + - buyer: The wallet address of the offer maker + - price: The original offer price in lamports + - newPrice: The price at which to accept the offer + + Optional: + - auctionHouseAddress: Custom auction house address + - buyerReferral: Buyer's referral address + - sellerReferral: Seller's referral address + - buyerExpiry: Buyer's offer expiration timestamp + - sellerExpiry: Seller's acceptance expiration timestamp + - prioFeeMicroLamports: Priority fee in micro lamports + - maxPrioFeeLamports: Maximum priority fee + - exactPrioFeeLamports: Exact priority fee + Important notes: + - Only the NFT owner can accept offers + - Accepting an offer requires an on-chain transaction + • This will incur gas fees + • First-time approvals may require additional gas + - For EVM: + • Multiple offers can be accepted in one transaction + • Order IDs can be used to accept specific offers + • Quantity can be specified for ERC1155 tokens + - For Solana: + • Priority fees can be adjusted to speed up transactions + • Both original and new prices must be specified + • Expiration times can be set for both parties `, schema: TakeItemOfferParams, }) @@ -365,7 +607,41 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "cancelItemOffer", description: ` + This tool will cancel an existing offer on an NFT on the Magic Eden marketplace. Both Solana and EVM chains are supported. + Required inputs differ by chain: + + EVM-specific parameters: + - chain: The blockchain where the offer exists (e.g., "base", "ethereum") + - orderIds: Array of order IDs to cancel + • Each ID represents a specific offer to cancel + • Multiple offers can be canceled in one transaction + + Solana-specific parameters: + Required: + - token: The NFT's mint address + - price: The original offer price in lamports + + Optional: + - expiry: Original offer expiration timestamp + - auctionHouseAddress: Custom auction house address + - buyerReferral: Referral address + - prioFeeMicroLamports: Priority fee in micro lamports + - maxPrioFeeLamports: Maximum priority fee + - exactPrioFeeLamports: Exact priority fee + + Important notes: + - Only the wallet that made the offer can cancel it + - Canceling an offer requires an on-chain transaction + • This will incur gas fees + • Gas fees are typically lower than making offers + - For Solana: + • Priority fees can be adjusted to speed up transactions + • The price must match the original offer exactly + • Expiration time must match if it was set + - For EVM: + • Multiple offers can be canceled in one transaction + • Order IDs can be found in the original offer response `, schema: CancelItemOfferParams, }) @@ -401,7 +677,51 @@ export class MagicEdenActionProvider extends ActionProvider { @CreateAction({ name: "buy", description: ` + This tool will buy one or more NFTs from the Magic Eden marketplace. Both Solana and EVM chains are supported. + + Required inputs for all chains: + - token: The NFT identifier + • For EVM: Use format "contractAddress:tokenId" + • For Solana: Use the NFT's mint address + + EVM-specific parameters: + - chain: The blockchain to buy on (e.g., "base", "ethereum") + - currency: (Optional) Token address to use for payment + - currencyChainId: (Optional) Chain ID of the payment token + - items: Array of NFTs to buy, each containing: + • token: Contract and token ID + • collection: (Optional) Collection address + • quantity: (Optional) Number of NFTs to buy + • orderId: (Optional) Specific listing to buy from + + Solana-specific parameters: + Required: + - seller: The wallet address of the NFT seller + - price: The purchase price in lamports + + Optional: + - auctionHouseAddress: Custom auction house address + - buyerReferral: Buyer's referral address + - sellerReferral: Seller's referral address + - buyerExpiry: Buyer's purchase expiration timestamp + - sellerExpiry: Seller's listing expiration timestamp + - buyerCreatorRoyaltyPercent: Buyer's share of creator royalties + - splPrice: Details for SPL token purchases + Important notes: + - The wallet must have sufficient funds to cover the purchase + - Buying an NFT requires an on-chain transaction + • This will incur gas fees + • First-time token approvals may require additional gas + - For EVM: + • Multiple NFTs can be purchased in one transaction + • Custom currencies can be used for payment + • Order IDs can be used to buy specific listings + • Quantity can be specified for ERC1155 tokens + - For Solana: + • Each purchase requires a separate transaction + • Creator royalties can be split between buyer and seller + • SPL tokens can be used for payment `, schema: BuyParams, }) From c9a7f8bf921e1059adb92df98be20d635ba9f663 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:27:37 -0400 Subject: [PATCH 07/10] cleanup and fix tests --- .../magiceden/magicEdenActionProvider.test.ts | 36 ++--- .../magiceden/magicEdenActionProvider.ts | 150 +++++------------- 2 files changed, 61 insertions(+), 125 deletions(-) diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts index f132d054c..00531ecf3 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.test.ts @@ -125,7 +125,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.createLaunchpad(args); expect(mockCreateLaunchpad).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully created launchpad.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'createLaunchpad' action.\nTransactions: [solana-tx-id]"); }); it("should successfully create a launchpad on EVM", async () => { @@ -186,7 +186,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.createLaunchpad(args); expect(mockCreateLaunchpad).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully created launchpad.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'createLaunchpad' action.\nTransactions: [evm-tx-id]"); }); }); @@ -230,7 +230,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.updateLaunchpad(args); expect(mockUpdateLaunchpad).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully updated launchpad.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'updateLaunchpad' action.\nTransactions: [solana-tx-id]"); }); it("should successfully update a launchpad on EVM", async () => { @@ -262,7 +262,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.updateLaunchpad(args); expect(mockUpdateLaunchpad).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully updated launchpad.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'updateLaunchpad' action.\nTransactions: [evm-tx-id]"); }); }); @@ -292,7 +292,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.listNft(args); expect(mockList).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully listed NFT.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'listNft' action.\nTransactions: [solana-tx-id]"); }); it("should successfully list an NFT on EVM", async () => { @@ -324,7 +324,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.listNft(args); expect(mockList).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully listed NFT.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'listNft' action.\nTransactions: [evm-tx-id]"); }); }); @@ -354,7 +354,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.cancelListing(args); expect(mockCancelListing).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully canceled listing.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'cancelListing' action.\nTransactions: [solana-tx-id]"); }); it("should successfully cancel a listing on EVM", async () => { @@ -381,7 +381,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.cancelListing(args); expect(mockCancelListing).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully canceled listing.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'cancelListing' action.\nTransactions: [evm-tx-id]"); }); }); @@ -411,7 +411,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.makeItemOffer(args); expect(mockMakeOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully made item offer.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'makeItemOffer' action.\nTransactions: [solana-tx-id]"); }); it("should successfully make an offer on EVM", async () => { @@ -443,7 +443,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.makeItemOffer(args); expect(mockMakeOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully made item offer.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'makeItemOffer' action.\nTransactions: [evm-tx-id]"); }); }); @@ -475,7 +475,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.takeItemOffer(args); expect(mockTakeOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully took item offer.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'takeItemOffer' action.\nTransactions: [solana-tx-id]"); }); it("should successfully take an offer on EVM", async () => { @@ -508,7 +508,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.takeItemOffer(args); expect(mockTakeOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully took item offer.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'takeItemOffer' action.\nTransactions: [evm-tx-id]"); }); }); @@ -538,7 +538,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.cancelItemOffer(args); expect(mockCancelOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully canceled item offer.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'cancelItemOffer' action.\nTransactions: [solana-tx-id]"); }); it("should successfully cancel an offer on EVM", async () => { @@ -565,7 +565,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.cancelItemOffer(args); expect(mockCancelOffer).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully canceled item offer.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'cancelItemOffer' action.\nTransactions: [evm-tx-id]"); }); }); @@ -596,7 +596,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.buy(args); expect(mockBuy).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully bought NFT.\nTransactions: [solana-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'buy' action.\nTransactions: [solana-tx-id]"); }); it("should successfully buy an NFT on EVM", async () => { @@ -628,7 +628,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.buy(args); expect(mockBuy).toHaveBeenCalledWith(args); - expect(response).toBe("Successfully bought NFT.\nTransactions: [evm-tx-id]"); + expect(response).toBe("Successfully executed MagicEden 'buy' action.\nTransactions: [evm-tx-id]"); }); it("should handle failed transactions", async () => { @@ -660,7 +660,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.buy(args); expect(mockBuy).toHaveBeenCalledWith(args); - expect(response).toBe("Failed to buy NFT: Insufficient funds"); + expect(response).toBe("Failed to execute MagicEden 'buy' action: Insufficient funds"); }); it("should handle errors during buy operation", async () => { @@ -692,7 +692,7 @@ describe("MagicEden Action Provider", () => { const response = await actionProvider.buy(args); expect(mockBuy).toHaveBeenCalledWith(args); - expect(response).toBe("Error buying NFT: Error: API error"); + expect(response).toBe("Error executing MagicEden 'buy' action: Error: API error"); }); }); diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index 7af672b23..ce75cfea6 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -39,6 +39,7 @@ import { MAX_ROYALTY_BPS, MAX_SYMBOL_LENGTH, MAX_NAME_LENGTH, + OperationResponse, } from "@magiceden/magiceden-sdk"; import { getMagicEdenChainFromNetworkId, isSupportedNetwork } from "./utils"; import { Keypair } from "@solana/web3.js"; @@ -165,20 +166,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.createLaunchpad(args as SolanaCreateLaunchpadParams) : await this.evmClient?.nft.createLaunchpad(args as EvmCreateLaunchpadParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to create launchpad: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully created launchpad.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "createLaunchpad"); } catch (error) { - return `Error creating launchpad: ${error}`; + return `Error executing MagicEden 'createLaunchpad' action: ${error}`; } } @@ -219,13 +209,14 @@ export class MagicEdenActionProvider extends ActionProvider { const response = await this.solClient?.nft.publishLaunchpad( args as SolanaPublishLaunchpadParams, ); + if (!response) { - return `Failed to publish launchpad`; + return `Failed to execute MagicEden 'publishLaunchpad' action`; } - return `Successfully published launchpad.`; + return `Successfully executed MagicEden 'publishLaunchpad' action.`; } catch (error) { - return `Error publishing launchpad: ${error}`; + return `Error executing MagicEden 'publishLaunchpad' action: ${error}`; } } @@ -286,20 +277,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.updateLaunchpad(args as SolanaUpdateLaunchpadParams) : await this.evmClient?.nft.updateLaunchpad(args as EvmUpdateLaunchpadParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to update launchpad: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully updated launchpad.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "updateLaunchpad"); } catch (error) { - return `Error updating launchpad: ${error}`; + return `Error executing MagicEden 'updateLaunchpad' action: ${error}`; } } @@ -358,20 +338,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.list(args as SolanaListParams) : await this.evmClient?.nft.list(args as EvmListParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to list NFT: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully listed NFT.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "listNft"); } catch (error) { - return `Error listing NFT: ${error}`; + return `Error executing MagicEden 'listNft' action: ${error}`; } } @@ -429,20 +398,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.cancelListing(args as SolanaCancelListingParams) : await this.evmClient?.nft.cancelListing(args as EvmCancelListingParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to cancel listing: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully canceled listing.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "cancelListing"); } catch (error) { - return `Error canceling listing: ${error}`; + return `Error executing MagicEden 'cancelListing' action: ${error}`; } } @@ -504,20 +462,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.makeItemOffer(args as SolanaMakeItemOfferParams) : await this.evmClient?.nft.makeItemOffer(args as EvmMakeItemOfferParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to make item offer: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully made item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "makeItemOffer"); } catch (error) { - return `Error making item offer: ${error}`; + return `Error executing MagicEden 'makeItemOffer' action: ${error}`; } } @@ -581,20 +528,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.takeItemOffer(args as SolanaTakeItemOfferParams) : await this.evmClient?.nft.takeItemOffer(args as EvmTakeItemOfferParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to take item offer: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully took item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "takeItemOffer"); } catch (error) { - return `Error taking item offer: ${error}`; + return `Error executing MagicEden 'takeItemOffer' action: ${error}`; } } @@ -651,20 +587,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.cancelItemOffer(args as SolanaCancelItemOfferParams) : await this.evmClient?.nft.cancelItemOffer(args as EvmCancelItemOfferParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to cancel item offer: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully canceled item offer.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "cancelItemOffer"); } catch (error) { - return `Error canceling item offer: ${error}`; + return `Error executing MagicEden 'cancelItemOffer' action: ${error}`; } } @@ -731,20 +656,9 @@ export class MagicEdenActionProvider extends ActionProvider { ? await this.solClient?.nft.buy(args as SolanaBuyParams) : await this.evmClient?.nft.buy(args as EvmBuyParams); - const failures = response?.filter( - r => r.status === "failed" || r.status === undefined || r.error, - ); - if (failures?.length) { - return `Failed to buy NFT: ${failures.map(f => f.error).join(", ")}`; - } - - const transactionResponse = response - ?.map(r => r as TransactionResponse) - .filter(r => r !== undefined); - - return `Successfully bought NFT.\nTransactions: [${transactionResponse?.map(r => r.txId).join(", ")}]`; + return this.handleTransactionResponse(response, "buy"); } catch (error) { - return `Error buying NFT: ${error}`; + return `Error executing MagicEden 'buy' action: ${error}`; } } @@ -755,6 +669,28 @@ export class MagicEdenActionProvider extends ActionProvider { * @returns True if supported, false otherwise. */ public supportsNetwork = (network: Network): boolean => isSupportedNetwork(network); + + private handleTransactionResponse( + response: OperationResponse[] | undefined, + action: string + ): string { + if (!response) { + return `Failed to ${action}`; + } + + const failures = response.filter( + r => r.status === "failed" || r.status === undefined || r.error + ); + if (failures.length) { + return `Failed to execute MagicEden '${action}' action: ${failures.map(f => f.error).join(", ")}`; + } + + const transactionResponse = response + .filter(r => r !== undefined) + .map(r => r as TransactionResponse); + + return `Successfully executed MagicEden '${action}' action.\nTransactions: [${transactionResponse.map(r => r.txId).join(", ")}]`; + } } export const magicEdenActionProvider = (config: MagicEdenActionProviderConfig) => From 3ae0d147a741c6c62ce8aad6ae7ae62523315a3e Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Thu, 10 Apr 2025 13:36:52 -0400 Subject: [PATCH 08/10] add readme info --- typescript/agentkit/README.md | 41 +++++++ .../src/action-providers/magiceden/README | 104 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 typescript/agentkit/src/action-providers/magiceden/README diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index e80216763..2ecda0e20 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -330,6 +330,47 @@ const agent = createReactAgent({
+Magic Eden + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
createLaunchpadCreates a new NFT launchpad on Magic Eden with customizable parameters like name, symbol, royalties, and mint stages.
publishLaunchpadPublishes a previously created Solana launchpad, making it visible to the public on Magic Eden.
updateLaunchpadUpdates an existing NFT launchpad's parameters such as name, description, royalties, or mint stages.
listNftLists an NFT for sale on the Magic Eden marketplace with specified price and optional expiration.
cancelListingCancels an existing NFT listing on the Magic Eden marketplace.
makeItemOfferMakes an offer on an NFT listed on Magic Eden with specified price and optional expiration.
takeItemOfferAccepts an existing offer on an NFT listed on Magic Eden.
cancelItemOfferCancels an existing offer on an NFT on the Magic Eden marketplace.
buyBuys one or more NFTs directly from the Magic Eden marketplace at listed prices.
+
+
Pyth diff --git a/typescript/agentkit/src/action-providers/magiceden/README b/typescript/agentkit/src/action-providers/magiceden/README new file mode 100644 index 000000000..91a546398 --- /dev/null +++ b/typescript/agentkit/src/action-providers/magiceden/README @@ -0,0 +1,104 @@ +# Magic Eden Action Provider + +This directory contains the **MagicEdenActionProvider** implementation, which provides actions to interact with the **Magic Eden NFT marketplace** across multiple blockchains (Solana and EVM chains). + +## Directory Structure + +``` +magiceden/ +├── magicEdenActionProvider.ts # Main provider implementation +├── magicEdenActionProvider.test.ts # Test file for provider +├── schemas.ts # Action parameter schemas +├── utils.ts # Utility functions and network mappings +├── index.ts # Main exports +└── README.md # This file +``` + +## Actions + +### NFT Trading +- `buy`: Buy one or more NFTs at listed prices +- `listNft`: List an NFT for sale with specified price +- `cancelListing`: Cancel an existing NFT listing +- `makeItemOffer`: Make an offer on a listed NFT +- `takeItemOffer`: Accept an existing offer on your NFT +- `cancelItemOffer`: Cancel an existing offer you made + +### Launchpad Management +- `createLaunchpad`: Create a new NFT launchpad with customizable parameters +- `publishLaunchpad`: Publish a Solana launchpad (required after creation) +- `updateLaunchpad`: Update an existing launchpad's parameters + +## Network Support + +The Magic Eden provider supports the following networks: + +### EVM Networks +- Ethereum +- Base +- Polygon +- Sei +- Arbitrum +- ApeChain +- BeraChain +- Monad Testnet +- Abstract + +### Solana Networks +- Solana Mainnet + +## Configuration + +The provider requires the following configuration: + +```typescript +interface MagicEdenActionProviderConfig { + apiKey?: string; // Magic Eden API key + networkId?: string; // Network identifier + privateKey?: string; // Wallet private key + rpcUrl?: string; // RPC URL (required for Solana) +} +``` + +Configuration can be provided via: +- Environment variable: `MAGICEDEN_API_KEY` +- Provider configuration: `apiKey` parameter + +## Usage Examples + +### Listing an NFT +```typescript +const result = await provider.listNft({ + token: "0x1234...5678:1", // EVM: contract:tokenId + price: "1000000000" // Price in wei/lamports +}); +``` + +### Making an Offer +```typescript +const result = await provider.makeItemOffer({ + token: "0x1234...5678:1", + price: "900000000" +}); +``` + +### Creating a Launchpad +```typescript +const result = await provider.createLaunchpad({ + chain: "solana", + name: "My Collection", + symbol: "MYCOL", + // ... other parameters +}); +``` + +For complete examples, see the [magicEdenActionProvider.test.ts](magicEdenActionProvider.test.ts) file or the README file in the [Magic Eden SDK GitHub](https://github.com/magiceden/magiceden-sdk). + +## Notes + +- For Solana operations, a valid RPC URL must be provided +- EVM operations support batch transactions for multiple NFTs +- Launchpad creation on Solana requires a separate publish step +- All prices should be in the chain's smallest unit (wei/lamports) + +For more information on the Magic Eden API, visit [Magic Eden Developer Documentation](https://docs.magiceden.io//) or visit the [Magic Eden SDK GitHub](https://github.com/magiceden/magiceden-sdk). \ No newline at end of file From 83f1bbe1d73e961eb17b357d6a89dc45b41d739c Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Fri, 11 Apr 2025 01:58:54 -0400 Subject: [PATCH 09/10] more accurate descriptions --- .../magiceden/magicEdenActionProvider.ts | 219 ++++++++---------- 1 file changed, 102 insertions(+), 117 deletions(-) diff --git a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts index ce75cfea6..aca93d234 100644 --- a/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts +++ b/typescript/agentkit/src/action-providers/magiceden/magicEdenActionProvider.ts @@ -124,32 +124,34 @@ export class MagicEdenActionProvider extends ActionProvider { This tool will create a new NFT launchpad on Magic Eden. Both Solana and EVM chains are supported. It takes the following inputs: - - chain: The blockchain to deploy on (e.g., "solana", "base", "ethereum") - - protocol: The NFT standard to use (Solana: "metaplex", EVM: "erc721" or "erc1155") + - chain: The blockchain to deploy on, one of ["solana", "ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] + - protocol: The NFT standard to use (Solana: "METAPLEX_CORE", EVM: "ERC721" or "ERC1155") - creator: The wallet address that will be the creator of the collection - - name: The name of the collection + - name: The name of the collection (max ${MAX_NAME_LENGTH} characters, ${SOL_MAX_NAME_LENGTH} for Solana) - symbol: The symbol for the collection (max ${MAX_SYMBOL_LENGTH} characters) - imageUrl: (Optional) URL pointing to the collection's image - description: (Optional) Description of the collection - royaltyBps: Royalty basis points (between ${MIN_ROYALTY_BPS} and ${MAX_ROYALTY_BPS}) - - royaltyRecipients: Array of recipients and their shares (must sum to 100%) + - royaltyRecipients: Array of {address, share} (shares must sum to 100%) (maximum of 4 royalty recipients for Solana) - payoutRecipient: Wallet address to receive mint proceeds - nftMetadataUrl: (Optional) URL to metadata JSON files - tokenImageUrl: (Optional) URL for token image in open editions - - mintStages: Configuration for the minting phases - - Solana-specific requirements: - - creator: Must be a valid Solana address - - payoutRecipient: Must be a valid Solana address - - social: (Optional) Social media links (Discord, Twitter, etc.) + - mintStages: Configuration for the minting phases, containing: + • stages: (Optional) Array of mint stages, minimum 1 stage. Each stage has: + - kind: Type of mint stage ("public" or "allowlist") + - price: Object with {currency: string, raw: string} for mint price + - startTime: Start time in ISO format (YYYY-MM-DDTHH:MM:SS.MSZ) + - endTime: End time in ISO format (YYYY-MM-DDTHH:MM:SS.MSZ) + - walletLimit: (Optional) Max mints per wallet (0-10000) + - maxSupply: (Optional) Max supply for this stage (1-uint256) + - allowlist: (Optional, for allowlist kind) Array of allowed wallet addresses (2-2500 addresses) + • tokenId: (Optional) Token ID for ERC1155 collections + • walletLimit: (Optional) Default wallet limit if no stages defined (0-10000) + • maxSupply: (Optional) Total items available for minting (1-uint256) + + Solana-specific parameters: - isOpenEdition: Whether the collection is an open edition - - Maximum 4 royalty recipients - - Name must be max ${SOL_MAX_NAME_LENGTH} characters - - EVM-specific requirements: - - Uses contract:tokenId format for token identifiers - - Supports both ERC721 and ERC1155 standards - - Prices in wei + - social: (Optional) Social media links (Discord, Twitter, etc.) Important notes: - Creating a launchpad requires approval transactions and will incur gas fees @@ -157,6 +159,8 @@ export class MagicEdenActionProvider extends ActionProvider { - Royalty recipients' shares must sum to exactly 100% - All URLs should be publicly accessible - For non-open editions, metadata JSON files should follow the 0.json, 1.json naming pattern + - Mint stages must not overlap in time + - For Solana, if no stages are defined but walletLimit is set, it becomes the default public mint stage limit `, schema: CreateLaunchpadParams, }) @@ -231,35 +235,50 @@ export class MagicEdenActionProvider extends ActionProvider { description: ` This tool will update an existing NFT launchpad on Magic Eden. Both Solana and EVM chains are supported. - It takes the following required inputs: - - chain: The blockchain where the collection is deployed - - protocol: The NFT standard (Solana: "metaplex", EVM: "erc721" or "erc1155") + All chains take the following inputs: + - chain: The blockchain to update on, one of ["solana", "ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] + - protocol: The NFT standard (e.g., "METAPLEX_CORE" for Solana, "ERC721" or "ERC1155" for EVM) - collectionId: The collection address/ID to update - owner: The owner wallet address + - name: (Optional) Collection name (max ${MAX_NAME_LENGTH} chars, ${SOL_MAX_NAME_LENGTH} for Solana) + - imageUrl: (Optional) URL pointing to collection image + - description: (Optional) Collection description + - royaltyBps: (Optional) Royalty basis points (${MIN_ROYALTY_BPS}-${MAX_ROYALTY_BPS}) + - royaltyRecipients: (Optional) Array of {address, share} (shares must sum to 100%) + - payoutRecipient: (Optional) Wallet to receive mint proceeds + - nftMetadataUrl: (Optional) URL to metadata JSON files + - tokenImageUrl: (Optional) URL for token image (open editions) + - mintStages: (Optional) Configuration for minting phases, containing: + • stages: (Optional) Array of mint stages, minimum 1 stage. Each stage has: + - kind: Type of mint stage ("public" or "allowlist") + - price: Object with {currency: string, raw: string} for mint price + - startTime: Start time in ISO format (YYYY-MM-DDTHH:MM:SS.MSZ) + - endTime: End time in ISO format (YYYY-MM-DDTHH:MM:SS.MSZ) + - walletLimit: (Optional) Max mints per wallet (0-10000) + - maxSupply: (Optional) Max supply for this stage (1-uint256) + - allowlist: (Optional, for allowlist kind) Array of allowed wallet addresses (2-2500 addresses) + • tokenId: (Optional) Token ID for ERC1155 collections + • walletLimit: (Optional) Default wallet limit if no stages defined (0-10000) + • maxSupply: (Optional) Total items available for minting (1-uint256) - Optional parameters for both chains: - - name: Collection name (max ${MAX_NAME_LENGTH} chars, ${SOL_MAX_NAME_LENGTH} for Solana) - - imageUrl: URL pointing to collection image - - description: Collection description - - royaltyBps: Royalty basis points (${MIN_ROYALTY_BPS}-${MAX_ROYALTY_BPS}) - - royaltyRecipients: Array of {address, share} (shares must sum to 100%) - - payoutRecipient: Wallet to receive mint proceeds - - nftMetadataUrl: URL to metadata JSON files - - tokenImageUrl: URL for token image (open editions) - - mintStages: Configuration for minting phases - + EVM-specific parameters: + - tokenId: (Optional) Token ID for ERC1155 collections, required if protocol is "ERC1155" + - collectionId must be a valid EVM address + - Uses contract:tokenId format for tokens + Solana-specific parameters: - payer: Address paying for transaction fees - candyMachineId: The Candy Machine address - symbol: Current collection symbol + - name: Current collection name (required on Solana) + - royaltyRecipients: Array of {address, share} (shares must sum to 100%) (required on Solana) (maximum of 4 royalty recipients) + - payoutRecipient: Wallet to receive mint proceeds (required on Solana) - newSymbol: (Optional) New symbol to update to - - social: Optional social media links (Discord, Twitter, etc.) - - Maximum 4 royalty recipients - - EVM-specific parameters: - - tokenId: Required for ERC1155 collections - - collectionId must be a valid EVM address - - Uses contract:tokenId format for tokens + - social: (Optional) Social media links (Discord, Twitter, etc.) + • discordUrl: (Optional) Discord URL + • twitterUsername: (Optional) Twitter username + • telegramUrl: (Optional) Telegram URL + • externalUrl: (Optional) External URL Important notes: - Updates require approval transactions and will incur gas fees @@ -294,24 +313,17 @@ export class MagicEdenActionProvider extends ActionProvider { description: ` This tool will list an NFT for sale on the Magic Eden marketplace. Both Solana and EVM chains are supported. - Required inputs for all chains: - - token: The NFT identifier - • For EVM: Use format "contractAddress:tokenId" - • For Solana: Use the NFT's mint address - - price: The listing price - • For EVM: Amount in wei (smallest unit) - • For Solana: Amount in lamports - - expiry: (Optional) Unix timestamp in seconds for when the listing should expire - EVM-specific parameters: - - chain: The blockchain to list on (e.g., "base", "ethereum") + - chain: The blockchain to list on, one of ["ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] - params: Array of listings, each containing: - • token: Contract and token ID (e.g., "0x1234...abcd:123") + • token: Contract and token ID in format "contractAddress:tokenId" • price: Amount in wei - • expiry: (Optional) Expiration timestamp + • expiry: (Optional) Expiration Unix timestamp in seconds for when the listing should expire Solana-specific parameters: - - auctionHouseAddress: (Optional) Custom auction house address + - token: The NFT's mint address + - price: The listing price in lamports + - expiry: (Optional) Expiration Unix timestamp in seconds for when the listing should expire - tokenAccount: (Optional) Required only for legacy NFTs - splPrice: (Optional) Details for SPL token pricing - sellerReferral: (Optional) Referral address @@ -364,18 +376,14 @@ export class MagicEdenActionProvider extends ActionProvider { • Multiple listings can be canceled in one transaction Solana-specific parameters: - Required: - token: The NFT's mint address - price: The listing price that was used (in lamports) - - Optional: - - tokenAccount: Required only for legacy NFTs - - auctionHouseAddress: Custom auction house address - - sellerReferral: Referral address - - expiry: Original listing expiration timestamp - - prioFeeMicroLamports: Priority fee in micro lamports - - maxPrioFeeLamports: Maximum priority fee - - exactPrioFeeLamports: Exact priority fee + - tokenAccount: (Optional) Required only for legacy NFTs + - sellerReferral: (Optional) Referral address + - expiry: (Optional) Original listing expiration Unix timestamp in seconds + - prioFeeMicroLamports: (Optional) Priority fee in micro lamports + - maxPrioFeeLamports: (Optional) Maximum priority fee + - exactPrioFeeLamports: (Optional) Exact priority fee Important notes: - Only the wallet that created the listing can cancel it @@ -415,25 +423,21 @@ export class MagicEdenActionProvider extends ActionProvider { description: ` This tool will make an offer on an NFT listed on the Magic Eden marketplace. Both Solana and EVM chains are supported. - Required inputs for all chains: - - token: The NFT identifier - • For EVM: Use format "contractAddress:tokenId" - • For Solana: Use the NFT's mint address - - price: The offer amount - • For EVM: Amount in wei (smallest unit) - • For Solana: Amount in lamports - - expiry: (Optional) Unix timestamp in seconds for when the offer should expire - EVM-specific parameters: - - chain: The blockchain to make the offer on (e.g., "base", "ethereum") + - chain: The blockchain to make the offer on, one of ["ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] - params: Array of offers, each containing: + • token: Contract and token ID in format "contractAddress:tokenId" + • price: The offer amount in wei (smallest unit) + • expiry: (Optional) Offer expiration Unix timestamp in seconds • quantity: (Optional) Number of NFTs to bid on • automatedRoyalties: (Optional) Auto-set royalty amounts and recipients • royaltyBps: (Optional) Maximum royalty basis points to pay (1 BPS = 0.01%) • currency: (Optional) Token address for payment (defaults to wrapped native token) Solana-specific parameters: - - auctionHouseAddress: (Optional) Custom auction house address + - token: The NFT's mint address + - price: The offer amount in lamports + - expiry: (Optional) Offer expiration Unix timestamp in seconds - buyerReferral: (Optional) Referral address - useBuyV2: (Optional) Whether to use buy v2 protocol - buyerCreatorRoyaltyPercent: (Optional) Buyer's share of creator royalties @@ -478,33 +482,26 @@ export class MagicEdenActionProvider extends ActionProvider { name: "takeItemOffer", description: ` This tool will accept an existing offer on an NFT listed on the Magic Eden marketplace. Both Solana and EVM chains are supported. - - Required inputs for all chains: - - token: The NFT identifier - • For EVM: Use format "contractAddress:tokenId" - • For Solana: Use the NFT's mint address - + EVM-specific parameters: - - chain: The blockchain where the offer exists (e.g., "base", "ethereum") + - chain: The blockchain where the offer exists, one of ["ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] - items: Array of offers to accept, each containing: + • token: Contract and token ID in format "contractAddress:tokenId" • quantity: (Optional) Number of tokens to sell • orderId: (Optional) Specific order ID to accept Solana-specific parameters: - Required: + - token: The NFT's mint address - buyer: The wallet address of the offer maker - price: The original offer price in lamports - newPrice: The price at which to accept the offer - - Optional: - - auctionHouseAddress: Custom auction house address - - buyerReferral: Buyer's referral address - - sellerReferral: Seller's referral address - - buyerExpiry: Buyer's offer expiration timestamp - - sellerExpiry: Seller's acceptance expiration timestamp - - prioFeeMicroLamports: Priority fee in micro lamports - - maxPrioFeeLamports: Maximum priority fee - - exactPrioFeeLamports: Exact priority fee + - buyerReferral: (Optional) Buyer's referral address + - sellerReferral: (Optional) Seller's referral address + - buyerExpiry: (Optional) Buyer's offer expiration Unix timestamp in seconds + - sellerExpiry: (Optional) Seller's acceptance expiration Unix timestamp in seconds + - prioFeeMicroLamports: (Optional) Priority fee in micro lamports + - maxPrioFeeLamports: (Optional) Maximum priority fee + - exactPrioFeeLamports: (Optional) Exact priority fee Important notes: - Only the NFT owner can accept offers @@ -548,23 +545,19 @@ export class MagicEdenActionProvider extends ActionProvider { Required inputs differ by chain: EVM-specific parameters: - - chain: The blockchain where the offer exists (e.g., "base", "ethereum") + - chain: The blockchain where the offer exists, one of ["ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] - orderIds: Array of order IDs to cancel • Each ID represents a specific offer to cancel • Multiple offers can be canceled in one transaction Solana-specific parameters: - Required: - token: The NFT's mint address - price: The original offer price in lamports - - Optional: - - expiry: Original offer expiration timestamp - - auctionHouseAddress: Custom auction house address - - buyerReferral: Referral address - - prioFeeMicroLamports: Priority fee in micro lamports - - maxPrioFeeLamports: Maximum priority fee - - exactPrioFeeLamports: Exact priority fee + - expiry: (Optional) Original offer expiration Unix timestamp in seconds + - buyerReferral: (Optional) Referral address + - prioFeeMicroLamports: (Optional) Priority fee in micro lamports + - maxPrioFeeLamports: (Optional) Maximum priority fee + - exactPrioFeeLamports: (Optional) Exact priority fee Important notes: - Only the wallet that made the offer can cancel it @@ -604,34 +597,26 @@ export class MagicEdenActionProvider extends ActionProvider { description: ` This tool will buy one or more NFTs from the Magic Eden marketplace. Both Solana and EVM chains are supported. - Required inputs for all chains: - - token: The NFT identifier - • For EVM: Use format "contractAddress:tokenId" - • For Solana: Use the NFT's mint address - EVM-specific parameters: - - chain: The blockchain to buy on (e.g., "base", "ethereum") + - chain: The blockchain to buy on, one of ["ethereum", "base", "polygon", "sei", "arbitrum", "apechain", "berachain", "monad_testnet", "bsc", "abstract"] - currency: (Optional) Token address to use for payment - currencyChainId: (Optional) Chain ID of the payment token - items: Array of NFTs to buy, each containing: - • token: Contract and token ID + • token: Contract and token ID in format "contractAddress:tokenId" • collection: (Optional) Collection address • quantity: (Optional) Number of NFTs to buy • orderId: (Optional) Specific listing to buy from Solana-specific parameters: - Required: + - token: The NFT's mint address - seller: The wallet address of the NFT seller - price: The purchase price in lamports - - Optional: - - auctionHouseAddress: Custom auction house address - - buyerReferral: Buyer's referral address - - sellerReferral: Seller's referral address - - buyerExpiry: Buyer's purchase expiration timestamp - - sellerExpiry: Seller's listing expiration timestamp - - buyerCreatorRoyaltyPercent: Buyer's share of creator royalties - - splPrice: Details for SPL token purchases + - buyerReferral: (Optional) Buyer's referral address + - sellerReferral: (Optional) Seller's referral address + - buyerExpiry: (Optional) Buyer's purchase expiration Unix timestamp in seconds + - sellerExpiry: (Optional) Seller's listing expiration Unix timestamp in seconds + - buyerCreatorRoyaltyPercent: (Optional) Buyer's share of creator royalties + - splPrice: (Optional) Details for SPL token purchases Important notes: - The wallet must have sufficient funds to cover the purchase From 52d3ad398e60944d363e43f800fe9cf76f5f8a47 Mon Sep 17 00:00:00 2001 From: Matthew <19557547+mtleliever@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:00:51 -0400 Subject: [PATCH 10/10] add missing export --- typescript/agentkit/src/action-providers/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/typescript/agentkit/src/action-providers/index.ts b/typescript/agentkit/src/action-providers/index.ts index 6f2aeab75..19736788e 100644 --- a/typescript/agentkit/src/action-providers/index.ts +++ b/typescript/agentkit/src/action-providers/index.ts @@ -26,3 +26,4 @@ export * from "./wow"; export * from "./allora"; export * from "./flaunch"; export * from "./onramp"; +export * from "./magiceden";