Merge branch 'main' into main

This commit is contained in:
aryan
2025-01-07 17:54:10 +05:30
committed by GitHub
20 changed files with 9042 additions and 877 deletions

View File

@@ -35,7 +35,10 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin
- Balance checks
- Stake SOL
- Zk compressed Airdrop by Light Protocol and Helius
- **NFTs on 3.Land**
- Create your own collection
- NFT creation and automatic listing on 3.land
- List your NFT for sale in any SPL token
- **NFT Management via Metaplex**
- Collection deployment
- NFT minting
@@ -127,6 +130,57 @@ const result = await agent.deployToken(
console.log("Token Mint Address:", result.mint.toString());
```
### Create NFT Collection on 3Land
```typescript
const optionsWithBase58: StoreInitOptions = {
privateKey: "",
isMainnet: true, // if false, collection will be created on devnet 3.land (dev.3.land)
};
const collectionOpts: CreateCollectionOptions = {
collectionName: "",
collectionSymbol: "",
collectionDescription: "",
mainImageUrl: ""
};
const result = await agent.create3LandCollection(
optionsWithBase58,
collectionOpts
);
```
### Create NFT on 3Land
When creating an NFT using 3Land's tool, it automatically goes for sale on 3.land website
```typescript
const optionsWithBase58: StoreInitOptions = {
privateKey: "",
isMainnet: true, // if false, listing will be on devnet 3.land (dev.3.land)
};
const collectionAccount = ""; //hash for the collection
const createItemOptions: CreateSingleOptions = {
itemName: "",
sellerFee: 500, //5%
itemAmount: 100, //total items to be created
itemSymbol: "",
itemDescription: "",
traits: [
{ trait_type: "", value: "" },
],
price: 0, //100000000 == 0.1 sol, can be set to 0 for a free mint
mainImageUrl: "",
splHash: "", //present if listing is on a specific SPL token, if not present sale will be on $SOL
};
const isMainnet = true;
const result = await agent.create3LandNft(
optionsWithBase58,
collectionAccount,
createItemOptions,
isMainnet
);
```
### Create NFT Collection

View File

@@ -3,7 +3,7 @@ import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
export const agentKit = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL!,
process.env.OPENAI_API_KEY!,
{ OPENAI_API_KEY: process.env.OPENAI_API_KEY! },
);
export const solanaTools = createSolanaTools(agentKit);

View File

@@ -0,0 +1,3 @@
OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=

View File

@@ -0,0 +1,50 @@
# AI Guided Market Making Agent
This agent showcases an ai guided market maker on Manifest, Solana's CLOB DEX. The agent guides the user to setup basic two-sided quotes on Manifest markets.
[Reference](https://github.com/CKS-Systems/manifest)
## Key Features
- **Automated Quoting**: The agent automatically refreshes quotes on an interval.
- **Reducing Complexity**: Designed to abstract away parameters for setting up market making.
- **Random Model**: The market making introduces randomness to prevent front running or other negative botting behavoirs.
## Example
=== Market Maker Configuration ===
Enter the market ID: 2Uj8277fkaVBtTU6Wp2GPRbQC86SkSdgQ2mp1Q5N2LHc
Enter the base token symbol (e.g., SEND): SEND
Enter the quote token symbol (e.g., USDC): USDC
=== Quote Parameters (applies to both buy and sell sides) ===
Enter number of quotes to place on each side: 4
Enter minimum quote depth (% distance from mid price): 0.1
Enter maximum quote depth (% distance from mid price): 2
=== Token Allowances ===
Enter total SEND allowance: 2
Enter total USDC allowance: 3
Enter update interval in seconds: 20
=== Configuration Summary ===
{
"marketId": "2Uj8277fkaVBtTU6Wp2GPRbQC86SkSdgQ2mp1Q5N2LHc",
"baseToken": "SEND",
"quoteToken": "USDC",
"quoteParams": {
"number": 4,
"minDepth": 0.1,
"maxDepth": 2
},
"allowance": {
"base": 2,
"quote": 3
},
"intervalSeconds": 20
}
Is this configuration correct? (yes/no): yes
Starting market maker mode for SEND/USDC...

View File

@@ -0,0 +1,389 @@
import { HumanMessage } from "@langchain/core/messages";
import { MemorySaver } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
import * as dotenv from "dotenv";
import * as fs from "fs";
import * as readline from "readline";
import { createSolanaTools, SolanaAgentKit } from "../../src";
dotenv.config();
function validateEnvironment(): void {
const missingVars: string[] = [];
const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY"];
requiredVars.forEach((varName) => {
if (!process.env[varName]) {
missingVars.push(varName);
}
});
if (missingVars.length > 0) {
console.error("Error: Required environment variables are not set");
missingVars.forEach((varName) => {
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
});
process.exit(1);
}
}
validateEnvironment();
const WALLET_DATA_FILE = "wallet_data.txt";
async function initializeAgent() {
try {
const llm = new ChatOpenAI({
modelName: "gpt-4o-mini",
temperature: 0.3,
});
let walletDataStr: string | null = null;
if (fs.existsSync(WALLET_DATA_FILE)) {
try {
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
} catch (error) {
console.error("Error reading wallet data:", error);
}
}
const solanaAgent = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL!,
{
OPENAI_API_KEY: process.env.OPENAI_API_KEY!,
},
);
const tools = createSolanaTools(solanaAgent);
const memory = new MemorySaver();
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
const agent = createReactAgent({
llm,
tools,
checkpointSaver: memory,
messageModifier: `
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
can't do with your currently available tools, you must say so, and encourage them to implement it
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
`,
});
if (walletDataStr) {
fs.writeFileSync(WALLET_DATA_FILE, walletDataStr);
}
return { agent, config };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
}
}
async function runAutonomousMode(agent: any, config: any, interval = 10) {
console.log("Starting autonomous mode...");
while (true) {
try {
const thought =
"Be creative and do something interesting on the blockchain. " +
"Choose an action or set of actions and execute it that highlights your abilities.";
const stream = await agent.stream(
{ messages: [new HumanMessage(thought)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
}
async function runChatMode(agent: any, config: any) {
console.log("Starting chat mode... Type 'exit' to end.");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
try {
while (true) {
const userInput = await question("\nPrompt: ");
if (userInput.toLowerCase() === "exit") {
break;
}
const stream = await agent.stream(
{ messages: [new HumanMessage(userInput)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
} finally {
rl.close();
}
}
interface MarketMakerConfig {
marketId: string;
baseToken: string;
quoteToken: string;
quoteParams: {
number: number; // Number of quotes on each side
minDepth: number; // Minimum distance from mid (%)
maxDepth: number; // Maximum distance from mid (%)
};
allowance: {
base: number;
quote: number;
};
intervalSeconds: number;
}
function createReadlineInterface() {
return readline.createInterface({
input: process.stdin,
output: process.stdout,
});
}
async function askQuestion(
rl: readline.Interface,
question: string,
): Promise<string> {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
});
});
}
async function configureMarketMaker(): Promise<MarketMakerConfig> {
const rl = createReadlineInterface();
try {
console.log("\n=== Market Maker Configuration ===\n");
// Basic market information
const marketId = await askQuestion(rl, "Enter the market ID: ");
const baseToken = await askQuestion(
rl,
"Enter the base token symbol (e.g., SEND): ",
);
const quoteToken = await askQuestion(
rl,
"Enter the quote token symbol (e.g., USDC): ",
);
// Quote parameters
console.log(
"\n=== Quote Parameters (applies to both buy and sell sides) ===",
);
const quoteNumber = parseInt(
await askQuestion(rl, "Enter number of quotes to place on each side: "),
);
const minDepth = parseFloat(
await askQuestion(
rl,
"Enter minimum quote depth (% distance from mid price): ",
),
);
const maxDepth = parseFloat(
await askQuestion(
rl,
"Enter maximum quote depth (% distance from mid price): ",
),
);
// Token allowances
console.log("\n=== Token Allowances ===");
const baseAllowance = parseFloat(
await askQuestion(rl, `Enter total ${baseToken} allowance: `),
);
const quoteAllowance = parseFloat(
await askQuestion(rl, `Enter total ${quoteToken} allowance: `),
);
// Update interval
const interval = parseInt(
await askQuestion(rl, "\nEnter update interval in seconds: "),
);
const config: MarketMakerConfig = {
marketId,
baseToken,
quoteToken,
quoteParams: {
number: quoteNumber,
minDepth: minDepth,
maxDepth: maxDepth,
},
allowance: {
base: baseAllowance,
quote: quoteAllowance,
},
intervalSeconds: interval,
};
// Display summary
console.log("\n=== Configuration Summary ===");
console.log(JSON.stringify(config, null, 2));
const confirm = await askQuestion(
rl,
"\nIs this configuration correct? (yes/no): ",
);
if (confirm.toLowerCase() !== "yes") {
throw new Error("Configuration cancelled by user");
}
return config;
} finally {
rl.close();
}
}
async function runMarketMakerMode(agent: any, config: any) {
try {
const marketMakerConfig = await configureMarketMaker();
console.log(
`\nStarting market maker mode for ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken}...`,
);
while (true) {
try {
const thought = `You are an on-chain Solana market maker for the ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken} Manifest market, ${marketMakerConfig.marketId}.
Find the ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken} live price by checking Jupiter.
Use solana_batch_order to provide ${marketMakerConfig.quoteParams.number} buys at different prices between -${marketMakerConfig.quoteParams.minDepth}% to -${marketMakerConfig.quoteParams.maxDepth}% and ${marketMakerConfig.quoteParams.number} sells at different prices between +${marketMakerConfig.quoteParams.minDepth}% to +${marketMakerConfig.quoteParams.maxDepth}% with increasing quantities further from the live price.
You have an allowance of ${marketMakerConfig.allowance.base} ${marketMakerConfig.baseToken} and ${marketMakerConfig.allowance.quote} ${marketMakerConfig.quoteToken}.
Important! Only send 1 transaction, buy and sells can be combined into a single solana_batch_order.`;
const stream = await agent.stream(
{ messages: [new HumanMessage(thought)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
await new Promise((resolve) =>
setTimeout(resolve, marketMakerConfig.intervalSeconds * 1000),
);
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error);
}
process.exit(1);
}
}
} catch (error) {
console.error("Configuration error:", error);
process.exit(1);
}
}
async function chooseMode(): Promise<"chat" | "auto" | "mm"> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
while (true) {
console.log("\nAvailable modes:");
console.log("1. chat - Interactive chat mode");
console.log("2. auto - Autonomous action mode");
console.log("3. mm - AI guided market making");
const choice = (await question("\nChoose a mode (enter number or name): "))
.toLowerCase()
.trim();
rl.close();
if (choice === "1" || choice === "chat") {
return "chat";
} else if (choice === "2" || choice === "auto") {
return "auto";
} else if (choice === "3" || choice === "mm") {
return "mm";
}
console.log("Invalid choice. Please try again.");
}
}
async function main() {
try {
console.log("Starting Agent...");
const { agent, config } = await initializeAgent();
const mode = await chooseMode();
if (mode === "chat") {
await runChatMode(agent, config);
} else if (mode === "auto") {
await runAutonomousMode(agent, config);
} else {
await runMarketMakerMode(agent, config);
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
if (require.main === module) {
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
}

View File

@@ -0,0 +1,16 @@
{
"name": "market-making-agent",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"main": "ts-node index.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@langchain/langgraph-checkpoint-postgres": "^0.0.2",
"solana-agent-kit": "^1.3.6"
}
}

6000
examples/market-making-agent/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
"author": "sendaifun",
"license": "Apache-2.0",
"dependencies": {
"@3land/listings-sdk": "^0.0.4",
"@ai-sdk/openai": "^1.0.11",
"@bonfida/spl-name-service": "^3.0.7",
"@cks-systems/manifest-sdk": "0.1.59",

2734
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -75,6 +75,15 @@ import {
FlashTradeParams,
FlashCloseTradeParams,
} from "../types";
import {
createCollection,
createSingle,
} from "../tools/create_3land_collectible";
import {
CreateCollectionOptions,
CreateSingleOptions,
StoreInitOptions,
} from "@3land/listings-sdk/dist/types/implementation/implementationTypes";
/**
* Main class for interacting with Solana blockchain
@@ -563,4 +572,27 @@ export class SolanaAgentKit {
async flashCloseTrade(params: FlashCloseTradeParams): Promise<string> {
return flashCloseTrade(this, params);
}
async create3LandCollection(
optionsWithBase58: StoreInitOptions,
collectionOpts: CreateCollectionOptions,
): Promise<string> {
const tx = await createCollection(optionsWithBase58, collectionOpts);
return `Transaction: ${tx}`;
}
async create3LandNft(
optionsWithBase58: StoreInitOptions,
collectionAccount: string,
createItemOptions: CreateSingleOptions,
isMainnet: boolean,
): Promise<string> {
const tx = await createSingle(
optionsWithBase58,
collectionAccount,
createItemOptions,
isMainnet,
);
return `Transaction: ${tx}`;
}
}

View File

@@ -10,6 +10,11 @@ import {
} from "../index";
import { create_image, FEE_TIERS, generateOrdersfromPattern } from "../tools";
import { marketTokenMap } from "../utils/flashUtils";
import {
CreateCollectionOptions,
CreateSingleOptions,
StoreInitOptions,
} from "@3land/listings-sdk/dist/types/implementation/implementationTypes";
export class SolanaBalanceTool extends Tool {
name = "solana_balance";
@@ -2255,6 +2260,153 @@ export class SolanaFetchTokenDetailedReportTool extends Tool {
}
}
export class Solana3LandCreateSingle extends Tool {
name = "3land_minting_tool";
description = `Creates an NFT and lists it on 3.land's website
Inputs:
privateKey (required): represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
collectionAccount (optional): represents the account for the nft collection
itemName (required): the name of the NFT
sellerFee (required): the fee of the seller
itemAmount (required): the amount of the NFTs that can be minted
itemDescription (required): the description of the NFT
traits (required): the traits of the NFT [{trait_type: string, value: string}]
price (required): the price of the item, if is 0 the listing will be free
mainImageUrl (required): the main image of the NFT
coverImageUrl (optional): the cover image of the NFT
splHash (optional): the hash of the spl token, if not provided listing will be in $SOL
isMainnet (required): defines is the tx takes places in mainnet
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const privateKey = inputFormat.privateKey;
const isMainnet = inputFormat.isMainnet;
const optionsWithBase58: StoreInitOptions = {
...(privateKey && { privateKey }),
...(isMainnet && { isMainnet }),
};
let collectionAccount = inputFormat.collectionAccount;
const itemName = inputFormat?.itemName;
const sellerFee = inputFormat?.sellerFee;
const itemAmount = inputFormat?.itemAmount;
const itemSymbol = inputFormat?.itemSymbol;
const itemDescription = inputFormat?.itemDescription;
const traits = inputFormat?.traits;
const price = inputFormat?.price;
const mainImageUrl = inputFormat?.mainImageUrl;
const coverImageUrl = inputFormat?.coverImageUrl;
const splHash = inputFormat?.splHash;
const createItemOptions: CreateSingleOptions = {
...(itemName && { itemName }),
...(sellerFee && { sellerFee }),
...(itemAmount && { itemAmount }),
...(itemSymbol && { itemSymbol }),
...(itemDescription && { itemDescription }),
...(traits && { traits }),
...(price && { price }),
...(mainImageUrl && { mainImageUrl }),
...(coverImageUrl && { coverImageUrl }),
...(splHash && { splHash }),
};
if (!collectionAccount) {
throw new Error("Collection account is required");
}
const tx = await this.solanaKit.create3LandNft(
optionsWithBase58,
collectionAccount,
createItemOptions,
isMainnet,
);
return JSON.stringify({
status: "success",
message: `Created listing successfully ${tx}`,
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class Solana3LandCreateCollection extends Tool {
name = "3land_minting_tool";
description = `Creates an NFT Collection that you can visit on 3.land's website (3.land/collection/{collectionAccount})
Inputs:
privateKey (required): represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
isMainnet (required): defines is the tx takes places in mainnet
collectionSymbol (required): the symbol of the collection
collectionName (required): the name of the collection
collectionDescription (required): the description of the collection
mainImageUrl (required): the image of the collection
coverImageUrl (optional): the cover image of the collection
`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const inputFormat = JSON.parse(input);
const privateKey = inputFormat.privateKey;
const isMainnet = inputFormat.isMainnet;
const optionsWithBase58: StoreInitOptions = {
...(privateKey && { privateKey }),
...(isMainnet && { isMainnet }),
};
const collectionSymbol = inputFormat?.collectionSymbol;
const collectionName = inputFormat?.collectionName;
const collectionDescription = inputFormat?.collectionDescription;
const mainImageUrl = inputFormat?.mainImageUrl;
const coverImageUrl = inputFormat?.coverImageUrl;
const collectionOpts: CreateCollectionOptions = {
...(collectionSymbol && { collectionSymbol }),
...(collectionName && { collectionName }),
...(collectionDescription && { collectionDescription }),
...(mainImageUrl && { mainImageUrl }),
...(coverImageUrl && { coverImageUrl }),
};
const tx = await this.solanaKit.create3LandCollection(
optionsWithBase58,
collectionOpts,
);
return JSON.stringify({
status: "success",
message: `Created Collection successfully ${tx}`,
transaction: tx,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
@@ -2307,9 +2459,12 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaCancelNFTListingTool(solanaKit),
new SolanaFetchTokenReportSummaryTool(solanaKit),
new SolanaFetchTokenDetailedReportTool(solanaKit),
new Solana3LandCreateSingle(solanaKit),
new Solana3LandCreateCollection(solanaKit),
new SolanaPerpOpenTradeTool(solanaKit),
new SolanaPerpCloseTradeTool(solanaKit),
new SolanaFlashOpenTrade(solanaKit),
new SolanaFlashCloseTrade(solanaKit),
new Solana3LandCreateSingle(solanaKit),
];
}

View File

@@ -1,37 +0,0 @@
import {
PublicKey,
sendAndConfirmTransaction,
Transaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { ManifestClient } from "@cks-systems/manifest-sdk";
/**
* Cancels all orders from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function cancelAllOrders(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const cancelAllOrdersIx = await mfxClient.cancelAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(cancelAllOrdersIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Cancel all orders failed: ${error.message}`);
}
}

View File

@@ -0,0 +1,69 @@
import { createCollectionImp, createSingleImp } from "@3land/listings-sdk";
import {
StoreInitOptions,
CreateCollectionOptions,
CreateSingleOptions,
} from "@3land/listings-sdk/dist/types/implementation/implementationTypes";
/**
* Create a collection on 3Land
* @param optionsWithBase58 represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
* @param collectionOpts represents the options for the collection creation
* @returns
*/
export async function createCollection(
optionsWithBase58: StoreInitOptions,
collectionOpts: CreateCollectionOptions,
) {
try {
const collection = await createCollectionImp(
optionsWithBase58,
collectionOpts,
);
return collection;
} catch (error: any) {
throw new Error(`Collection creation failed: ${error.message}`);
}
}
/**
* Create a single edition on 3Land
* @param optionsWithBase58 represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
* @param collectionAccount represents the account for the nft collection
* @param createItemOptions the options for the creation of the single NFT listing
* @returns
*/
export async function createSingle(
optionsWithBase58: StoreInitOptions,
collectionAccount: string,
createItemOptions: CreateSingleOptions,
isMainnet: boolean,
) {
try {
const landStore = isMainnet
? "AmQNs2kgw4LvS9sm6yE9JJ4Hs3JpVu65eyx9pxMG2xA"
: "GyPCu89S63P9NcCQAtuSJesiefhhgpGWrNVJs4bF2cSK";
const singleEditionTx = await createSingleImp(
optionsWithBase58,
landStore,
collectionAccount,
createItemOptions,
);
return singleEditionTx;
} catch (error: any) {
throw new Error(`Single edition creation failed: ${error.message}`);
}
}
/**
* Buy a single edition on 3Land
* @param
* @returns
*/
// export async function buySingle() {
// try {
// } catch (error: any) {
// throw new Error(`Buying single edition failed: ${error.message}`);
// }
// }

View File

@@ -1,6 +1,4 @@
export * from "./adrena_perp_trading";
export * from "./batch_order";
export * from "./cancel_all_orders";
export * from "./create_gibwork_task";
export * from "./create_image";
export * from "./create_tiplinks";
@@ -20,8 +18,7 @@ export * from "./get_tps";
export * from "./get_wallet_address";
export * from "./launch_pumpfun_token";
export * from "./lend";
export * from "./limit_order";
export * from "./manifest_create_market";
export * from "./manifest_trade";
export * from "./mint_nft";
export * from "./openbook_create_market";
export * from "./orca_close_position";
@@ -46,7 +43,7 @@ export * from "./stake_with_solayer";
export * from "./tensor_trade";
export * from "./trade";
export * from "./transfer";
export * from "./withdraw_all";
export * from "./flash_open_trade";
export * from "./flash_close_trade";
export * from "./create_3land_collectible";

View File

@@ -1,61 +0,0 @@
import {
PublicKey,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import {
ManifestClient,
WrapperPlaceOrderParamsExternal,
OrderType,
} from "@cks-systems/manifest-sdk";
/**
* Place limit orders using Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @param quantity Amount to trade in tokens
* @param side Buy or Sell
* @param price Price in tokens ie. SOL/USDC
* @returns Transaction signature
*/
export async function limitOrder(
agent: SolanaAgentKit,
marketId: PublicKey,
quantity: number,
side: string,
price: number,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const orderParams: WrapperPlaceOrderParamsExternal = {
numBaseTokens: quantity,
tokenPrice: price,
isBid: side === "Buy",
lastValidSlot: 0,
orderType: OrderType.Limit,
clientOrderId: Number(Math.random() * 1000),
};
const depositPlaceOrderIx: TransactionInstruction[] =
await mfxClient.placeOrderWithRequiredDepositIx(
agent.wallet.publicKey,
orderParams,
);
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...depositPlaceOrderIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Limit Order failed: ${error.message}`);
}
}

View File

@@ -1,43 +0,0 @@
import { ManifestClient } from "@cks-systems/manifest-sdk";
import {
Keypair,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
export async function manifestCreateMarket(
agent: SolanaAgentKit,
baseMint: PublicKey,
quoteMint: PublicKey,
): Promise<string[]> {
const marketKeypair: Keypair = Keypair.generate();
const FIXED_MANIFEST_HEADER_SIZE: number = 256;
const createAccountIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: agent.wallet.publicKey,
newAccountPubkey: marketKeypair.publicKey,
space: FIXED_MANIFEST_HEADER_SIZE,
lamports: await agent.connection.getMinimumBalanceForRentExemption(
FIXED_MANIFEST_HEADER_SIZE,
),
programId: new PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"),
});
const createMarketIx = ManifestClient["createMarketIx"](
agent.wallet.publicKey,
baseMint,
quoteMint,
marketKeypair.publicKey,
);
const tx: Transaction = new Transaction();
tx.add(createAccountIx);
tx.add(createMarketIx);
const signature = await sendAndConfirmTransaction(agent.connection, tx, [
agent.wallet,
marketKeypair,
]);
return [signature, marketKeypair.publicKey.toBase58()];
}

View File

@@ -1,16 +1,159 @@
import {
PublicKey,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
} from "@solana/web3.js";
import {
ManifestClient,
WrapperPlaceOrderParamsExternal,
OrderType,
WrapperPlaceOrderParamsExternal,
} from "@cks-systems/manifest-sdk";
import { SolanaAgentKit } from "../index";
import { BatchOrderPattern, OrderParams } from "../types";
import {
Keypair,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { BatchOrderPattern, OrderParams, SolanaAgentKit } from "../index";
export async function manifestCreateMarket(
agent: SolanaAgentKit,
baseMint: PublicKey,
quoteMint: PublicKey,
): Promise<string[]> {
const marketKeypair: Keypair = Keypair.generate();
const FIXED_MANIFEST_HEADER_SIZE: number = 256;
const createAccountIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: agent.wallet.publicKey,
newAccountPubkey: marketKeypair.publicKey,
space: FIXED_MANIFEST_HEADER_SIZE,
lamports: await agent.connection.getMinimumBalanceForRentExemption(
FIXED_MANIFEST_HEADER_SIZE,
),
programId: new PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"),
});
const createMarketIx = ManifestClient["createMarketIx"](
agent.wallet.publicKey,
baseMint,
quoteMint,
marketKeypair.publicKey,
);
const tx: Transaction = new Transaction();
tx.add(createAccountIx);
tx.add(createMarketIx);
const signature = await sendAndConfirmTransaction(agent.connection, tx, [
agent.wallet,
marketKeypair,
]);
return [signature, marketKeypair.publicKey.toBase58()];
}
/**
* Place limit orders using Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @param quantity Amount to trade in tokens
* @param side Buy or Sell
* @param price Price in tokens ie. SOL/USDC
* @returns Transaction signature
*/
export async function limitOrder(
agent: SolanaAgentKit,
marketId: PublicKey,
quantity: number,
side: string,
price: number,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const orderParams: WrapperPlaceOrderParamsExternal = {
numBaseTokens: quantity,
tokenPrice: price,
isBid: side === "Buy",
lastValidSlot: 0,
orderType: OrderType.Limit,
clientOrderId: Number(Math.random() * 1000),
};
const depositPlaceOrderIx: TransactionInstruction[] =
await mfxClient.placeOrderWithRequiredDepositIx(
agent.wallet.publicKey,
orderParams,
);
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...depositPlaceOrderIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Limit Order failed: ${error.message}`);
}
}
/**
* Cancels all orders from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function cancelAllOrders(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const cancelAllOrdersIx = await mfxClient.cancelAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(cancelAllOrdersIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Cancel all orders failed: ${error.message}`);
}
}
/**
* Withdraws all funds from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function withdrawAll(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const withdrawAllIx = await mfxClient.withdrawAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...withdrawAllIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Withdraw all failed: ${error.message}`);
}
}
/**
* Generates an array of orders based on the specified pattern

View File

@@ -1,37 +0,0 @@
import {
PublicKey,
sendAndConfirmTransaction,
Transaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { ManifestClient } from "@cks-systems/manifest-sdk";
/**
* Withdraws all funds from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function withdrawAll(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const withdrawAllIx = await mfxClient.withdrawAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...withdrawAllIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Withdraw all failed: ${error.message}`);
}
}

63
test/tools/3land.ts Normal file
View File

@@ -0,0 +1,63 @@
import {
CreateCollectionOptions,
CreateSingleOptions,
StoreInitOptions,
} from "@3land/listings-sdk/dist/types/implementation/implementationTypes";
import "dotenv/config";
import { SolanaAgentKit, createSolanaTools } from "../../src";
const agent = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL!,
{ OPENAI_API_KEY: process.env.OPENAI_API_KEY! },
);
const optionsWithBase58: StoreInitOptions = {
privateKey: process.env.SOLANA_PRIVATE_KEY!,
isMainnet: false,
};
/****************************** CREATING COLLECTION ******************************** */
const collectionOpts: CreateCollectionOptions = {
collectionName: "",
collectionSymbol: "",
collectionDescription: "",
mainImageUrl: "",
};
(async () => {
const collection = await agent.create3LandCollection(
optionsWithBase58,
collectionOpts,
);
console.log("collection: ", collection);
})();
/****************************** CREATING NFT ******************************** */
const collectionAccount = "";
const createItemOptions: CreateSingleOptions = {
itemName: "",
sellerFee: 500, //5%
itemAmount: 100,
itemSymbol: "",
itemDescription: "",
traits: [{ trait_type: "", value: "" }],
price: 0, //100000000 == 0.1 sol
mainImageUrl: "",
};
const isMainnet = false;
(async () => {
const result = agent.create3LandNft(
optionsWithBase58,
collectionAccount,
createItemOptions,
isMainnet,
);
console.log("result: ", result);
})();
export { SolanaAgentKit, createSolanaTools };

View File

@@ -22,7 +22,6 @@
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]