Skip to content

Commit 1547668

Browse files
bryan-eastwood3youssefea
authored andcommitted
Plugin: Solana cNFT Transfers (goat-sdk#13)
* changes * changes * changes * changes * changes * fix formatter * more diff fixing * changes * changes * temp example * changes * fix build * changes * fix * changes
1 parent 7703f48 commit 1547668

File tree

11 files changed

+5835
-82
lines changed

11 files changed

+5835
-82
lines changed

biome.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
typescript/biome.json

typescript/examples/vercel-ai/crossmint-solana-custodial-wallets/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"create-wallet": "ts-node create-custodial-wallet.ts",
88
"test": "vitest run --passWithNoTests"
99
},
10+
"main": "index.ts",
1011
"author": "",
1112
"license": "MIT",
1213
"dependencies": {
@@ -15,6 +16,7 @@
1516
"@goat-sdk/core": "workspace:*",
1617
"@goat-sdk/plugin-erc20": "workspace:*",
1718
"@goat-sdk/crossmint": "workspace:*",
19+
"@goat-sdk/plugin-solana-nfts": "workspace:*",
1820
"@solana/web3.js": "1.95.8",
1921
"ai": "^4.0.3",
2022
"dotenv": "^16.4.5",

typescript/packages/core/package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
"name": "@goat-sdk/core",
33
"version": "0.3.10",
44
"sideEffects": false,
5-
"files": ["dist/**/*", "README.md", "package.json"],
5+
"files": [
6+
"dist/**/*",
7+
"README.md",
8+
"package.json"
9+
],
610
"scripts": {
711
"build": "tsup",
812
"clean": "rm -rf dist",
@@ -26,5 +30,9 @@
2630
"bugs": {
2731
"url": "https://github.com/goat-sdk/goat/issues"
2832
},
29-
"keywords": ["ai", "agents", "web3"]
33+
"keywords": [
34+
"ai",
35+
"agents",
36+
"web3"
37+
]
3038
}

typescript/packages/core/src/wallets/solana.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TransactionInstruction } from "@solana/web3.js";
1+
import type { Connection, TransactionInstruction } from "@solana/web3.js";
22
import type { WalletClient } from "./core";
33

44
export function isSolanaWalletClient(wallet: WalletClient): wallet is SolanaWalletClient {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@goat-sdk/plugin-solana-nfts",
3+
"version": "0.1.0",
4+
"files": ["dist/**/*", "README.md", "package.json"],
5+
"scripts": {
6+
"build": "tsup",
7+
"clean": "rm -rf dist",
8+
"test": "vitest run --passWithNoTests"
9+
},
10+
"sideEffects": false,
11+
"main": "./dist/index.js",
12+
"module": "./dist/index.mjs",
13+
"types": "./dist/index.d.ts",
14+
"dependencies": {
15+
"@goat-sdk/core": "workspace:*",
16+
"@metaplex-foundation/digital-asset-standard-api": "1.0.0",
17+
"@metaplex-foundation/mpl-bubblegum": "3.1.0",
18+
"@metaplex-foundation/umi": "^0.9.2",
19+
"@metaplex-foundation/umi-bundle-defaults": "0.9.2",
20+
"@metaplex-foundation/umi-web3js-adapters": "0.8.10",
21+
"@solana/web3.js": "1.95.8",
22+
"viem": "^2.21.49",
23+
"zod": "^3.23.8"
24+
},
25+
"peerDependencies": {
26+
"@goat-sdk/core": "workspace:*",
27+
"viem": "^2.21.49"
28+
},
29+
"homepage": "https://ohmygoat.dev",
30+
"repository": {
31+
"type": "git",
32+
"url": "git+https://github.com/goat-sdk/goat.git"
33+
},
34+
"license": "MIT",
35+
"bugs": {
36+
"url": "https://github.com/goat-sdk/goat/issues"
37+
},
38+
"keywords": ["ai", "agents", "web3"]
39+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./nfts";
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { type Connection, PublicKey } from "@solana/web3.js";
2+
import { z } from "zod";
3+
import type { Plugin, SolanaWalletClient } from "@goat-sdk/core";
4+
import { getAssetWithProof, mplBubblegum, transfer } from "@metaplex-foundation/mpl-bubblegum";
5+
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
6+
import { fromWeb3JsPublicKey, toWeb3JsInstruction } from "@metaplex-foundation/umi-web3js-adapters";
7+
8+
export function nfts(connection: Connection): Plugin<SolanaWalletClient> {
9+
return {
10+
name: "nfts",
11+
supportsSmartWallets: () => false,
12+
supportsChain: (chain) => chain.type === "solana",
13+
getTools: async () => {
14+
return [
15+
{
16+
name: "transfer_nft",
17+
description: "This {{tool}} sends an NFT from your wallet to an address on a Solana chain.",
18+
parameters: transferNFTParametersSchema,
19+
method: transferNFTMethod(connection),
20+
},
21+
];
22+
},
23+
};
24+
}
25+
26+
const transferNFTParametersSchema = z.object({
27+
recipientAddress: z.string().describe("The address to send the NFT to"),
28+
assetId: z.string().describe("The asset ID of the NFT to send"),
29+
});
30+
31+
const transferNFTMethod =
32+
(connection: Connection) =>
33+
async (
34+
walletClient: SolanaWalletClient,
35+
parameters: z.infer<typeof transferNFTParametersSchema>,
36+
): Promise<string> => {
37+
const { recipientAddress, assetId } = parameters;
38+
const umi = createUmi(connection);
39+
umi.use(mplBubblegum());
40+
const assetWithProof = await getAssetWithProof(umi, fromWeb3JsPublicKey(new PublicKey(assetId)));
41+
const instructions = transfer(umi, {
42+
...assetWithProof,
43+
leafOwner: fromWeb3JsPublicKey(new PublicKey(walletClient.getAddress())),
44+
newLeafOwner: fromWeb3JsPublicKey(new PublicKey(recipientAddress)),
45+
}).getInstructions();
46+
47+
const result = await walletClient.sendTransaction({
48+
instructions: instructions.map(toWeb3JsInstruction),
49+
});
50+
51+
return result.hash;
52+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
"extends": "../../../tsconfig.base.json",
4+
"include": ["src/**/*"],
5+
"exclude": ["node_modules", "dist"]
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { defineConfig } from "tsup";
2+
import { treeShakableConfig } from "../../../tsup.config.base";
3+
4+
export default defineConfig({
5+
...treeShakableConfig,
6+
});

typescript/packages/wallets/crossmint/src/custodial.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function custodialFactory(apiKey: string) {
4242
return async function custodial(params: CustodialOptions): Promise<SolanaWalletClient> {
4343
const { connection, env = "staging" } = params;
4444

45-
const locator = `${getLocator(params)}:solana-custodial-wallet`;
45+
const locator = `${getLocator(params)}`;
4646
const client = createCrossmintAPI(apiKey, env);
4747
const { address } = await client.getWallet(locator);
4848

@@ -83,11 +83,10 @@ export function custodialFactory(apiKey: string) {
8383
}
8484
},
8585
async sendTransaction({ instructions }: SolanaTransaction) {
86-
const latestBlockhash = await connection.getLatestBlockhash("confirmed");
86+
const publicKey = new PublicKey("11111111111111111111111111111112");
8787
const message = new TransactionMessage({
88-
// Placeholder payer key since Crossmint will override it
89-
payerKey: new PublicKey("placeholder"),
90-
recentBlockhash: latestBlockhash.blockhash,
88+
payerKey: publicKey,
89+
recentBlockhash: "11111111111111111111111111111111",
9190
instructions,
9291
}).compileToV0Message();
9392
const transaction = new VersionedTransaction(message);

0 commit comments

Comments
 (0)