Skip to content

Commit

Permalink
0.1.0(ts): deploy nft (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
stat authored and John-peterson-coinbase committed Feb 1, 2025
1 parent c5c5073 commit 1eb5cc1
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CdpWalletProvider } from "../../wallet_providers";
import { CdpActionProvider } from "./cdpActionProvider";
import { AddressReputationSchema, RequestFaucetFundsSchema } from "./schemas";
import { AddressReputationSchema, DeployNftSchema, RequestFaucetFundsSchema } from "./schemas";
import { SmartContract } from "@coinbase/coinbase-sdk";

// Mock the entire module
Expand Down Expand Up @@ -34,6 +34,28 @@ describe("CDP Action Provider Input Schemas", () => {
});
});

describe("Deploy NFT Schema", () => {
it("should successfully parse valid input", () => {
const validInput = {
baseURI: "https://www.test.xyz/metadata/",
name: "Test Token",
symbol: "TEST",
};

const result = DeployNftSchema.safeParse(validInput);

expect(result.success).toBe(true);
expect(result.data).toEqual(validInput);
});

it("should fail parsing empty input", () => {
const emptyInput = {};
const result = DeployNftSchema.safeParse(emptyInput);

expect(result.success).toBe(false);
});
});

describe("Request Faucet Funds Schema", () => {
it("should successfully parse with optional assetId", () => {
const validInput = {
Expand Down Expand Up @@ -120,6 +142,65 @@ describe("CDP Action Provider", () => {
});
});

describe("deployNft", () => {
let mockWallet: jest.Mocked<CdpWalletProvider>;
const MOCK_NFT_BASE_URI = "https://www.test.xyz/metadata/";
const MOCK_NFT_NAME = "Test Token";
const MOCK_NFT_SYMBOL = "TEST";
const CONTRACT_ADDRESS = "0x123456789abcdef";
const NETWORK_ID = "base-sepolia";
const TRANSACTION_HASH = "0xghijkl987654321";
const TRANSACTION_LINK = `https://etherscan.io/tx/${TRANSACTION_HASH}`;

beforeEach(() => {
mockWallet = {
deployNFT: jest.fn().mockResolvedValue({
wait: jest.fn().mockResolvedValue({
getContractAddress: jest.fn().mockReturnValue(CONTRACT_ADDRESS),
getTransaction: jest.fn().mockReturnValue({
getTransactionHash: jest.fn().mockReturnValue(TRANSACTION_HASH),
getTransactionLink: jest.fn().mockReturnValue(TRANSACTION_LINK),
}),
}),
}),
getNetwork: jest.fn().mockReturnValue({ networkId: NETWORK_ID }),
} as unknown as jest.Mocked<CdpWalletProvider>;
});

it("should successfully deploy an NFT", async () => {
const args = {
name: MOCK_NFT_NAME,
symbol: MOCK_NFT_SYMBOL,
baseURI: MOCK_NFT_BASE_URI,
};

const result = await actionProvider.deployNFT(mockWallet, args);

expect(mockWallet.deployNFT).toHaveBeenCalledWith(args);
expect(result).toContain(`Deployed NFT Collection ${MOCK_NFT_NAME}:`);
expect(result).toContain(`- to address ${CONTRACT_ADDRESS}`);
expect(result).toContain(`- on network ${NETWORK_ID}`);
expect(result).toContain(`Transaction hash: ${TRANSACTION_HASH}`);
expect(result).toContain(`Transaction link: ${TRANSACTION_LINK}`);
});

it("should handle deployment errors", async () => {
const args = {
name: MOCK_NFT_NAME,
symbol: MOCK_NFT_SYMBOL,
baseURI: MOCK_NFT_BASE_URI,
};

const error = new Error("An error has occurred");
mockWallet.deployNFT.mockRejectedValue(error);

const result = await actionProvider.deployNFT(mockWallet, args);

expect(mockWallet.deployNFT).toHaveBeenCalledWith(args);
expect(result).toBe(`Error deploying NFT: ${error}`);
});
});

describe("deployToken", () => {
beforeEach(() => {
mockWallet = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SolidityVersions } from "./constants";
import {
AddressReputationSchema,
DeployContractSchema,
DeployNftSchema,
DeployTokenSchema,
RequestFaucetFundsSchema,
} from "./schemas";
Expand Down Expand Up @@ -101,6 +102,48 @@ map where the key is the arg name and the value is the arg value. Encode uint/in
}
}

/**
* Deploys an NFT (ERC-721) token collection onchain from the wallet.
*
* @param walletProvider - The wallet provider to deploy the NFT from.
* @param args - The input arguments for the action.
* @returns A message containing the NFT token deployment details.
*/
@CreateAction({
name: "deploy_nft",
description: `This tool will deploy an NFT (ERC-721) contract onchain from the wallet.
It takes the name of the NFT collection, the symbol of the NFT collection, and the base URI for the token metadata as inputs.`,
schema: DeployNftSchema,
})
async deployNFT(
walletProvider: CdpWalletProvider,
args: z.infer<typeof DeployNftSchema>,
): Promise<string> {
try {
const nftContract = await walletProvider.deployNFT({
name: args.name,
symbol: args.symbol,
baseURI: args.baseURI,
});

const result = await nftContract.wait();

const transaction = result.getTransaction()!;
const networkId = walletProvider.getNetwork().networkId;
const contractAddress = result.getContractAddress();

return [
`Deployed NFT Collection ${args.name}:`,
`- to address ${contractAddress}`,
`- on network ${networkId}.`,
`Transaction hash: ${transaction.getTransactionHash()}`,
`Transaction link: ${transaction.getTransactionLink()}`,
].join("\n");
} catch (error) {
return `Error deploying NFT: ${error}`;
}
}

/**
* Deploys a token.
*
Expand Down
12 changes: 12 additions & 0 deletions cdp-agentkit-core/typescript/src/action_providers/cdp/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ export const DeployContractSchema = z
.strip()
.describe("Instructions for deploying an arbitrary contract");

/**
* Input schema for deploy NFT action
*/
export const DeployNftSchema = z
.object({
name: z.string().describe("The name of the NFT collection"),
symbol: z.string().describe("The symbol of the NFT collection"),
baseURI: z.string().describe("The base URI for the token metadata"),
})
.strip()
.describe("Instructions for deploying an NFT collection");

/**
* Input schema for deploy token action.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,28 @@ export class CdpWalletProvider extends EvmWalletProvider {

return this.#cdpWallet.deployContract(options);
}

/**
* Deploys a new NFT (ERC-721) smart contract.
*
* @param options - Configuration options for the NFT contract deployment
* @param options.name - The name of the collection
* @param options.symbol - The token symbol for the collection
* @param options.baseURI - The base URI for token metadata.
*
* @returns A Promise that resolves to the deployed SmartContract instance
* @throws Error if the wallet is not properly initialized
* @throws Error if the deployment fails for any reason (network issues, insufficient funds, etc.)
*/
async deployNFT(options: {
name: string;
symbol: string;
baseURI: string;
}): Promise<SmartContract> {
if (!this.#cdpWallet) {
throw new Error("Wallet not initialized");
}

return this.#cdpWallet.deployNFT(options);
}
}

0 comments on commit 1eb5cc1

Please sign in to comment.