Skip to content

Commit

Permalink
Merge pull request #1011 from xwxtwd/feat/add_plugin-nft-generator
Browse files Browse the repository at this point in the history
feat: Add plugin-nft-generation: create Solana NFT collections.
  • Loading branch information
lalalune authored Dec 14, 2024
2 parents 7a12096 + 9325eb3 commit 0ca14a3
Show file tree
Hide file tree
Showing 18 changed files with 1,047 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ EVM_PROVIDER_URL=
# Solana
SOLANA_PRIVATE_KEY=
SOLANA_PUBLIC_KEY=
SOLANA_CLUSTER= # Default: devnet. Solana Cluster: 'devnet' | 'testnet' | 'mainnet-beta'
SOLANA_ADMIN_PRIVATE_KEY= # This wallet is used to verify NFTs
SOLANA_ADMIN_PUBLIC_KEY= # This wallet is used to verify NFTs
SOLANA_VERIFY_TOKEN= # Authentication token for calling the verification API

# Fallback Wallet Configuration (deprecated)
WALLET_PRIVATE_KEY=
Expand Down
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@ai16z/plugin-goat": "workspace:*",
"@ai16z/plugin-icp": "workspace:*",
"@ai16z/plugin-image-generation": "workspace:*",
"@ai16z/plugin-nft-generation": "workspace:*",
"@ai16z/plugin-node": "workspace:*",
"@ai16z/plugin-solana": "workspace:*",
"@ai16z/plugin-starknet": "workspace:*",
Expand Down
9 changes: 9 additions & 0 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { solanaPlugin } from "@ai16z/plugin-solana";
import { TEEMode, teePlugin } from "@ai16z/plugin-tee";
import { tonPlugin } from "@ai16z/plugin-ton";
import { zksyncEraPlugin } from "@ai16z/plugin-zksync-era";
import { nftGenerationPlugin, createNFTApiRouter } from "@ai16z/plugin-nft-generation";
import Database from "better-sqlite3";
import fs from "fs";
import path from "path";
Expand Down Expand Up @@ -493,6 +494,14 @@ export async function createAgent(
getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))
? evmPlugin
: null,
(getSecret(character, "SOLANA_PUBLIC_KEY") ||
(getSecret(character, "WALLET_PUBLIC_KEY") &&
!getSecret(character, "WALLET_PUBLIC_KEY")?.startsWith("0x"))) &&
getSecret(character, "SOLANA_ADMIN_PUBLIC_KEY") &&
getSecret(character, "SOLANA_PRIVATE_KEY") &&
getSecret(character, "SOLANA_ADMIN_PRIVATE_KEY")
? nftGenerationPlugin
: null,
getSecret(character, "ZEROG_PRIVATE_KEY") ? zgPlugin : null,
getSecret(character, "COINBASE_COMMERCE_KEY")
? coinbaseCommercePlugin
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const CharacterSchema = z.object({
nicknames: z.array(z.string()).optional(),
})
.optional(),
nft: z
.object({
prompt: z.string().optional(),
})
.optional(),
});

// Type inference
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,10 @@ export type Character = {
bio: string;
nicknames?: string[];
};
/** Optional NFT prompt */
nft?: {
prompt: string;
}
};

/**
Expand Down Expand Up @@ -1163,6 +1167,7 @@ export interface IPdfService extends Service {
export interface IAwsS3Service extends Service {
uploadFile(
imagePath: string,
subDirectory: string,
useSignedUrl: boolean,
expiresIn: number
): Promise<{
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-nft-generation/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*

!dist/**
!package.json
!readme.md
!tsup.config.ts
3 changes: 3 additions & 0 deletions packages/plugin-nft-generation/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
30 changes: 30 additions & 0 deletions packages/plugin-nft-generation/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@ai16z/plugin-nft-generation",
"version": "0.1.5-alpha.5",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@ai16z/eliza": "workspace:*",
"@ai16z/plugin-image-generation": "workspace:*",
"@ai16z/plugin-node": "workspace:*",
"@metaplex-foundation/mpl-token-metadata": "^3.3.0",
"@metaplex-foundation/mpl-toolbox": "^0.9.4",
"@metaplex-foundation/umi": "^0.9.2",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
"@solana-developers/helpers": "^2.5.6",
"@solana/web3.js": "1.95.5",
"bs58": "6.0.0",
"express": "4.21.1",
"node-cache": "5.1.2",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint . --fix"
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
165 changes: 165 additions & 0 deletions packages/plugin-nft-generation/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import express from "express";

import { AgentRuntime } from "@ai16z/eliza";
import { createCollection } from "./handlers/createCollection.ts";
import { createNFT, createNFTMetadata } from "./handlers/createNFT.ts";
import { verifyNFT } from "./handlers/verifyNFT.ts";

export function createNFTApiRouter(agents: Map<string, AgentRuntime>) {
const router = express.Router();

router.post(
"/api/nft-generation/create-collection",
async (req: express.Request, res: express.Response) => {
const agentId = req.body.agentId;
const fee = req.body.fee || 0;
const runtime = agents.get(agentId);
if (!runtime) {
res.status(404).send("Agent not found");
return;
}
try {
const collectionAddressRes = await createCollection({
runtime,
collectionName: runtime.character.name,
fee
});

res.json({
success: true,
data: collectionAddressRes,
});
} catch (e: any) {
console.log(e);
res.json({
success: false,
data: JSON.stringify(e),
});
}
}
);


router.post(
"/api/nft-generation/create-nft-metadata",
async (req: express.Request, res: express.Response) => {
const agentId = req.body.agentId;
const collectionName = req.body.collectionName;
const collectionAddress = req.body.collectionAddress;
const collectionAdminPublicKey = req.body.collectionAdminPublicKey;
const collectionFee = req.body.collectionFee;
const tokenId = req.body.tokenId;
const runtime = agents.get(agentId);
if (!runtime) {
res.status(404).send("Agent not found");
return;
}

try {
const nftInfo = await createNFTMetadata({
runtime,
collectionName,
collectionAdminPublicKey,
collectionFee,
tokenId,
});

res.json({
success: true,
data: {
...nftInfo,
collectionAddress,
},
});
} catch (e: any) {
console.log(e);
res.json({
success: false,
data: JSON.stringify(e),
});
}
}
);

router.post(
"/api/nft-generation/create-nft",
async (req: express.Request, res: express.Response) => {
const agentId = req.body.agentId;
const collectionName = req.body.collectionName;
const collectionAddress = req.body.collectionAddress;
const collectionAdminPublicKey = req.body.collectionAdminPublicKey;
const collectionFee = req.body.collectionFee;
const tokenId = req.body.tokenId;
const runtime = agents.get(agentId);
if (!runtime) {
res.status(404).send("Agent not found");
return;
}

try {
const nftRes = await createNFT({
runtime,
collectionName,
collectionAddress,
collectionAdminPublicKey,
collectionFee,
tokenId,
});

res.json({
success: true,
data: nftRes,
});
} catch (e: any) {
console.log(e);
res.json({
success: false,
data: JSON.stringify(e),
});
}
}
);


router.post(
"/api/nft-generation/verify-nft",
async (req: express.Request, res: express.Response) => {
const agentId = req.body.agentId;
const collectionAddress = req.body.collectionAddress;
const NFTAddress = req.body.nftAddress;
const token = req.body.token;

const runtime = agents.get(agentId);
if (!runtime) {
res.status(404).send("Agent not found");
return;
}
const verifyToken = runtime.getSetting('SOLANA_VERIFY_TOKEN')
if (token !== verifyToken) {
res.status(401).send(" Access denied for translation");
return;
}
try {
const {success} = await verifyNFT({
runtime,
collectionAddress,
NFTAddress,
});

res.json({
success: true,
data: success ? 'verified' : 'unverified',
});
} catch (e: any) {
console.log(e);
res.json({
success: false,
data: JSON.stringify(e),
});
}
}
);


return router;
}
Loading

0 comments on commit 0ca14a3

Please sign in to comment.