Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin: Solana cNFT Transfers #13

Merged
merged 17 commits into from
Dec 8, 2024
15 changes: 13 additions & 2 deletions typescript/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@goat-sdk/core",
"version": "0.3.10",
"sideEffects": false,
"files": ["dist/**/*", "README.md", "package.json"],
"files": [
"dist/**/*",
"README.md",
"package.json"
],
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
Expand All @@ -20,6 +24,9 @@
"vitest": "^2.1.5"
},
"dependencies": {
"@metaplex-foundation/mpl-bubblegum": "^4.2.1",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
"@metaplex-foundation/umi-web3js-adapters": "^0.9.2",
"@solana/web3.js": "1.95.8",
"abitype": "^1.0.6",
"viem": "^2.21.49",
Expand All @@ -40,6 +47,10 @@
"bugs": {
"url": "https://github.com/goat-sdk/goat/issues"
},
"keywords": ["ai", "agents", "web3"],
"keywords": [
"ai",
"agents",
"web3"
],
"packageManager": "[email protected]"
}
64 changes: 64 additions & 0 deletions typescript/packages/core/src/plugins/send-nft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Connection, PublicKey } from "@solana/web3.js";
import { parseUnits } from "viem";
import type { SolanaWalletClient } from "../wallets";
import type { Plugin } from "./plugins";
import { z } from "zod";
import { SystemProgram } from "@solana/web3.js";
import {
mplBubblegum,
getAssetWithProof,
transfer,
} from "@metaplex-foundation/mpl-bubblegum";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import {
fromWeb3JsPublicKey,
toWeb3JsInstruction,
} from "@metaplex-foundation/umi-web3js-adapters";

export function transferNFT(): Plugin<SolanaWalletClient> {
return {
name: "transfer_nft",
supportsSmartWallets: () => true,
supportsChain: (chain) => chain.type === "solana",
getTools: async () => {
return [
{
name: "transfer_nft",
description:
"This {{tool}} sends an NFT from your wallet to an address on a Solana chain.",
parameters: transferNFTParametersSchema,
method: transferNFTMethod,
},
];
},
};
}

const transferNFTParametersSchema = z.object({
recipientAddress: z.string().describe("The address to send the NFT to"),
assetId: z.string().describe("The asset ID of the NFT to send"),
});

async function transferNFTMethod(
walletClient: SolanaWalletClient,
parameters: z.infer<typeof transferNFTParametersSchema>,
): Promise<string> {
const { recipientAddress, assetId } = parameters;
const umi = createUmi(walletClient.connection);
umi.use(mplBubblegum());
const assetWithProof = await getAssetWithProof(
umi,
fromWeb3JsPublicKey(new PublicKey(assetId)),
);
const instructions = transfer(umi, {
...assetWithProof,
leafOwner: fromWeb3JsPublicKey(new PublicKey(walletClient.getAddress())),
newLeafOwner: fromWeb3JsPublicKey(new PublicKey(recipientAddress)),
}).getInstructions();

const result = await walletClient.sendTransaction({
instructions: instructions.map(toWeb3JsInstruction),
});

return result.hash;
}
3 changes: 2 additions & 1 deletion typescript/packages/core/src/wallets/solana.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TransactionInstruction } from "@solana/web3.js";
import type { Connection, TransactionInstruction } from "@solana/web3.js";
import type { WalletClient } from "./core";

export function isSolanaWalletClient(
Expand Down Expand Up @@ -28,4 +28,5 @@ export interface SolanaWalletClient extends WalletClient {
transaction: SolanaTransaction,
) => Promise<SolanaTransactionResult>;
read: (request: SolanaReadRequest) => Promise<SolanaReadResult>;
connection: Connection;
}
15 changes: 13 additions & 2 deletions typescript/packages/wallets/crossmint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@goat-sdk/crossmint",
"version": "0.1.5",
"sideEffects": false,
"files": ["dist/**/*", "README.md", "package.json"],
"files": [
"dist/**/*",
"README.md",
"package.json"
],
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
Expand All @@ -21,6 +25,9 @@
},
"dependencies": {
"@goat-sdk/core": "workspace:*",
"@metaplex-foundation/mpl-bubblegum": "^4.2.1",
"@metaplex-foundation/umi": "^0.9.2",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
"@solana/web3.js": "1.95.8",
"abitype": "^1.0.6",
"bs58": "^6.0.0",
Expand All @@ -45,6 +52,10 @@
"bugs": {
"url": "https://github.com/goat-sdk/goat/issues"
},
"keywords": ["ai", "agents", "web3"],
"keywords": [
"ai",
"agents",
"web3"
],
"packageManager": "[email protected]"
}
10 changes: 6 additions & 4 deletions typescript/packages/wallets/crossmint/src/custodial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type {
} from "@goat-sdk/core";
import {
type Connection,
PublicKey,
TransactionMessage,
VersionedTransaction,
PublicKey,
} from "@solana/web3.js";
import bs58 from "bs58";
import { createCrossmintAPI } from "./api";
Expand Down Expand Up @@ -53,11 +53,12 @@ export function custodialFactory(apiKey: string) {
): Promise<SolanaWalletClient> {
const { connection, env = "staging" } = params;

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

return {
connection,
getAddress() {
return address;
},
Expand Down Expand Up @@ -102,9 +103,10 @@ export function custodialFactory(apiKey: string) {
async sendTransaction({ instructions }: SolanaTransaction) {
const latestBlockhash =
await connection.getLatestBlockhash("confirmed");
// see https://linear.app/crossmint/issue/WAL-3436/solana-transactions-get-stuck-when-sending-the-dummy-value
const publicKey = new PublicKey("11111111111111111111111111111112");
const message = new TransactionMessage({
// Placeholder payer key since Crossmint will override it
payerKey: new PublicKey("placeholder"),
payerKey: publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions,
}).compileToV0Message();
Expand Down
1 change: 1 addition & 0 deletions typescript/packages/wallets/solana/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function solana({
keypair,
}: SolanaWalletOptions): SolanaWalletClient {
return {
connection,
getAddress: () => keypair.publicKey.toBase58(),
getChain() {
return {
Expand Down
Loading