Skip to content

Commit

Permalink
save
Browse files Browse the repository at this point in the history
  • Loading branch information
mPaella committed Dec 10, 2024
1 parent d0bc8d0 commit 948ba70
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 0 deletions.
32 changes: 32 additions & 0 deletions typescript/packages/plugins/solana-magiceden/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@goat-sdk/plugin-tensor",
"version": "0.0.1",
"files": ["dist/**/*", "README.md", "package.json"],
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"test": "vitest run --passWithNoTests"
},
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"dependencies": {
"@goat-sdk/core": "workspace:*",
"@solana/web3.js": "catalog:",
"zod": "catalog:"
},
"peerDependencies": {
"@goat-sdk/core": "workspace:*"
},
"homepage": "https://ohmygoat.dev",
"repository": {
"type": "git",
"url": "git+https://github.com/goat-sdk/goat.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/goat-sdk/goat/issues"
},
"keywords": ["ai", "agents", "web3"]
}
1 change: 1 addition & 0 deletions typescript/packages/plugins/solana-magiceden/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./plugin";
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { SolanaWalletClient } from "@goat-sdk/core";
import { type Connection, VersionedTransaction } from "@solana/web3.js";
import type { z } from "zod";
import type { getBuyListingTransactionResponseSchema, getNftInfoParametersSchema } from "../parameters";
import { decompileVersionedTransactionToInstructions } from "../utils/decompileVersionedTransactionToInstructions";
import { getNftListings } from "./getNftListings";

export async function buyListing(
apiKey: string | undefined,
connection: Connection,
walletClient: SolanaWalletClient,
parameters: z.infer<typeof getNftInfoParametersSchema>,
) {
const nftInfo = await getNftListings(apiKey, parameters);

const queryParams = new URLSearchParams({
buyer: walletClient.getAddress(),
seller: nftInfo.seller,
tokenMint: parameters.mintHash,
tokenATA: nftInfo.pdaAddress,
price: nftInfo.price.toString(),
});

let data: z.infer<typeof getBuyListingTransactionResponseSchema>;
try {
const response = await fetch(
`https://api-mainnet.magiceden.dev/v2/instructions/buy_now?${queryParams.toString()}`,
{
headers: {
"Content-Type": "application/json",
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
},
},
);

data = (await response.json()) as z.infer<typeof getBuyListingTransactionResponseSchema>;
} catch (error) {
throw new Error(`Failed to get buy listing transaction: ${error}`);
}

const versionedTransaction = VersionedTransaction.deserialize(Buffer.from(data.v0.tx.data));
const instructions = await decompileVersionedTransactionToInstructions(connection, versionedTransaction);
const lookupTableAddresses = versionedTransaction.message.addressTableLookups.map((lookup) => lookup.accountKey);

return { versionedTransaction, instructions, lookupTableAddresses };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { z } from "zod";
import type { getNftInfoParametersSchema, getNftInfoResponseSchema } from "../parameters";

export async function getNftListings(
apiKey: string | undefined,
parameters: z.infer<typeof getNftInfoParametersSchema>,
) {
let nftInfo: z.infer<typeof getNftInfoResponseSchema>;
try {
const response = await fetch(
`https://api-mainnet.magiceden.dev/v2/tokens/${parameters.mintHash}/listings
`,
{
headers: {
"Content-Type": "application/json",
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
},
},
);

nftInfo = (await response.json()) as z.infer<typeof getNftInfoResponseSchema>;
} catch (error) {
throw new Error(`Failed to get NFT listings: ${error}`);
}

return nftInfo[0];
}
45 changes: 45 additions & 0 deletions typescript/packages/plugins/solana-magiceden/src/parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { z } from "zod";

export const getNftInfoParametersSchema = z.object({
mintHash: z.string(),
});

export const getNftInfoResponseSchema = z.array(
z.object({
pdaAddress: z.string(),
auctionHouse: z.string().optional(),
tokenAddress: z.string().optional(),
tokenMint: z.string().optional(),
seller: z.string(),
sellerReferral: z.string().optional(),
tokenSize: z.number().optional(),
price: z.number(),
priceInfo: z
.object({
solPrice: z.object({
rawAmount: z.string(),
address: z.string(),
decimals: z.number(),
}),
})
.optional(),
rarity: z.any().optional(),
extra: z.any().optional(),
expiry: z.number().optional(),
token: z.any().optional(),
listingSource: z.string().optional(),
}),
);

export const getBuyListingTransactionResponseSchema = z.object({
v0: z.object({
tx: z.object({
type: z.string(),
data: z.array(z.number()),
}),
txSigned: z.object({
type: z.string(),
data: z.array(z.number()),
}),
}),
});
12 changes: 12 additions & 0 deletions typescript/packages/plugins/solana-magiceden/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Plugin, SolanaWalletClient } from "@goat-sdk/core";
import type { Connection } from "@solana/web3.js";
import { getTools } from "./tools";

export function solanaMagicEden(params: { connection: Connection; apiKey: string }): Plugin<SolanaWalletClient> {
return {
name: "solana-magiceden",
supportsSmartWallets: () => false,
supportsChain: (chain) => chain.type === "solana",
getTools: async () => getTools(params),
};
}
28 changes: 28 additions & 0 deletions typescript/packages/plugins/solana-magiceden/src/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { SolanaWalletClient } from "@goat-sdk/core";

import type { DeferredTool } from "@goat-sdk/core";
import type { Connection } from "@solana/web3.js";
import { buyListing } from "./methods/buyListing";
import { getNftListings } from "./methods/getNftListings";
import { getNftInfoParametersSchema } from "./parameters";

export function getTools({
apiKey,
connection,
}: { apiKey?: string; connection: Connection }): DeferredTool<SolanaWalletClient>[] {
const getNftListingsTool: DeferredTool<SolanaWalletClient> = {
name: "get_nft_listings",
description: "Gets information about a Solana NFT, from the Magic Eden API",
parameters: getNftInfoParametersSchema,
method: async (walletClient, parameters) => getNftListings(apiKey, parameters),
};

const buyListingTool: DeferredTool<SolanaWalletClient> = {
name: "get_buy_listing_transaction",
description: "Gets a transaction to buy a Solana NFT from a listing from the Magic Eden API",
parameters: getNftInfoParametersSchema,
method: async (walletClient, parameters) => buyListing(apiKey, connection, walletClient, parameters),
};

return [getNftListingsTool, buyListingTool];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type DecompileArgs, TransactionMessage } from "@solana/web3.js";

import type { Connection, VersionedTransaction } from "@solana/web3.js";

export async function decompileVersionedTransactionToInstructions(
connection: Connection,
versionedTransaction: VersionedTransaction,
) {
const lookupTableAddresses = versionedTransaction.message.addressTableLookups.map((lookup) => lookup.accountKey);
const addressLookupTableAccounts = await Promise.all(
lookupTableAddresses.map((address) =>
connection.getAddressLookupTable(address).then((lookupTable) => lookupTable.value),
),
);
const nonNullAddressLookupTableAccounts = addressLookupTableAccounts.filter((lookupTable) => lookupTable != null);
const decompileArgs: DecompileArgs = {
addressLookupTableAccounts: nonNullAddressLookupTableAccounts,
};
return TransactionMessage.decompile(versionedTransaction.message, decompileArgs).instructions;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { type Connection, VersionedTransaction } from "@solana/web3.js";
import type { z } from "zod";
import type { getBuyListingTransactionResponseSchema } from "../parameters";
import { decompileVersionedTransactionToInstructions } from "./decompileVersionedTransactionToInstructions";

export async function deserializeTxResponseToInstructions(
connection: Connection,
txResponse: z.infer<typeof getBuyListingTransactionResponseSchema>,
) {
const txV0 = txResponse.v0.txSigned;
if (txV0 == null) {
throw new Error("No transaction in response");
}
const versionedTransaction = VersionedTransaction.deserialize(Buffer.from(txV0.data));
const instructions = await decompileVersionedTransactionToInstructions(connection, versionedTransaction);
return { versionedTransaction, instructions };
}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/solana-magiceden/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/solana-magiceden/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from "tsup";
import { treeShakableConfig } from "../../../tsup.config.base";

export default defineConfig({
...treeShakableConfig,
});

0 comments on commit 948ba70

Please sign in to comment.