Skip to content

Commit

Permalink
Merge pull request solana-labs#127 from jarry-xiao/sorend/gumball-mac…
Browse files Browse the repository at this point in the history
…hine-cli

Gumball Machine CLI
  • Loading branch information
samwise2 authored Jul 1, 2022
2 parents 543c52b + 31dc44e commit ebc5e73
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 94 deletions.
51 changes: 25 additions & 26 deletions contracts/sdk/gumball-machine/instructions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
createInitializeGumballMachineInstruction,
createDispenseNftSolInstruction,
createDispenseNftTokenInstruction,
DispenseNftSolInstructionArgs,
DispenseNftTokenInstructionArgs
} from "../src/generated";
import { getWillyWonkaPDAKey } from "../utils";
import { CANDY_WRAPPER_PROGRAM_ID } from "../../utils";
Expand All @@ -31,6 +33,7 @@ export async function createInitializeGumballMachineIxs(
merkleRollAccountSize: number,
gumballMachineInitArgs: InitializeGumballMachineInstructionArgs,
mint: PublicKey,
creator: PublicKey,
gummyrollProgramId: PublicKey,
bubblegumProgramId: PublicKey,
gumballMachine: Program<GumballMachine>
Expand Down Expand Up @@ -68,7 +71,7 @@ export async function createInitializeGumballMachineIxs(
const initGumballMachineInstr = createInitializeGumballMachineInstruction(
{
gumballMachine: gumballMachineAcctKeypair.publicKey,
creator: payer.publicKey,
creator,
mint,
willyWonka: willyWonkaPDAKey,
bubblegumAuthority: bubblegumAuthorityPDAKey,
Expand All @@ -90,39 +93,37 @@ export async function createInitializeGumballMachineIxs(
* Wrapper on top of Solita's createDispenseNftSolInstruction. Automatically fetches necessary PDA keys for instruction
* */
export async function createDispenseNFTForSolIx(
numNFTs: BN,
payer: Keypair,
args: DispenseNftSolInstructionArgs,
payer: PublicKey,
receiver: PublicKey,
gumballMachineAcctKeypair: Keypair,
merkleRollKeypair: Keypair,
gumballMachinePubkey: PublicKey,
merkleRollPubkey: PublicKey,
gummyrollProgramId: PublicKey,
bubblegumProgramId: PublicKey,
gumballMachine: Program<GumballMachine>
): Promise<TransactionInstruction> {
const willyWonkaPDAKey = await getWillyWonkaPDAKey(
gumballMachineAcctKeypair.publicKey,
gumballMachinePubkey,
gumballMachine.programId
);
const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDA(
merkleRollKeypair.publicKey,
merkleRollPubkey,
);
const dispenseInstr = createDispenseNftSolInstruction(
{
gumballMachine: gumballMachineAcctKeypair.publicKey,
payer: payer.publicKey,
gumballMachine: gumballMachinePubkey,
payer,
receiver: receiver,
willyWonka: willyWonkaPDAKey,
recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY,
instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
bubblegumAuthority: bubblegumAuthorityPDAKey,
candyWrapper: CANDY_WRAPPER_PROGRAM_ID,
gummyroll: gummyrollProgramId,
merkleSlab: merkleRollKeypair.publicKey,
merkleSlab: merkleRollPubkey,
bubblegum: bubblegumProgramId,
},
{
numItems: numNFTs,
}
args
);
return dispenseInstr;
}
Expand All @@ -131,27 +132,27 @@ export async function createDispenseNFTForSolIx(
* Wrapper on top of Solita's createDispenseNftTokenInstruction. Automatically fetches necessary PDA keys for instruction
* */
export async function createDispenseNFTForTokensIx(
numNFTs: BN,
payer: Keypair,
args: DispenseNftTokenInstructionArgs,
payer: PublicKey,
payerTokens: PublicKey,
receiver: PublicKey,
gumballMachineAcctKeypair: Keypair,
merkleRollKeypair: Keypair,
gumballMachinePubkey: PublicKey,
merkleRollPubkey: PublicKey,
gummyrollProgramId: PublicKey,
bubblegumProgramId: PublicKey,
gumballMachine: Program<GumballMachine>,
): Promise<TransactionInstruction> {
const willyWonkaPDAKey = await getWillyWonkaPDAKey(
gumballMachineAcctKeypair.publicKey,
gumballMachinePubkey,
gumballMachine.programId
);
const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDA(
merkleRollKeypair.publicKey,
merkleRollPubkey,
);
const dispenseInstr = createDispenseNftTokenInstruction(
{
gumballMachine: gumballMachineAcctKeypair.publicKey,
payer: payer.publicKey,
gumballMachine: gumballMachinePubkey,
payer,
payerTokens,
receiver,
willyWonka: willyWonkaPDAKey,
Expand All @@ -160,12 +161,10 @@ export async function createDispenseNFTForTokensIx(
bubblegumAuthority: bubblegumAuthorityPDAKey,
candyWrapper: CANDY_WRAPPER_PROGRAM_ID,
gummyroll: gummyrollProgramId,
merkleSlab: merkleRollKeypair.publicKey,
merkleSlab: merkleRollPubkey,
bubblegum: bubblegumProgramId,
},
{
numItems: numNFTs,
}
args
);
return dispenseInstr;
}
}
41 changes: 31 additions & 10 deletions contracts/sdk/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import { PublicKey } from "@solana/web3.js";
import { PublicKey, TransactionInstruction, Transaction, Signer } from "@solana/web3.js";
import * as borsh from "borsh";
import { bignum } from "@metaplex-foundation/beet";
import { BN } from "@project-serum/anchor";
import { BN, Provider } from "@project-serum/anchor";

export const CANDY_WRAPPER_PROGRAM_ID = new PublicKey("WRAPYChf58WFCnyjXKJHtrPgzKXgHp6MD9aVDqJBbGh");

/// Wait for a transaction of a certain id to confirm and optionally log its messages
export async function logTx(provider: Provider, txId: string, verbose: boolean = true) {
await provider.connection.confirmTransaction(txId, "confirmed");
if (verbose) {
console.log(
(await provider.connection.getConfirmedTransaction(txId, "confirmed")).meta
.logMessages
);
}
};

/// Execute a series of instructions in a txn
export async function execute(
provider: Provider,
instructions: TransactionInstruction[],
signers: Signer[],
skipPreflight: boolean = false,
verbose: boolean = false,
): Promise<String> {
let tx = new Transaction();
instructions.map((ix) => { tx = tx.add(ix) });
const txid = await provider.send(tx, signers, {
commitment: "confirmed",
skipPreflight,
});
await logTx(provider, txid, verbose);
return txid;
}

export function readPublicKey(reader: borsh.BinaryReader): PublicKey {
return new PublicKey(reader.readFixedArray(32));
}
Expand All @@ -31,11 +60,3 @@ export function strToByteUint8Array(str: string): Uint8Array {
[...str].reduce((acc, c, ind) => acc.concat([str.charCodeAt(ind)]), [])
);
}

export async function getBubblegumAuthorityPDAKey(merkleRollPubKey: PublicKey, bubblegumProgramId: PublicKey) {
const [bubblegumAuthorityPDAKey] = await PublicKey.findProgramAddress(
[merkleRollPubKey.toBuffer()],
bubblegumProgramId
);
return bubblegumAuthorityPDAKey;
}
4 changes: 2 additions & 2 deletions contracts/tests/bubblegum-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ import {
TOKEN_PROGRAM_ID,
Token,
} from "@solana/spl-token";
import { execute, logTx, bufferToArray } from "./utils";
import { bufferToArray } from "./utils";
import { TokenProgramVersion, Version } from "../sdk/bubblegum/src/generated";
import { CANDY_WRAPPER_PROGRAM_ID } from "../sdk/utils";
import { CANDY_WRAPPER_PROGRAM_ID, execute, logTx } from "../sdk/utils";
import { getBubblegumAuthorityPDA, getCreateTreeIxs, getNonceCount, getVoucherPDA } from "../sdk/bubblegum/src/convenience";

// @ts-ignore
Expand Down
39 changes: 20 additions & 19 deletions contracts/tests/gumball-machine-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
val,
strToByteArray,
strToByteUint8Array,
getBubblegumAuthorityPDAKey
logTx
} from "../sdk/utils/index";
import {
GumballMachineHeader,
Expand All @@ -51,7 +51,7 @@ import {
getAccount,
} from "../../deps/solana-program-library/token/js/src";
import { NATIVE_MINT } from "@solana/spl-token";
import { logTx, num32ToBuffer, arrayEquals } from "./utils";
import { num32ToBuffer, arrayEquals } from "./utils";
import { EncodeMethod } from "../sdk/gumball-machine/src/generated/types/EncodeMethod";
import { getBubblegumAuthorityPDA } from "../sdk/bubblegum/src/convenience";

Expand Down Expand Up @@ -211,6 +211,7 @@ describe("gumball-machine", () => {
merkleRollAccountSize,
gumballMachineInitArgs,
mint,
payer.publicKey,
GummyrollProgramId,
BubblegumProgramId,
GumballMachine
Expand Down Expand Up @@ -413,11 +414,11 @@ describe("gumball-machine", () => {
verbose?: boolean
) {
const dispenseInstr = await createDispenseNFTForSolIx(
numNFTs,
payer,
{ numItems: numNFTs },
payer.publicKey,
receiver,
gumballMachineAcctKeypair,
merkleRollKeypair,
gumballMachineAcctKeypair.publicKey,
merkleRollKeypair.publicKey,
GummyrollProgramId,
BubblegumProgramId,
GumballMachine
Expand All @@ -441,12 +442,12 @@ describe("gumball-machine", () => {
verbose?: boolean
) {
const dispenseInstr = await createDispenseNFTForTokensIx(
numNFTs,
payer,
{ numItems: numNFTs },
payer.publicKey,
payerTokens,
receiver,
gumballMachineAcctKeypair,
merkleRollKeypair,
gumballMachineAcctKeypair.publicKey,
merkleRollKeypair.publicKey,
GummyrollProgramId,
BubblegumProgramId,
GumballMachine
Expand Down Expand Up @@ -542,7 +543,7 @@ describe("gumball-machine", () => {
botWallet: Keypair.generate().publicKey,
receiver: creatorPaymentWallet.publicKey,
authority: creatorAddress.publicKey,
collectionKey: SystemProgram.programId, // 0x0 -> no collection key
collectionKey: SystemProgram.programId,
extensionLen: new BN(28),
maxMintSize: new BN(10),
maxItems: new BN(250),
Expand Down Expand Up @@ -603,11 +604,11 @@ describe("gumball-machine", () => {

beforeEach(async () => {
dispenseNFTForSolInstr = await createDispenseNFTForSolIx(
new BN(1),
nftBuyer,
{ numItems: new BN(1) },
nftBuyer.publicKey,
baseGumballMachineInitProps.receiver,
gumballMachineAcctKeypair,
merkleRollKeypair,
gumballMachineAcctKeypair.publicKey,
merkleRollKeypair.publicKey,
GummyrollProgramId,
BubblegumProgramId,
GumballMachine
Expand Down Expand Up @@ -887,12 +888,12 @@ describe("gumball-machine", () => {

beforeEach(async () => {
dispenseNFTForTokensInstr = await createDispenseNFTForTokensIx(
new BN(1),
nftBuyer,
{ numItems: new BN(1) },
nftBuyer.publicKey,
nftBuyerTokenAccount.address,
creatorReceiverTokenAccount.address,
gumballMachineAcctKeypair,
merkleRollKeypair,
gumballMachineAcctKeypair.publicKey,
merkleRollKeypair.publicKey,
GummyrollProgramId,
BubblegumProgramId,
GumballMachine
Expand Down
3 changes: 1 addition & 2 deletions contracts/tests/gummyroll-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ import {
assertOnChainMerkleRollProperties,
createAllocTreeIx,
} from "../sdk/gummyroll";
import { execute, logTx } from "./utils";
import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet";
import { execute, logTx } from "../sdk/utils";
import { CANDY_WRAPPER_PROGRAM_ID } from "../sdk/utils";

// @ts-ignore
Expand Down
8 changes: 4 additions & 4 deletions contracts/tests/sugar-shack-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ import {
getListingPDAKeyForPrice
} from "../sdk/sugar-shack";
import {
CANDY_WRAPPER_PROGRAM_ID,
getBubblegumAuthorityPDAKey
CANDY_WRAPPER_PROGRAM_ID
} from "../sdk/utils/index";
import {
MetadataArgs,
Expand All @@ -52,9 +51,10 @@ import {
TreeNode,
} from "./merkle-tree";
import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet";
import { execute, logTx, bufferToArray } from "./utils";
import { bufferToArray } from "./utils";
import { TokenProgramVersion, Version } from "../sdk/bubblegum/src/generated";
import { SugarShack } from "../target/types/sugar_shack";
import { getBubblegumAuthorityPDA } from "../sdk/bubblegum/src/convenience";

// @ts-ignore
let BubblegumProgramId;
Expand Down Expand Up @@ -296,7 +296,7 @@ describe("sugar-shack", () => {
payer.publicKey,
merkleRollKeypair.publicKey,
)
bubblegumAuthority = await getBubblegumAuthorityPDAKey(merkleRollKeypair.publicKey, BubblegumProgramId);
bubblegumAuthority = await getBubblegumAuthorityPDA(merkleRollKeypair.publicKey);

// Instruction to create merkle tree for compressed NFTs through Bubblegum
const createCompressedNFTTreeIx = createCreateTreeInstruction(
Expand Down
31 changes: 0 additions & 31 deletions contracts/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,3 @@
import { Provider } from "@project-serum/anchor";
import { TransactionInstruction, Transaction, Signer } from "@solana/web3.js";

/// Wait for a transaction of a certain id to confirm and optionally log its messages
export async function logTx(provider: Provider, txId: string, verbose: boolean = true) {
await provider.connection.confirmTransaction(txId, "confirmed");
if (verbose) {
console.log(
(await provider.connection.getConfirmedTransaction(txId, "confirmed")).meta
.logMessages
);
}
};

/// Execute a series of instructions in a txn
export async function execute(
provider: Provider,
instructions: TransactionInstruction[],
signers: Signer[],
skipPreflight: boolean = false
): Promise<String> {
let tx = new Transaction();
instructions.map((ix) => { tx = tx.add(ix) });
const txid = await provider.send(tx, signers, {
commitment: "confirmed",
skipPreflight,
});
await logTx(provider, txid, false);
return txid;
}

/// Convert a 32 bit number to a buffer of bytes
export function num32ToBuffer(num: number) {
const isU32 = (num >= 0 && num < Math.pow(2,32));
Expand Down

0 comments on commit ebc5e73

Please sign in to comment.