├── packages └── sdk │ ├── src │ ├── utils │ │ ├── index.ts │ │ └── types.ts │ ├── helpers │ │ ├── index.ts │ │ └── metaplex.ts │ ├── lib │ │ ├── nft │ │ │ ├── index.ts │ │ │ └── airdrop.ts │ │ ├── index.ts │ │ └── candy-machine │ │ │ ├── index.ts │ │ │ ├── mint.ts │ │ │ └── gasless.ts │ ├── constants │ │ ├── index.ts │ │ ├── urls.ts │ │ └── addresses.ts │ ├── modules │ │ ├── index.ts │ │ ├── nft.ts │ │ └── candy-machine.ts │ ├── types │ │ ├── index.ts │ │ ├── candy-machine.ts │ │ ├── gasless.ts │ │ └── nft.ts │ └── index.ts │ ├── tests │ ├── fixtures │ │ ├── index.ts │ │ └── constants.ts │ ├── nft.test.ts │ └── candy-machine.test.ts │ ├── .npmignore │ ├── tsup.config.ts │ ├── jest.config.json │ ├── tsconfig.json │ ├── package.json │ └── README.md ├── pnpm-workspace.yaml ├── .editorconfig ├── .gitignore ├── turbo.json ├── package.json ├── LICENSE └── README.md /packages/sdk/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | -------------------------------------------------------------------------------- /packages/sdk/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./metaplex"; 2 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/nft/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./airdrop"; 2 | -------------------------------------------------------------------------------- /packages/sdk/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export type Option = T | null; 2 | -------------------------------------------------------------------------------- /packages/sdk/tests/fixtures/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constants"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./candy-machine"; 2 | export * from "./nft"; 3 | -------------------------------------------------------------------------------- /packages/sdk/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./addresses"; 2 | export * from "./urls"; 3 | -------------------------------------------------------------------------------- /packages/sdk/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./candy-machine"; 2 | export * from "./nft"; 3 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/candy-machine/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mint"; 2 | export * from "./gasless"; 3 | -------------------------------------------------------------------------------- /packages/sdk/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src 3 | tests 4 | .turbo 5 | tsconfig.json 6 | tsup.config.json 7 | .env -------------------------------------------------------------------------------- /packages/sdk/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./candy-machine"; 2 | export * from "./gasless"; 3 | export * from "./nft"; 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next/ 3 | out/ 4 | build 5 | dist 6 | .DS_Store 7 | *.pem 8 | .pnpm-debug.log* 9 | .env.local 10 | .env.development.local 11 | .env.test.local 12 | .env.production.local 13 | .env 14 | .turbo 15 | -------------------------------------------------------------------------------- /packages/sdk/src/modules/nft.ts: -------------------------------------------------------------------------------- 1 | import { nftAirdrop } from "../lib"; 2 | import { NftAirdropInput } from "../types"; 3 | 4 | export class Nft { 5 | async airdrop(options: NftAirdropInput) { 6 | return await nftAirdrop(options); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/sdk/src/constants/urls.ts: -------------------------------------------------------------------------------- 1 | const rpc = new Map([ 2 | ["devnet", "https://api.devnet.solana.com"], 3 | ["testnet", "https://api.testnet.solana.com"], 4 | ["mainnet-beta", "https://api.mainnet-beta.solana.com"], 5 | ]); 6 | 7 | export { rpc }; 8 | -------------------------------------------------------------------------------- /packages/sdk/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { Options } from "tsup"; 2 | 3 | export const tsup: Options = { 4 | target: "es2017", 5 | clean: true, 6 | dts: true, 7 | entry: ["src"], 8 | keepNames: true, 9 | minify: true, 10 | sourcemap: true, 11 | format: ["cjs"], 12 | }; 13 | -------------------------------------------------------------------------------- /packages/sdk/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.(t|j)sx?$": "ts-jest" 4 | }, 5 | "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 6 | "moduleFileExtensions": ["js", "json", "ts"], 7 | "testPathIgnorePatterns": ["node_modules", "dist", "tests/fixtures"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | import { CandyMachine, Nft } from "./modules"; 2 | 3 | export class CandyPay { 4 | public candyMachine: CandyMachine; 5 | public nft: Nft; 6 | 7 | constructor() { 8 | this.candyMachine = new CandyMachine(); 9 | this.nft = new Nft(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["dist/**", ".next/**"] 7 | }, 8 | "lint": { 9 | "outputs": [] 10 | }, 11 | "dev": { 12 | "cache": false 13 | }, 14 | "test": { 15 | "inputs": ["tests/**"], 16 | "cache": false 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/sdk/src/modules/candy-machine.ts: -------------------------------------------------------------------------------- 1 | import { candyMachineMint, gaslessCandyMachineMint } from "../lib"; 2 | import { CandyMachineMintInput, GaslessMintInput } from "../types"; 3 | 4 | export class CandyMachine { 5 | async mint(options: CandyMachineMintInput) { 6 | return await candyMachineMint(options); 7 | } 8 | 9 | async gasless(options: GaslessMintInput) { 10 | return await gaslessCandyMachineMint(options); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/sdk/tests/fixtures/constants.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | import dotenv from "dotenv"; 3 | import base58 from "bs58"; 4 | 5 | dotenv.config(); 6 | 7 | export const CANDY_MACHINE_ID = new anchor.web3.PublicKey( 8 | "GrVSy3ZRbuw5ACbwSEMsj9gULk9MW7QPK1TUYcP6nLM" 9 | ); 10 | export const PAYER = anchor.web3.Keypair.fromSecretKey( 11 | base58.decode(process.env.PAYER_SECRET_KEY!) 12 | ); 13 | export const USER = new anchor.web3.PublicKey( 14 | "2S9jKJEGKoVxR3xkEfFyGVrLwJj1H8xYjqtSP5LAX97x" 15 | ); 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sdk", 3 | "version": "0.0.0", 4 | "private": true, 5 | "workspaces": [ 6 | "apps/*", 7 | "packages/*" 8 | ], 9 | "scripts": { 10 | "build": "turbo run build", 11 | "dev": "turbo run dev --parallel", 12 | "build:sdk": "turbo run build --filter=./packages/sdk", 13 | "test:sdk": "turbo run test --filter=./packages/sdk" 14 | }, 15 | "devDependencies": { 16 | "prettier": "latest", 17 | "turbo": "latest" 18 | }, 19 | "engines": { 20 | "node": ">=14.0.0" 21 | }, 22 | "dependencies": {}, 23 | "packageManager": "pnpm@7.12.2" 24 | } 25 | -------------------------------------------------------------------------------- /packages/sdk/src/constants/addresses.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | 3 | export const CANDY_MACHINE_PROGRAM = new anchor.web3.PublicKey( 4 | "cndy3Z4yapfJBmL3ShUp5exZKqR3z33thTzeNMm2gRZ" 5 | ); 6 | 7 | export const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey( 8 | "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" 9 | ); 10 | 11 | export const CIVIC = new anchor.web3.PublicKey( 12 | "gatem74V238djXdzWnJf94Wo1DcnuGkfijbf3AuBhfs" 13 | ); 14 | 15 | export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = 16 | new anchor.web3.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); 17 | 18 | export const DEFAULT_TIMEOUT = 60000; 19 | -------------------------------------------------------------------------------- /packages/sdk/src/helpers/metaplex.ts: -------------------------------------------------------------------------------- 1 | import { 2 | guestIdentity, 3 | keypairIdentity, 4 | Metaplex, 5 | } from "@metaplex-foundation/js"; 6 | import * as anchor from "@project-serum/anchor"; 7 | 8 | interface InitMetaplexOptions { 9 | connection: anchor.web3.Connection; 10 | guest_mode: boolean; 11 | keypair?: anchor.web3.Keypair; 12 | guest_public_key?: anchor.web3.PublicKey; 13 | } 14 | 15 | export const initMetaplex = async (options: InitMetaplexOptions) => { 16 | const metaplex = new Metaplex(options.connection); 17 | return options.guest_mode === true 18 | ? metaplex.use(guestIdentity(options.guest_public_key)) 19 | : metaplex.use(keypairIdentity(options.keypair!)); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/sdk/tests/nft.test.ts: -------------------------------------------------------------------------------- 1 | import { CandyPay } from "../src"; 2 | 3 | import { PAYER, USER } from "./fixtures"; 4 | 5 | jest.setTimeout(100000); 6 | 7 | describe("NFT module", () => { 8 | it("Airdrop NFT", async () => { 9 | const sdk = new CandyPay(); 10 | const { signature } = await sdk.nft.airdrop({ 11 | metadata: { 12 | name: "DeGod", 13 | uri: "https://metadata.degods.com/g/4924.json", 14 | symbol: "DEGOD", 15 | collection: null, 16 | sellerFeeBasisPoints: 1000, 17 | creators: [ 18 | { 19 | address: PAYER.publicKey, 20 | share: 100, 21 | }, 22 | ], 23 | uses: null, 24 | }, 25 | network: "devnet", 26 | owner: USER, 27 | payer: PAYER, 28 | }); 29 | 30 | console.log(signature); 31 | 32 | expect(signature && typeof signature === "string").toBe(true); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "lib": ["dom", "es6", "es2017", "esnext.asynciterable"], 6 | "skipLibCheck": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "moduleResolution": "node", 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "strictFunctionTypes": true, 13 | "noImplicitThis": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noImplicitReturns": false, 17 | "noFallthroughCasesInSwitch": true, 18 | "allowSyntheticDefaultImports": true, 19 | "esModuleInterop": true, 20 | "experimentalDecorators": true, 21 | "resolveJsonModule": true, 22 | "baseUrl": ".", 23 | "declarationDir": "dist/", 24 | "declaration": true, 25 | "types": ["jest", "node"] 26 | }, 27 | "exclude": ["node_modules"], 28 | "include": ["src/**/*.ts", "tests/**/*.ts"] 29 | } 30 | -------------------------------------------------------------------------------- /packages/sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@candypay/sdk", 3 | "version": "1.1.1", 4 | "description": "A TypeScript SDK for effortlessly create NFT minting functions for Candy Machine v2 collections", 5 | "license": "MIT", 6 | "main": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "repository": "https://github.com/candypay/sdk", 9 | "author": "CandyPay Team ", 10 | "publishConfig": { 11 | "access": "public" 12 | }, 13 | "scripts": { 14 | "build": "tsup & tsc -p tsconfig.json", 15 | "test": "jest" 16 | }, 17 | "dependencies": { 18 | "@metaplex-foundation/js": "^0.17.4", 19 | "@metaplex-foundation/mpl-token-metadata": "^2.5.1", 20 | "@project-serum/anchor": "0.17.0" 21 | }, 22 | "devDependencies": { 23 | "@types/jest": "^29.1.2", 24 | "@types/node": "^18.0.3", 25 | "bs58": "^5.0.0", 26 | "dotenv": "^16.0.3", 27 | "jest": "^29.1.2", 28 | "ts-jest": "^29.0.3", 29 | "ts-node": "^10.8.2", 30 | "tsup": "^6.1.3", 31 | "typescript": "^4.7.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 CandyPay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/nft/airdrop.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | 3 | import { initMetaplex } from "../../helpers"; 4 | import { rpc } from "../../constants"; 5 | import { NftAirdropInput, NftAirdropOutput } from "../../types"; 6 | 7 | export const nftAirdrop = async ( 8 | options: NftAirdropInput 9 | ): Promise => { 10 | try { 11 | const connection = new anchor.web3.Connection( 12 | rpc.get(options.network) || options.rpc_url! 13 | ); 14 | const metaplex = await initMetaplex({ 15 | connection, 16 | guest_mode: false, 17 | keypair: options.payer, 18 | }); 19 | 20 | const transaction = await metaplex.nfts().create({ 21 | ...options.metadata, 22 | tokenOwner: options.owner, 23 | }); 24 | 25 | return { 26 | signature: transaction.response.signature, 27 | accounts: { 28 | mint_account: transaction.mintAddress, 29 | token_account: transaction.tokenAddress, 30 | metadata_account: transaction.metadataAddress, 31 | master_edition_account: transaction.masterEditionAddress, 32 | }, 33 | blockhash: transaction.response.blockhash, 34 | lastValidBlockHeight: transaction.response.lastValidBlockHeight, 35 | }; 36 | } catch (err) { 37 | throw new Error(err); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /packages/sdk/src/types/candy-machine.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | import { Signer } from "@metaplex-foundation/js"; 3 | 4 | export type CandyMachineNetworks = "mainnet-beta" | "devnet"; 5 | 6 | export interface CandyMachineMintInput { 7 | /** 8 | * The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 9 | */ 10 | network: CandyMachineNetworks; 11 | /** 12 | * The address of the Candy Machine from which the NFT would to be minted 13 | */ 14 | candyMachineId: anchor.web3.PublicKey; 15 | /** 16 | * The public key of the end-user 17 | */ 18 | user: anchor.web3.PublicKey; 19 | /** 20 | * Custom RPC URL 21 | */ 22 | rpc_url?: string; 23 | } 24 | 25 | export interface CandyMachineMintOutput { 26 | /** 27 | * The transaction object containing all the required instructions 28 | */ 29 | transaction: anchor.web3.Transaction; 30 | /** 31 | * The blockhash which is being used in the transaction 32 | */ 33 | blockhash: string; 34 | /** 35 | * The last valid block height after which the transaction is declared expired 36 | */ 37 | lastValidBlockHeight: number; 38 | /** 39 | * Array of signers which should be passed while sending the transaction to the network 40 | */ 41 | signers: Signer[]; 42 | /** 43 | * The mint keypair which is used to sign the transaction 44 | */ 45 | mint: anchor.web3.Signer; 46 | } 47 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/candy-machine/mint.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | 3 | import { initMetaplex } from "../../helpers"; 4 | import { rpc } from "../../constants"; 5 | import { CandyMachineMintInput, CandyMachineMintOutput } from "../../types"; 6 | 7 | export const candyMachineMint = async ( 8 | options: CandyMachineMintInput 9 | ): Promise => { 10 | try { 11 | const connection = new anchor.web3.Connection( 12 | rpc.get(options.network) || options.rpc_url! 13 | ); 14 | const metaplex = await initMetaplex({ 15 | connection, 16 | guest_mode: true, 17 | guest_public_key: options.user, 18 | }); 19 | 20 | const candyMachine = await metaplex.candyMachinesV2().findByAddress({ 21 | address: options.candyMachineId, 22 | }); 23 | const transactionBuilder = await metaplex 24 | .candyMachinesV2() 25 | .builders() 26 | .mint({ 27 | candyMachine, 28 | }); 29 | 30 | const { blockhash, lastValidBlockHeight } = 31 | await connection.getLatestBlockhash(); 32 | const transaction = transactionBuilder.toTransaction({ 33 | blockhash, 34 | lastValidBlockHeight, 35 | }); 36 | 37 | return { 38 | transaction, 39 | blockhash, 40 | lastValidBlockHeight, 41 | signers: transactionBuilder.getSigners(), 42 | mint: transactionBuilder.getContext().mintSigner as anchor.web3.Signer, 43 | }; 44 | } catch (err) { 45 | throw new Error(err); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /packages/sdk/src/lib/candy-machine/gasless.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | 3 | import { initMetaplex } from "../../helpers"; 4 | import { rpc } from "../../constants"; 5 | import { GaslessMintInput, GaslessMintOutput } from "../../types"; 6 | 7 | export const gaslessCandyMachineMint = async ( 8 | options: GaslessMintInput 9 | ): Promise => { 10 | try { 11 | const connection = new anchor.web3.Connection( 12 | rpc.get(options.network) || options.rpc_url! 13 | ); 14 | const metaplex = await initMetaplex({ 15 | connection, 16 | guest_mode: true, 17 | guest_public_key: options.payer, 18 | }); 19 | 20 | const candyMachine = await metaplex.candyMachinesV2().findByAddress({ 21 | address: options.candyMachineId, 22 | }); 23 | const transactionBuilder = await metaplex 24 | .candyMachinesV2() 25 | .builders() 26 | .mint({ 27 | candyMachine, 28 | newOwner: options.user, 29 | }); 30 | 31 | const { blockhash, lastValidBlockHeight } = 32 | await connection.getLatestBlockhash(); 33 | const transaction = transactionBuilder.toTransaction({ 34 | blockhash, 35 | lastValidBlockHeight, 36 | }); 37 | 38 | return { 39 | transaction, 40 | blockhash, 41 | lastValidBlockHeight, 42 | signers: transactionBuilder.getSigners(), 43 | mint: transactionBuilder.getContext().mintSigner as anchor.web3.Signer, 44 | }; 45 | } catch (err) { 46 | throw new Error(err); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /packages/sdk/src/types/gasless.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | import { Signer } from "@metaplex-foundation/js"; 3 | 4 | import { CandyMachineNetworks } from "./candy-machine"; 5 | 6 | export interface GaslessMintInput { 7 | /** 8 | * The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 9 | */ 10 | network: CandyMachineNetworks; 11 | /** 12 | * The address of the Candy Machine from which the NFT would to be minted 13 | */ 14 | candyMachineId: anchor.web3.PublicKey; 15 | /** 16 | * The public key of the wallet which would pay the gas fees of the transaction 17 | */ 18 | payer: anchor.web3.PublicKey; 19 | /** 20 | * The public key of the end-user 21 | */ 22 | user: anchor.web3.PublicKey; 23 | /** 24 | * Custom RPC URL 25 | */ 26 | rpc_url?: string; 27 | } 28 | 29 | export interface GaslessMintOutput { 30 | /** 31 | * The transaction object containing all the required instructions 32 | */ 33 | transaction: anchor.web3.Transaction; 34 | /** 35 | * The blockhash which is being used in the transaction 36 | */ 37 | blockhash: string; 38 | /** 39 | * The last valid block height after which the transaction is declared expired 40 | */ 41 | lastValidBlockHeight: number; 42 | /** 43 | * Array of signers which should be passed while sending the transaction to the network 44 | */ 45 | signers: Signer[]; 46 | /** 47 | * The mint keypair which is used to sign the transaction 48 | */ 49 | mint: anchor.web3.Signer; 50 | } 51 | -------------------------------------------------------------------------------- /packages/sdk/tests/candy-machine.test.ts: -------------------------------------------------------------------------------- 1 | import { CandyPay } from "../src"; 2 | import * as anchor from "@project-serum/anchor"; 3 | 4 | import { CANDY_MACHINE_ID, PAYER, USER } from "./fixtures"; 5 | 6 | jest.setTimeout(100000); 7 | 8 | describe("Candy Machine module", () => { 9 | it("Candy Machine mint", async () => { 10 | const sdk = new CandyPay(); 11 | const connection = new anchor.web3.Connection( 12 | "https://metaplex.devnet.rpcpool.com" 13 | ); 14 | const { transaction, mint } = await sdk.candyMachine.mint({ 15 | candyMachineId: CANDY_MACHINE_ID, 16 | network: "devnet", 17 | user: PAYER.publicKey, 18 | }); 19 | 20 | const signature = await anchor.web3.sendAndConfirmTransaction( 21 | connection, 22 | transaction, 23 | [PAYER, mint] 24 | ); 25 | console.log(signature); 26 | 27 | expect(signature && typeof signature === "string").toBe(true); 28 | }); 29 | 30 | it("Gasless Candy Machine mint", async () => { 31 | const sdk = new CandyPay(); 32 | const connection = new anchor.web3.Connection( 33 | "https://metaplex.devnet.rpcpool.com" 34 | ); 35 | const { transaction, mint } = await sdk.candyMachine.gasless({ 36 | candyMachineId: CANDY_MACHINE_ID, 37 | network: "devnet", 38 | payer: PAYER.publicKey, 39 | user: USER, 40 | }); 41 | 42 | const signature = await anchor.web3.sendAndConfirmTransaction( 43 | connection, 44 | transaction, 45 | [PAYER, mint] 46 | ); 47 | console.log(signature); 48 | 49 | expect(signature && typeof signature === "string").toBe(true); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/sdk/src/types/nft.ts: -------------------------------------------------------------------------------- 1 | import * as anchor from "@project-serum/anchor"; 2 | import { CreatorInput } from "@metaplex-foundation/js"; 3 | import { Uses } from "@metaplex-foundation/mpl-token-metadata"; 4 | 5 | import { Option } from "../utils"; 6 | 7 | export interface NftAirdropInput { 8 | /** 9 | * The public key of the wallet which would pay gas fees of the transaction 10 | */ 11 | payer: anchor.web3.Keypair; 12 | /** 13 | * The public key of user to whom the NFT would be airdropped 14 | */ 15 | owner: anchor.web3.PublicKey; 16 | /** 17 | * The cluster where the transaction would take place i.e either `mainnet-beta`, `devnet` or `testnet` 18 | */ 19 | network: anchor.web3.Cluster; 20 | /** 21 | * The metadata regarding the NFT 22 | */ 23 | metadata: { 24 | /** 25 | * The URI that points to the JSON metadata of the asset. 26 | */ 27 | uri: string; 28 | /** 29 | * The on-chain name of the asset, e.g. "My NFT #123". 30 | */ 31 | name: string; 32 | /** 33 | * The symbol of the NFT 34 | */ 35 | symbol: string; 36 | /** 37 | * The royalties in percent basis point (i.e. 250 is 2.5%) that should be paid to the creators on each secondary sale. 38 | */ 39 | sellerFeeBasisPoints: number; 40 | /** 41 | * This object provides a way of providing creator information when needed, e.g. when creating or updating NFTs, candy machines, etc. 42 | */ 43 | creators?: CreatorInput[]; 44 | /** 45 | * The Collection NFT that this new NFT belongs to. When `null`, the created NFT will not be part of a collection. 46 | */ 47 | collection: Option; 48 | /** 49 | * When this field is not `null`, it indicates that the NFT can be "used" by its owner or any approved "use authorities". 50 | */ 51 | uses: Option; 52 | }; 53 | /** 54 | * Custom RPC URL 55 | */ 56 | rpc_url?: string; 57 | } 58 | 59 | export interface NftAirdropOutput { 60 | /** 61 | * The signature of the NFT airdrop transaction 62 | */ 63 | signature: string; 64 | /** 65 | * The accounts related to the NFT airdrop transaction i.e mint account, metadata account, master edition account and token account 66 | */ 67 | accounts: { 68 | /** 69 | * The public key of the mint account 70 | */ 71 | mint_account: anchor.web3.PublicKey; 72 | /** 73 | * The public key of the metadata account 74 | */ 75 | metadata_account: anchor.web3.PublicKey; 76 | /** 77 | * The public key of the master edition account 78 | */ 79 | master_edition_account: anchor.web3.PublicKey; 80 | /** 81 | * The public key of the token account 82 | */ 83 | token_account: anchor.web3.PublicKey; 84 | }; 85 | /** 86 | * The blockhash which is being used in the transaction 87 | */ 88 | blockhash: string; 89 | /** 90 | * The last valid block height after which the transaction is declared expired 91 | */ 92 | lastValidBlockHeight: number; 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `@candypay/sdk` 2 | 3 | CandyPay SDK lets you effortlessly create NFT minting functions for Candy Machine v2 collections. Simulate minting transactions for multiple use-cases like NFT collection launch, gasless mint and many more on Solana Blockchain! 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install @candypay/sdk @project-serum/anchor 9 | ``` 10 | 11 | ## Setup 12 | 13 | The entry point to the SDK is a `CandyPay` instance that will give you access to its API. 14 | 15 | ```ts 16 | import { CandyPay } from "@candypay/sdk"; 17 | 18 | const candypay = new CandyPay(); 19 | ``` 20 | 21 | ## Candy Machine module 22 | 23 | The `candyMachine` module can be accessed via `candypay.candyMachine()` and provides the following methods: 24 | 25 | - [mint](#mint) 26 | - [gasless](#gasless) 27 | 28 | ### `mint` 29 | 30 | The `mint` method returns the transaction object with the all the required instructions for minting a Candy Machine v2 in the default way, where the end-user would pay the gas fees. 31 | 32 | **Parameters**: 33 | 34 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 35 | - `candyMachineId`: The address of the Candy Machine 36 | - `user`: The public key of the end-user 37 | - `rpc_url` (optional): Custom RPC URL 38 | 39 | **Response**: 40 | 41 | - `transaction`: The transaction object containing all the required instructions 42 | - `blockhash`: Blockhash which is being used in the transaction 43 | - `lastValidBlockHeight`: The last valid block height after which the transaction is declared expired 44 | - `signers`: Array of signers which should be passed while sending the transaction to the network 45 | - `mint`: The mint keypair which is used to sign the transaction 46 | 47 | **Example**: 48 | 49 | ```ts 50 | import { CandyPay } from "@candypay/sdk"; 51 | import * as anchor from "@project-serum/anchor"; 52 | import dotenv from "dotenv"; 53 | import base58 from "bs58"; 54 | 55 | dotenv.config(); 56 | 57 | const sdk = new CandyPay(); 58 | const connection = new anchor.web3.Connection( 59 | "https://metaplex.devnet.rpcpool.com" 60 | ); 61 | const CANDY_MACHINE_ID = new anchor.web3.PublicKey( 62 | "GrVSy3ZRbuw5ACbwSEMsj9gULk9MW7QPK1TUYcP6nLM" 63 | ); 64 | const PAYER = anchor.web3.Keypair.fromSecretKey( 65 | base58.decode(process.env.PAYER_SECRET_KEY!) 66 | ); 67 | 68 | const { transaction, mint } = await sdk.candyMachine.mint({ 69 | candyMachineId: CANDY_MACHINE_ID, 70 | network: "devnet", 71 | user: PAYER.publicKey, 72 | }); 73 | 74 | const signature = await anchor.web3.sendAndConfirmTransaction( 75 | connection, 76 | transaction, 77 | [PAYER, mint] 78 | ); 79 | 80 | console.log(`Signature - ${signature}`); 81 | ``` 82 | 83 | ### `gasless` 84 | 85 | The `gasless` method returns the transaction object with the all the required instructions for minting a Candy Machine v2 in the gasless way, where the end-user doesn't need pay the gas fees. 86 | 87 | **Parameters**: 88 | 89 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 90 | - `candyMachineId`: The address of the Candy Machine from which the NFT would to be minted 91 | - `payer`: The public key of the wallet which would pay the gas fees of the transaction 92 | - `user`: The public key of the end-user 93 | - `rpc_url` (optional): Custom RPC URL 94 | 95 | **Response**: 96 | 97 | - `transaction`: The transaction object containing all the required instructions 98 | - `blockhash`: The blockhash which is being used in the transaction 99 | - `lastValidBlockHeight`: The last valid block height after which the transaction is declared expired 100 | - `signers`: Array of signers which should be passed while sending the transaction to the network 101 | - `mint`: The mint keypair which is used to sign the transaction 102 | 103 | **Example**: 104 | 105 | ```ts 106 | import { CandyPay } from "@candypay/sdk"; 107 | import * as anchor from "@project-serum/anchor"; 108 | import dotenv from "dotenv"; 109 | import base58 from "bs58"; 110 | 111 | dotenv.config(); 112 | 113 | const sdk = new CandyPay(); 114 | const connection = new anchor.web3.Connection( 115 | "https://metaplex.devnet.rpcpool.com" 116 | ); 117 | 118 | const CANDY_MACHINE_ID = new anchor.web3.PublicKey( 119 | "GrVSy3ZRbuw5ACbwSEMsj9gULk9MW7QPK1TUYcP6nLM" 120 | ); 121 | const PAYER = anchor.web3.Keypair.fromSecretKey( 122 | base58.decode(process.env.PAYER_SECRET_KEY!) 123 | ); 124 | const USER = new anchor.web3.PublicKey( 125 | "2S9jKJEGKoVxR3xkEfFyGVrLwJj1H8xYjqtSP5LAX97x" 126 | ); 127 | 128 | const { transaction, mint } = await sdk.candyMachine.gasless({ 129 | candyMachineId: CANDY_MACHINE_ID, 130 | network: "devnet", 131 | payer: PAYER.publicKey, 132 | user: USER, 133 | }); 134 | 135 | const signature = await anchor.web3.sendAndConfirmTransaction( 136 | connection, 137 | transaction, 138 | [PAYER, mint] 139 | ); 140 | 141 | console.log(`Signature - ${signature}`); 142 | ``` 143 | 144 | ## NFT module 145 | 146 | The `nft` module can be accessed via `candypay.nft()` and provides the following methods: 147 | 148 | - [`airdrop`](#airdrop) 149 | 150 | ### airdrop 151 | 152 | The `airdrop` method allows you to airdrop certain NFT without having to create an NFT beforehand. 153 | 154 | **Parameters**: 155 | 156 | - `payer`: The public key of the wallet which would pay gas fees of the transaction 157 | - `owner`: The public key of user to whom the NFT would be airdropped 158 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta`, `devnet` or `testnet` 159 | - `metadata`: The metadata regarding the NFT 160 | - `rpc_url`: Custom RPC URL 161 | 162 | **Response**: 163 | 164 | - `signature`: The signature of the NFT airdrop transaction 165 | - `accounts`: The accounts related to the NFT airdrop transaction i.e mint account, metadata account, master edition account and token account 166 | - `blockhash`: The blockhash which is being used in the transaction 167 | 168 | **Example**: 169 | 170 | ```ts 171 | import { CandyPay } from "@candypay/sdk"; 172 | import * as anchor from "@project-serum/anchor"; 173 | import dotenv from "dotenv"; 174 | import base58 from "bs58"; 175 | 176 | dotenv.config(); 177 | 178 | const PAYER = anchor.web3.Keypair.fromSecretKey( 179 | base58.decode(process.env.PAYER_SECRET_KEY!) 180 | ); 181 | const USER = new anchor.web3.PublicKey( 182 | "2S9jKJEGKoVxR3xkEfFyGVrLwJj1H8xYjqtSP5LAX97x" 183 | ); 184 | 185 | const sdk = new CandyPay(); 186 | 187 | const { signature } = await sdk.nft.airdrop({ 188 | metadata: { 189 | name: "DeGod", 190 | uri: "https://metadata.degods.com/g/4924.json", 191 | symbol: "DEGOD", 192 | collection: null, 193 | sellerFeeBasisPoints: 1000, 194 | creators: [ 195 | { 196 | address: PAYER.publicKey, 197 | share: 100, 198 | }, 199 | ], 200 | uses: null, 201 | }, 202 | network: "devnet", 203 | owner: USER, 204 | payer: PAYER, 205 | }); 206 | 207 | console.log(`Signature - ${signature}`); 208 | ``` 209 | 210 | ## Using the SDK with Next.js 211 | 212 | Using our SDK with Next.js can sometimes run in build time error cause of [Anchor](https://npmjs.com/package/@project-serum/anchor) library using the node native "fs" module. We highly recommend adding this export in `next.config.js` file before deployment: 213 | 214 | ```js 215 | module.exports = { 216 | webpack: (config, { isServer }) => { 217 | if (!isServer) { 218 | config.resolve.fallback.fs = false; 219 | } 220 | return config; 221 | }, 222 | }; 223 | ``` 224 | 225 | ## Get in Touch 226 | 227 | - Twitter: [@candypayfun](https://twitter.com/candypayfun) 228 | - Discord: [Join Now](https://discord.com/invite/VGjPXWUHGT) 229 | - Email: [hello@candypay.fun](mailto:hello@candypay.fun) 230 | -------------------------------------------------------------------------------- /packages/sdk/README.md: -------------------------------------------------------------------------------- 1 | # `@candypay/sdk` 2 | 3 | CandyPay SDK lets you effortlessly create NFT minting functions for Candy Machine v2 collections. Simulate minting transactions for multiple use-cases like NFT collection launch, gasless mint and many more on Solana Blockchain! 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install @candypay/sdk @project-serum/anchor 9 | ``` 10 | 11 | ## Setup 12 | 13 | The entry point to the SDK is a `CandyPay` instance that will give you access to its API. 14 | 15 | ```ts 16 | import { CandyPay } from "@candypay/sdk"; 17 | 18 | const candypay = new CandyPay(); 19 | ``` 20 | 21 | ## Candy Machine module 22 | 23 | The `candyMachine` module can be accessed via `candypay.candyMachine()` and provides the following methods: 24 | 25 | - [mint](#mint) 26 | - [gasless](#gasless) 27 | 28 | ### `mint` 29 | 30 | The `mint` method returns the transaction object with the all the required instructions for minting a Candy Machine v2 in the default way, where the end-user would pay the gas fees. 31 | 32 | **Parameters**: 33 | 34 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 35 | - `candyMachineId`: The address of the Candy Machine 36 | - `user`: The public key of the end-user 37 | - `rpc_url` (optional): Custom RPC URL 38 | 39 | **Response**: 40 | 41 | - `transaction`: The transaction object containing all the required instructions 42 | - `blockhash`: Blockhash which is being used in the transaction 43 | - `lastValidBlockHeight`: The last valid block height after which the transaction is declared expired 44 | - `signers`: Array of signers which should be passed while sending the transaction to the network 45 | - `mint`: The mint keypair which is used to sign the transaction 46 | 47 | **Example**: 48 | 49 | ```ts 50 | import { CandyPay } from "@candypay/sdk"; 51 | import * as anchor from "@project-serum/anchor"; 52 | import dotenv from "dotenv"; 53 | import base58 from "bs58"; 54 | 55 | dotenv.config(); 56 | 57 | const sdk = new CandyPay(); 58 | const connection = new anchor.web3.Connection( 59 | "https://metaplex.devnet.rpcpool.com" 60 | ); 61 | 62 | const CANDY_MACHINE_ID = new anchor.web3.PublicKey( 63 | "GrVSy3ZRbuw5ACbwSEMsj9gULk9MW7QPK1TUYcP6nLM" 64 | ); 65 | const PAYER = anchor.web3.Keypair.fromSecretKey( 66 | base58.decode(process.env.PAYER_SECRET_KEY!) 67 | ); 68 | 69 | const { transaction, mint } = await sdk.candyMachine.mint({ 70 | candyMachineId: CANDY_MACHINE_ID, 71 | network: "devnet", 72 | user: PAYER.publicKey, 73 | }); 74 | 75 | const signature = await anchor.web3.sendAndConfirmTransaction( 76 | connection, 77 | transaction, 78 | [PAYER, mint] 79 | ); 80 | 81 | console.log(`Signature - ${signature}`); 82 | ``` 83 | 84 | ### `gasless` 85 | 86 | The `gasless` method returns the transaction object with the all the required instructions for minting a Candy Machine v2 in the gasless way, where the end-user doesn't need pay the gas fees. 87 | 88 | **Parameters**: 89 | 90 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta` or `devnet` 91 | - `candyMachineId`: The address of the Candy Machine from which the NFT would to be minted 92 | - `payer`: The public key of the wallet which would pay the gas fees of the transaction 93 | - `user`: The public key of the end-user 94 | - `rpc_url` (optional): Custom RPC URL 95 | 96 | **Response**: 97 | 98 | - `transaction`: The transaction object containing all the required instructions 99 | - `blockhash`: The blockhash which is being used in the transaction 100 | - `lastValidBlockHeight`: The last valid block height after which the transaction is declared expired 101 | - `signers`: Array of signers which should be passed while sending the transaction to the network 102 | - `mint`: The mint keypair which is used to sign the transaction 103 | 104 | **Example**: 105 | 106 | ```ts 107 | import { CandyPay } from "@candypay/sdk"; 108 | import * as anchor from "@project-serum/anchor"; 109 | import dotenv from "dotenv"; 110 | import base58 from "bs58"; 111 | 112 | dotenv.config(); 113 | 114 | const sdk = new CandyPay(); 115 | const connection = new anchor.web3.Connection( 116 | "https://metaplex.devnet.rpcpool.com" 117 | ); 118 | 119 | const CANDY_MACHINE_ID = new anchor.web3.PublicKey( 120 | "GrVSy3ZRbuw5ACbwSEMsj9gULk9MW7QPK1TUYcP6nLM" 121 | ); 122 | const PAYER = anchor.web3.Keypair.fromSecretKey( 123 | base58.decode(process.env.PAYER_SECRET_KEY!) 124 | ); 125 | const USER = new anchor.web3.PublicKey( 126 | "2S9jKJEGKoVxR3xkEfFyGVrLwJj1H8xYjqtSP5LAX97x" 127 | ); 128 | 129 | const { transaction, mint } = await sdk.candyMachine.gasless({ 130 | candyMachineId: CANDY_MACHINE_ID, 131 | network: "devnet", 132 | payer: PAYER.publicKey, 133 | user: USER, 134 | }); 135 | 136 | const signature = await anchor.web3.sendAndConfirmTransaction( 137 | connection, 138 | transaction, 139 | [PAYER, mint] 140 | ); 141 | 142 | console.log(`Signature - ${signature}`); 143 | ``` 144 | 145 | ## NFT module 146 | 147 | The `nft` module can be accessed via `candypay.nft()` and provides the following methods: 148 | 149 | - [`airdrop`](#airdrop) 150 | 151 | ### airdrop 152 | 153 | The `airdrop` method allows you to airdrop certain NFT without having to create an NFT beforehand. 154 | 155 | **Parameters**: 156 | 157 | - `payer`: The public key of the wallet which would pay gas fees of the transaction 158 | - `owner`: The public key of user to whom the NFT would be airdropped 159 | - `network`: The cluster where the transaction would take place i.e either `mainnet-beta`, `devnet` or `testnet` 160 | - `metadata`: The metadata regarding the NFT 161 | - `rpc_url`: Custom RPC URL 162 | 163 | **Response**: 164 | 165 | - `signature`: The signature of the NFT airdrop transaction 166 | - `accounts`: The accounts related to the NFT airdrop transaction i.e mint account, metadata account, master edition account and token account 167 | - `blockhash`: The blockhash which is being used in the transaction 168 | 169 | **Example**: 170 | 171 | ```ts 172 | import { CandyPay } from "@candypay/sdk"; 173 | import * as anchor from "@project-serum/anchor"; 174 | import dotenv from "dotenv"; 175 | import base58 from "bs58"; 176 | 177 | dotenv.config(); 178 | 179 | const sdk = new CandyPay(); 180 | 181 | const PAYER = anchor.web3.Keypair.fromSecretKey( 182 | base58.decode(process.env.PAYER_SECRET_KEY!) 183 | ); 184 | const USER = new anchor.web3.PublicKey( 185 | "2S9jKJEGKoVxR3xkEfFyGVrLwJj1H8xYjqtSP5LAX97x" 186 | ); 187 | 188 | const { signature } = await sdk.nft.airdrop({ 189 | metadata: { 190 | name: "DeGod", 191 | uri: "https://metadata.degods.com/g/4924.json", 192 | symbol: "DEGOD", 193 | collection: null, 194 | sellerFeeBasisPoints: 1000, 195 | creators: [ 196 | { 197 | address: PAYER.publicKey, 198 | share: 100, 199 | }, 200 | ], 201 | uses: null, 202 | }, 203 | network: "devnet", 204 | owner: USER, 205 | payer: PAYER, 206 | }); 207 | 208 | console.log(`Signature - ${signature}`); 209 | ``` 210 | 211 | ## Using the SDK with Next.js 212 | 213 | Using our SDK with Next.js can sometimes run in build time error cause of [Anchor](https://npmjs.com/package/@project-serum/anchor) library using the node native "fs" module. We highly recommend adding this export in `next.config.js` file before deployment: 214 | 215 | ```js 216 | module.exports = { 217 | webpack: (config, { isServer }) => { 218 | if (!isServer) { 219 | config.resolve.fallback.fs = false; 220 | } 221 | return config; 222 | }, 223 | }; 224 | ``` 225 | 226 | ## Get in Touch 227 | 228 | - Twitter: [@candypayfun](https://twitter.com/candypayfun) 229 | - Discord: [Join Now](https://discord.com/invite/VGjPXWUHGT) 230 | - Email: [hello@candypay.fun](mailto:hello@candypay.fun) 231 | --------------------------------------------------------------------------------