diff --git a/contracts/package.json b/contracts/package.json index a00dde9eaf4..12ab22e21e7 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@metaplex-foundation/amman": "^0.0.12", + "@metaplex-foundation/beet": "^0.2.0", "@metaplex-foundation/mpl-core": "^0.0.4", "@metaplex-foundation/mpl-token-metadata": "^2.1.1", "@project-serum/anchor": "^0.21.0", diff --git a/contracts/sdk/bubblegum/README.md b/contracts/sdk/bubblegum/README.md new file mode 100644 index 00000000000..3a02e6e7ec9 --- /dev/null +++ b/contracts/sdk/bubblegum/README.md @@ -0,0 +1,14 @@ +# Bubblegum + +This SDK uses MPL's `Solita` to generate typescript SDK for `anchor` smart-contract. + +Solita is particularly helpful: +- Enums: (i.e. TokenProgramVersion) +- Complex types: (ie MetadataArgs support) +- Using typed system to identify issues with smart contract args + +### Install + +1. `yarn` +2. `node solita.js` +3. `import { ... } from '../sdk/bubblegum'` diff --git a/contracts/sdk/bubblegum/idl/bubblegum.json b/contracts/sdk/bubblegum/idl/bubblegum.json new file mode 100644 index 00000000000..383fe94a033 --- /dev/null +++ b/contracts/sdk/bubblegum/idl/bubblegum.json @@ -0,0 +1,1006 @@ +{ + "version": "0.1.0", + "name": "bubblegum", + "instructions": [ + { + "name": "initializeNonce", + "accounts": [ + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "createTree", + "accounts": [ + { + "name": "treeCreator", + "isMut": false, + "isSigner": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "maxDepth", + "type": "u32" + }, + { + "name": "maxBufferSize", + "type": "u32" + } + ] + }, + { + "name": "mint", + "accounts": [ + { + "name": "mintAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "message", + "type": { + "defined": "MetadataArgs" + } + } + ] + }, + { + "name": "transfer", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": false + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "newOwner", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "nonce", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] + }, + { + "name": "delegate", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "previousDelegate", + "isMut": false, + "isSigner": false + }, + { + "name": "newDelegate", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "nonce", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] + }, + { + "name": "burn", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": false + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "nonce", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] + }, + { + "name": "redeem", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + }, + { + "name": "voucher", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "nonce", + "type": "u128" + }, + { + "name": "index", + "type": "u32" + } + ] + }, + { + "name": "cancelRedeem", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + }, + { + "name": "voucher", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "decompress", + "accounts": [ + { + "name": "voucher", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "mintAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false + }, + { + "name": "masterEdition", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "sysvarRent", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "metadata", + "type": { + "defined": "MetadataArgs" + } + } + ] + }, + { + "name": "compress", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false + }, + { + "name": "masterEdition", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenMetadataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyrollProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Nonce", + "type": { + "kind": "struct", + "fields": [ + { + "name": "count", + "type": "u128" + } + ] + } + }, + { + "name": "Voucher", + "type": { + "kind": "struct", + "fields": [ + { + "name": "leafSchema", + "type": { + "defined": "LeafSchema" + } + }, + { + "name": "index", + "type": "u32" + }, + { + "name": "merkleSlab", + "type": "publicKey" + } + ] + } + } + ], + "types": [ + { + "name": "LeafSchema", + "type": { + "kind": "struct", + "fields": [ + { + "name": "version", + "type": { + "defined": "Version" + } + }, + { + "name": "owner", + "type": "publicKey" + }, + { + "name": "delegate", + "type": "publicKey" + }, + { + "name": "nonce", + "type": "u128" + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "Creator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "verified", + "type": "bool" + }, + { + "name": "share", + "type": "u8" + } + ] + } + }, + { + "name": "Uses", + "type": { + "kind": "struct", + "fields": [ + { + "name": "useMethod", + "type": { + "defined": "UseMethod" + } + }, + { + "name": "remaining", + "type": "u64" + }, + { + "name": "total", + "type": "u64" + } + ] + } + }, + { + "name": "Collection", + "type": { + "kind": "struct", + "fields": [ + { + "name": "verified", + "type": "bool" + }, + { + "name": "key", + "type": "publicKey" + } + ] + } + }, + { + "name": "MetadataArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "docs": [ + "The name of the asset" + ], + "type": "string" + }, + { + "name": "symbol", + "docs": [ + "The symbol for the asset" + ], + "type": "string" + }, + { + "name": "uri", + "docs": [ + "URI pointing to JSON representing the asset" + ], + "type": "string" + }, + { + "name": "sellerFeeBasisPoints", + "docs": [ + "Royalty basis points that goes to creators in secondary sales (0-10000)" + ], + "type": "u16" + }, + { + "name": "primarySaleHappened", + "type": "bool" + }, + { + "name": "isMutable", + "type": "bool" + }, + { + "name": "editionNonce", + "docs": [ + "nonce for easy calculation of editions, if present" + ], + "type": { + "option": "u8" + } + }, + { + "name": "tokenStandard", + "docs": [ + "Since we cannot easily change Metadata, we add the new DataV2 fields here at the end." + ], + "type": { + "option": { + "defined": "TokenStandard" + } + } + }, + { + "name": "collection", + "docs": [ + "Collection" + ], + "type": { + "option": { + "defined": "Collection" + } + } + }, + { + "name": "uses", + "docs": [ + "Uses" + ], + "type": { + "option": { + "defined": "Uses" + } + } + }, + { + "name": "tokenProgramVersion", + "type": { + "defined": "TokenProgramVersion" + } + }, + { + "name": "creators", + "type": { + "vec": { + "defined": "Creator" + } + } + } + ] + } + }, + { + "name": "Version", + "type": { + "kind": "enum", + "variants": [ + { + "name": "V0" + } + ] + } + }, + { + "name": "TokenProgramVersion", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Original" + }, + { + "name": "Token2022" + } + ] + } + }, + { + "name": "TokenStandard", + "type": { + "kind": "enum", + "variants": [ + { + "name": "NonFungible" + }, + { + "name": "FungibleAsset" + }, + { + "name": "Fungible" + }, + { + "name": "NonFungibleEdition" + } + ] + } + }, + { + "name": "UseMethod", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Burn" + }, + { + "name": "Multiple" + }, + { + "name": "Single" + } + ] + } + }, + { + "name": "InstructionName", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Unknown" + }, + { + "name": "Mint" + }, + { + "name": "Redeem" + }, + { + "name": "CancelRedeem" + }, + { + "name": "Transfer" + }, + { + "name": "Delegate" + }, + { + "name": "Decompress" + } + ] + } + } + ], + "events": [ + { + "name": "LeafSchemaEvent", + "fields": [ + { + "name": "version", + "type": { + "defined": "Version" + }, + "index": false + }, + { + "name": "owner", + "type": "publicKey", + "index": false + }, + { + "name": "delegate", + "type": "publicKey", + "index": false + }, + { + "name": "nonce", + "type": "u128", + "index": false + }, + { + "name": "dataHash", + "type": { + "array": [ + "u8", + 32 + ] + }, + "index": false + }, + { + "name": "creatorHash", + "type": { + "array": [ + "u8", + 32 + ] + }, + "index": false + }, + { + "name": "leafHash", + "type": { + "array": [ + "u8", + 32 + ] + }, + "index": false + } + ] + } + ], + "metadata": { + "address": "BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o" + } +} \ No newline at end of file diff --git a/contracts/sdk/bubblegum/package.json b/contracts/sdk/bubblegum/package.json new file mode 100644 index 00000000000..1332340721b --- /dev/null +++ b/contracts/sdk/bubblegum/package.json @@ -0,0 +1,11 @@ +{ + "name": "bubblegum", + "version": "1.0.0", + "description": "SDK for MPL Bubblegum contract", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/rustbin": "^0.3.1", + "@metaplex-foundation/solita": "^0.8.2" + } +} diff --git a/contracts/sdk/bubblegum/solita.js b/contracts/sdk/bubblegum/solita.js new file mode 100644 index 00000000000..09f43fb6207 --- /dev/null +++ b/contracts/sdk/bubblegum/solita.js @@ -0,0 +1,66 @@ +const path = require('path'); +const { + rustbinMatch, + confirmAutoMessageConsole, +} = require('@metaplex-foundation/rustbin') +const { spawn } = require('child_process'); +const { Solita } = require('@metaplex-foundation/solita'); +const { writeFile } = require('fs/promises'); +const { fstat, existsSync } = require('fs'); + +const PROGRAM_NAME = 'bubblegum'; +const PROGRAM_ID = 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o'; + +const programDir = path.join(__dirname, '..', '..', 'programs', 'bubblegum'); +const cargoToml = path.join(programDir, 'Cargo.toml') +const generatedIdlDir = path.join(__dirname, 'idl'); +const generatedSDKDir = path.join(__dirname, 'src', 'generated'); +const rootDir = path.join(__dirname, '.crates') + +async function main() { + const anchorExecutable = realpathSync("../../../deps/anchor/target/debug/anchor"); + if (!existsSync(anchorExecutable)) { + console.log(`Could not find: ${anchorExecutable}`); + throw new Error("Please `cd candyland/deps/anchor/anchor-cli` && cargo build`") + } + const anchor = spawn(anchorExecutable, ['build', '--idl', generatedIdlDir], { cwd: programDir }) + .on('error', (err) => { + console.error(err); + // @ts-ignore this err does have a code + if (err.code === 'ENOENT') { + console.error( + 'Ensure that `anchor` is installed and in your path, see:\n https://project-serum.github.io/anchor/getting-started/installation.html#install-anchor\n', + ); + } + process.exit(1); + }) + .on('exit', () => { + console.log('IDL written to: %s', path.join(generatedIdlDir, `${PROGRAM_NAME}.json`)); + generateTypeScriptSDK(); + }); + + anchor.stdout.on('data', (buf) => console.log(buf.toString('utf8'))); + anchor.stderr.on('data', (buf) => console.error(buf.toString('utf8'))); +} + +async function generateTypeScriptSDK() { + console.error('Generating TypeScript SDK to %s', generatedSDKDir); + const generatedIdlPath = path.join(generatedIdlDir, `${PROGRAM_NAME}.json`); + + const idl = require(generatedIdlPath); + if (idl.metadata?.address == null) { + idl.metadata = { ...idl.metadata, address: PROGRAM_ID }; + await writeFile(generatedIdlPath, JSON.stringify(idl, null, 2)); + } + const gen = new Solita(idl, { formatCode: true }); + await gen.renderAndWriteTo(generatedSDKDir); + + console.error('Success!'); + + process.exit(0); +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/contracts/sdk/bubblegum/src/generated/accounts/Nonce.ts b/contracts/sdk/bubblegum/src/generated/accounts/Nonce.ts new file mode 100644 index 00000000000..0b4fbc5031a --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/accounts/Nonce.ts @@ -0,0 +1,154 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * Arguments used to create {@link Nonce} + * @category Accounts + * @category generated + */ +export type NonceArgs = { + count: beet.bignum +} + +const nonceDiscriminator = [143, 197, 147, 95, 106, 165, 50, 43] +/** + * Holds the data for the {@link Nonce} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class Nonce implements NonceArgs { + private constructor(readonly count: beet.bignum) {} + + /** + * Creates a {@link Nonce} instance from the provided args. + */ + static fromArgs(args: NonceArgs) { + return new Nonce(args.count) + } + + /** + * Deserializes the {@link Nonce} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0 + ): [Nonce, number] { + return Nonce.deserialize(accountInfo.data, offset) + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link Nonce} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey + ): Promise { + const accountInfo = await connection.getAccountInfo(address) + if (accountInfo == null) { + throw new Error(`Unable to find Nonce account at ${address}`) + } + return Nonce.fromAccountInfo(accountInfo, 0)[0] + } + + /** + * Deserializes the {@link Nonce} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [Nonce, number] { + return nonceBeet.deserialize(buf, offset) + } + + /** + * Serializes the {@link Nonce} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return nonceBeet.serialize({ + accountDiscriminator: nonceDiscriminator, + ...this, + }) + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link Nonce} + */ + static get byteSize() { + return nonceBeet.byteSize + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link Nonce} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment + ): Promise { + return connection.getMinimumBalanceForRentExemption( + Nonce.byteSize, + commitment + ) + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link Nonce} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === Nonce.byteSize + } + + /** + * Returns a readable version of {@link Nonce} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + count: (() => { + const x = <{ toNumber: () => number }>this.count + if (typeof x.toNumber === 'function') { + try { + return x.toNumber() + } catch (_) { + return x + } + } + return x + })(), + } + } +} + +/** + * @category Accounts + * @category generated + */ +export const nonceBeet = new beet.BeetStruct< + Nonce, + NonceArgs & { + accountDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['accountDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['count', beet.u128], + ], + Nonce.fromArgs, + 'Nonce' +) diff --git a/contracts/sdk/bubblegum/src/generated/accounts/Voucher.ts b/contracts/sdk/bubblegum/src/generated/accounts/Voucher.ts new file mode 100644 index 00000000000..335a1c3fc72 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/accounts/Voucher.ts @@ -0,0 +1,156 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js' +import * as beet from '@metaplex-foundation/beet' +import * as beetSolana from '@metaplex-foundation/beet-solana' +import { LeafSchema, leafSchemaBeet } from '../types/LeafSchema' + +/** + * Arguments used to create {@link Voucher} + * @category Accounts + * @category generated + */ +export type VoucherArgs = { + leafSchema: LeafSchema + index: number + merkleSlab: web3.PublicKey +} + +const voucherDiscriminator = [191, 204, 149, 234, 213, 165, 13, 65] +/** + * Holds the data for the {@link Voucher} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class Voucher implements VoucherArgs { + private constructor( + readonly leafSchema: LeafSchema, + readonly index: number, + readonly merkleSlab: web3.PublicKey + ) {} + + /** + * Creates a {@link Voucher} instance from the provided args. + */ + static fromArgs(args: VoucherArgs) { + return new Voucher(args.leafSchema, args.index, args.merkleSlab) + } + + /** + * Deserializes the {@link Voucher} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0 + ): [Voucher, number] { + return Voucher.deserialize(accountInfo.data, offset) + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link Voucher} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey + ): Promise { + const accountInfo = await connection.getAccountInfo(address) + if (accountInfo == null) { + throw new Error(`Unable to find Voucher account at ${address}`) + } + return Voucher.fromAccountInfo(accountInfo, 0)[0] + } + + /** + * Deserializes the {@link Voucher} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [Voucher, number] { + return voucherBeet.deserialize(buf, offset) + } + + /** + * Serializes the {@link Voucher} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return voucherBeet.serialize({ + accountDiscriminator: voucherDiscriminator, + ...this, + }) + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link Voucher} + */ + static get byteSize() { + return voucherBeet.byteSize + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link Voucher} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment + ): Promise { + return connection.getMinimumBalanceForRentExemption( + Voucher.byteSize, + commitment + ) + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link Voucher} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === Voucher.byteSize + } + + /** + * Returns a readable version of {@link Voucher} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + leafSchema: this.leafSchema, + index: this.index, + merkleSlab: this.merkleSlab.toBase58(), + } + } +} + +/** + * @category Accounts + * @category generated + */ +export const voucherBeet = new beet.BeetStruct< + Voucher, + VoucherArgs & { + accountDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['accountDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['leafSchema', leafSchemaBeet], + ['index', beet.u32], + ['merkleSlab', beetSolana.publicKey], + ], + Voucher.fromArgs, + 'Voucher' +) diff --git a/contracts/sdk/bubblegum/src/generated/accounts/index.ts b/contracts/sdk/bubblegum/src/generated/accounts/index.ts new file mode 100644 index 00000000000..7dfec8ad134 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/accounts/index.ts @@ -0,0 +1,2 @@ +export * from './Nonce' +export * from './Voucher' diff --git a/contracts/sdk/bubblegum/src/generated/index.ts b/contracts/sdk/bubblegum/src/generated/index.ts new file mode 100644 index 00000000000..31289687610 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/index.ts @@ -0,0 +1,20 @@ +import { PublicKey } from '@solana/web3.js' +export * from './accounts' +export * from './instructions' +export * from './types' + +/** + * Program address + * + * @category constants + * @category generated + */ +export const PROGRAM_ADDRESS = 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + +/** + * Program public key + * + * @category constants + * @category generated + */ +export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS) diff --git a/contracts/sdk/bubblegum/src/generated/instructions/burn.ts b/contracts/sdk/bubblegum/src/generated/instructions/burn.ts new file mode 100644 index 00000000000..2fd95e083bb --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/burn.ts @@ -0,0 +1,124 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { Version, versionBeet } from '../types/Version' + +/** + * @category Instructions + * @category Burn + * @category generated + */ +export type BurnInstructionArgs = { + version: Version + root: number[] /* size: 32 */ + dataHash: number[] /* size: 32 */ + creatorHash: number[] /* size: 32 */ + nonce: beet.bignum + index: number +} +/** + * @category Instructions + * @category Burn + * @category generated + */ +export const burnStruct = new beet.BeetArgsStruct< + BurnInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['version', versionBeet], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['dataHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['creatorHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['nonce', beet.u128], + ['index', beet.u32], + ], + 'BurnInstructionArgs' +) +/** + * Accounts required by the _burn_ instruction + * + * @property [] authority + * @property [] gummyrollProgram + * @property [] owner + * @property [] delegate + * @property [_writable_] merkleSlab + * @category Instructions + * @category Burn + * @category generated + */ +export type BurnInstructionAccounts = { + authority: web3.PublicKey + gummyrollProgram: web3.PublicKey + owner: web3.PublicKey + delegate: web3.PublicKey + merkleSlab: web3.PublicKey +} + +export const burnInstructionDiscriminator = [116, 110, 29, 56, 107, 219, 42, 93] + +/** + * Creates a _Burn_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Burn + * @category generated + */ +export function createBurnInstruction( + accounts: BurnInstructionAccounts, + args: BurnInstructionArgs +) { + const { authority, gummyrollProgram, owner, delegate, merkleSlab } = accounts + + const [data] = burnStruct.serialize({ + instructionDiscriminator: burnInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: false, + isSigner: false, + }, + { + pubkey: delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/cancelRedeem.ts b/contracts/sdk/bubblegum/src/generated/instructions/cancelRedeem.ts new file mode 100644 index 00000000000..57aeea7e2f5 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/cancelRedeem.ts @@ -0,0 +1,115 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category CancelRedeem + * @category generated + */ +export type CancelRedeemInstructionArgs = { + root: number[] /* size: 32 */ +} +/** + * @category Instructions + * @category CancelRedeem + * @category generated + */ +export const cancelRedeemStruct = new beet.BeetArgsStruct< + CancelRedeemInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ], + 'CancelRedeemInstructionArgs' +) +/** + * Accounts required by the _cancelRedeem_ instruction + * + * @property [] authority + * @property [] gummyrollProgram + * @property [_writable_] merkleSlab + * @property [_writable_] voucher + * @property [_writable_, **signer**] owner + * @category Instructions + * @category CancelRedeem + * @category generated + */ +export type CancelRedeemInstructionAccounts = { + authority: web3.PublicKey + gummyrollProgram: web3.PublicKey + merkleSlab: web3.PublicKey + voucher: web3.PublicKey + owner: web3.PublicKey +} + +export const cancelRedeemInstructionDiscriminator = [ + 111, 76, 232, 50, 39, 175, 48, 242, +] + +/** + * Creates a _CancelRedeem_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category CancelRedeem + * @category generated + */ +export function createCancelRedeemInstruction( + accounts: CancelRedeemInstructionAccounts, + args: CancelRedeemInstructionArgs +) { + const { authority, gummyrollProgram, merkleSlab, voucher, owner } = accounts + + const [data] = cancelRedeemStruct.serialize({ + instructionDiscriminator: cancelRedeemInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + { + pubkey: voucher, + isWritable: true, + isSigner: false, + }, + { + pubkey: owner, + isWritable: true, + isSigner: true, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/compress.ts b/contracts/sdk/bubblegum/src/generated/instructions/compress.ts new file mode 100644 index 00000000000..1888ae1943d --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/compress.ts @@ -0,0 +1,163 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from '@solana/spl-token' +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category Compress + * @category generated + */ +export const compressStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number[] /* size: 8 */ +}>( + [['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], + 'CompressInstructionArgs' +) +/** + * Accounts required by the _compress_ instruction + * + * @property [] authority + * @property [] merkleSlab + * @property [**signer**] owner + * @property [] delegate + * @property [_writable_] tokenAccount + * @property [_writable_] mint + * @property [_writable_] metadata + * @property [_writable_] masterEdition + * @property [_writable_, **signer**] payer + * @property [] tokenMetadataProgram + * @property [] gummyrollProgram + * @category Instructions + * @category Compress + * @category generated + */ +export type CompressInstructionAccounts = { + authority: web3.PublicKey + merkleSlab: web3.PublicKey + owner: web3.PublicKey + delegate: web3.PublicKey + tokenAccount: web3.PublicKey + mint: web3.PublicKey + metadata: web3.PublicKey + masterEdition: web3.PublicKey + payer: web3.PublicKey + tokenMetadataProgram: web3.PublicKey + gummyrollProgram: web3.PublicKey +} + +export const compressInstructionDiscriminator = [ + 82, 193, 176, 117, 176, 21, 115, 253, +] + +/** + * Creates a _Compress_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category Compress + * @category generated + */ +export function createCompressInstruction( + accounts: CompressInstructionAccounts +) { + const { + authority, + merkleSlab, + owner, + delegate, + tokenAccount, + mint, + metadata, + masterEdition, + payer, + tokenMetadataProgram, + gummyrollProgram, + } = accounts + + const [data] = compressStruct.serialize({ + instructionDiscriminator: compressInstructionDiscriminator, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: false, + isSigner: true, + }, + { + pubkey: delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: tokenAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: mint, + isWritable: true, + isSigner: false, + }, + { + pubkey: metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: masterEdition, + isWritable: true, + isSigner: false, + }, + { + pubkey: payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: tokenMetadataProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/createTree.ts b/contracts/sdk/bubblegum/src/generated/instructions/createTree.ts new file mode 100644 index 00000000000..f794dd828a2 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/createTree.ts @@ -0,0 +1,110 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category CreateTree + * @category generated + */ +export type CreateTreeInstructionArgs = { + maxDepth: number + maxBufferSize: number +} +/** + * @category Instructions + * @category CreateTree + * @category generated + */ +export const createTreeStruct = new beet.BeetArgsStruct< + CreateTreeInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['maxDepth', beet.u32], + ['maxBufferSize', beet.u32], + ], + 'CreateTreeInstructionArgs' +) +/** + * Accounts required by the _createTree_ instruction + * + * @property [**signer**] treeCreator + * @property [] authority + * @property [] gummyrollProgram + * @property [_writable_] merkleSlab + * @category Instructions + * @category CreateTree + * @category generated + */ +export type CreateTreeInstructionAccounts = { + treeCreator: web3.PublicKey + authority: web3.PublicKey + gummyrollProgram: web3.PublicKey + merkleSlab: web3.PublicKey +} + +export const createTreeInstructionDiscriminator = [ + 165, 83, 136, 142, 89, 202, 47, 220, +] + +/** + * Creates a _CreateTree_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category CreateTree + * @category generated + */ +export function createCreateTreeInstruction( + accounts: CreateTreeInstructionAccounts, + args: CreateTreeInstructionArgs +) { + const { treeCreator, authority, gummyrollProgram, merkleSlab } = accounts + + const [data] = createTreeStruct.serialize({ + instructionDiscriminator: createTreeInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: treeCreator, + isWritable: false, + isSigner: true, + }, + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/decompress.ts b/contracts/sdk/bubblegum/src/generated/instructions/decompress.ts new file mode 100644 index 00000000000..391b008711d --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/decompress.ts @@ -0,0 +1,173 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from '@solana/spl-token' +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { MetadataArgs, metadataArgsBeet } from '../types/MetadataArgs' + +/** + * @category Instructions + * @category Decompress + * @category generated + */ +export type DecompressInstructionArgs = { + metadata: MetadataArgs +} +/** + * @category Instructions + * @category Decompress + * @category generated + */ +export const decompressStruct = new beet.FixableBeetArgsStruct< + DecompressInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['metadata', metadataArgsBeet], + ], + 'DecompressInstructionArgs' +) +/** + * Accounts required by the _decompress_ instruction + * + * @property [_writable_] voucher + * @property [_writable_, **signer**] owner + * @property [_writable_] tokenAccount + * @property [_writable_] mint + * @property [] mintAuthority + * @property [_writable_] metadata + * @property [_writable_] masterEdition + * @property [] sysvarRent + * @property [] tokenMetadataProgram + * @property [] associatedTokenProgram + * @category Instructions + * @category Decompress + * @category generated + */ +export type DecompressInstructionAccounts = { + voucher: web3.PublicKey + owner: web3.PublicKey + tokenAccount: web3.PublicKey + mint: web3.PublicKey + mintAuthority: web3.PublicKey + metadata: web3.PublicKey + masterEdition: web3.PublicKey + sysvarRent: web3.PublicKey + tokenMetadataProgram: web3.PublicKey + associatedTokenProgram: web3.PublicKey +} + +export const decompressInstructionDiscriminator = [ + 74, 60, 49, 197, 18, 110, 93, 154, +] + +/** + * Creates a _Decompress_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Decompress + * @category generated + */ +export function createDecompressInstruction( + accounts: DecompressInstructionAccounts, + args: DecompressInstructionArgs +) { + const { + voucher, + owner, + tokenAccount, + mint, + mintAuthority, + metadata, + masterEdition, + sysvarRent, + tokenMetadataProgram, + associatedTokenProgram, + } = accounts + + const [data] = decompressStruct.serialize({ + instructionDiscriminator: decompressInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: voucher, + isWritable: true, + isSigner: false, + }, + { + pubkey: owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: tokenAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: mint, + isWritable: true, + isSigner: false, + }, + { + pubkey: mintAuthority, + isWritable: false, + isSigner: false, + }, + { + pubkey: metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: masterEdition, + isWritable: true, + isSigner: false, + }, + { + pubkey: web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: sysvarRent, + isWritable: false, + isSigner: false, + }, + { + pubkey: tokenMetadataProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: associatedTokenProgram, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/delegate.ts b/contracts/sdk/bubblegum/src/generated/instructions/delegate.ts new file mode 100644 index 00000000000..adf9ca68bc8 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/delegate.ts @@ -0,0 +1,140 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { Version, versionBeet } from '../types/Version' + +/** + * @category Instructions + * @category Delegate + * @category generated + */ +export type DelegateInstructionArgs = { + version: Version + root: number[] /* size: 32 */ + dataHash: number[] /* size: 32 */ + creatorHash: number[] /* size: 32 */ + nonce: beet.bignum + index: number +} +/** + * @category Instructions + * @category Delegate + * @category generated + */ +export const delegateStruct = new beet.BeetArgsStruct< + DelegateInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['version', versionBeet], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['dataHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['creatorHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['nonce', beet.u128], + ['index', beet.u32], + ], + 'DelegateInstructionArgs' +) +/** + * Accounts required by the _delegate_ instruction + * + * @property [] authority + * @property [**signer**] owner + * @property [] previousDelegate + * @property [] newDelegate + * @property [] gummyrollProgram + * @property [_writable_] merkleSlab + * @category Instructions + * @category Delegate + * @category generated + */ +export type DelegateInstructionAccounts = { + authority: web3.PublicKey + owner: web3.PublicKey + previousDelegate: web3.PublicKey + newDelegate: web3.PublicKey + gummyrollProgram: web3.PublicKey + merkleSlab: web3.PublicKey +} + +export const delegateInstructionDiscriminator = [ + 90, 147, 75, 178, 85, 88, 4, 137, +] + +/** + * Creates a _Delegate_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Delegate + * @category generated + */ +export function createDelegateInstruction( + accounts: DelegateInstructionAccounts, + args: DelegateInstructionArgs +) { + const { + authority, + owner, + previousDelegate, + newDelegate, + gummyrollProgram, + merkleSlab, + } = accounts + + const [data] = delegateStruct.serialize({ + instructionDiscriminator: delegateInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: false, + isSigner: true, + }, + { + pubkey: previousDelegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: newDelegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/index.ts b/contracts/sdk/bubblegum/src/generated/instructions/index.ts new file mode 100644 index 00000000000..2ba9192b72c --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/index.ts @@ -0,0 +1,10 @@ +export * from './burn' +export * from './cancelRedeem' +export * from './compress' +export * from './createTree' +export * from './decompress' +export * from './delegate' +export * from './initializeNonce' +export * from './mint' +export * from './redeem' +export * from './transfer' diff --git a/contracts/sdk/bubblegum/src/generated/instructions/initializeNonce.ts b/contracts/sdk/bubblegum/src/generated/instructions/initializeNonce.ts new file mode 100644 index 00000000000..bde9fbd1e24 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/initializeNonce.ts @@ -0,0 +1,82 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category InitializeNonce + * @category generated + */ +export const initializeNonceStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number[] /* size: 8 */ +}>( + [['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], + 'InitializeNonceInstructionArgs' +) +/** + * Accounts required by the _initializeNonce_ instruction + * + * @property [_writable_] nonce + * @property [_writable_, **signer**] payer + * @category Instructions + * @category InitializeNonce + * @category generated + */ +export type InitializeNonceInstructionAccounts = { + nonce: web3.PublicKey + payer: web3.PublicKey +} + +export const initializeNonceInstructionDiscriminator = [ + 64, 206, 214, 231, 20, 15, 231, 41, +] + +/** + * Creates a _InitializeNonce_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category InitializeNonce + * @category generated + */ +export function createInitializeNonceInstruction( + accounts: InitializeNonceInstructionAccounts +) { + const { nonce, payer } = accounts + + const [data] = initializeNonceStruct.serialize({ + instructionDiscriminator: initializeNonceInstructionDiscriminator, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: nonce, + isWritable: true, + isSigner: false, + }, + { + pubkey: payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/mint.ts b/contracts/sdk/bubblegum/src/generated/instructions/mint.ts new file mode 100644 index 00000000000..51cdc2a0d3b --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/mint.ts @@ -0,0 +1,141 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { Version, versionBeet } from '../types/Version' +import { MetadataArgs, metadataArgsBeet } from '../types/MetadataArgs' + +/** + * @category Instructions + * @category Mint + * @category generated + */ +export type MintInstructionArgs = { + version: Version + message: MetadataArgs +} +/** + * @category Instructions + * @category Mint + * @category generated + */ +export const mintStruct = new beet.FixableBeetArgsStruct< + MintInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['version', versionBeet], + ['message', metadataArgsBeet], + ], + 'MintInstructionArgs' +) +/** + * Accounts required by the _mint_ instruction + * + * @property [**signer**] mintAuthority + * @property [] authority + * @property [_writable_] nonce + * @property [] gummyrollProgram + * @property [**signer**] owner + * @property [] delegate + * @property [_writable_] merkleSlab + * @category Instructions + * @category Mint + * @category generated + */ +export type MintInstructionAccounts = { + mintAuthority: web3.PublicKey + authority: web3.PublicKey + nonce: web3.PublicKey + gummyrollProgram: web3.PublicKey + owner: web3.PublicKey + delegate: web3.PublicKey + merkleSlab: web3.PublicKey +} + +export const mintInstructionDiscriminator = [ + 51, 57, 225, 47, 182, 146, 137, 166, +] + +/** + * Creates a _Mint_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Mint + * @category generated + */ +export function createMintInstruction( + accounts: MintInstructionAccounts, + args: MintInstructionArgs +) { + const { + mintAuthority, + authority, + nonce, + gummyrollProgram, + owner, + delegate, + merkleSlab, + } = accounts + + const [data] = mintStruct.serialize({ + instructionDiscriminator: mintInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: mintAuthority, + isWritable: false, + isSigner: true, + }, + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: nonce, + isWritable: true, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: false, + isSigner: true, + }, + { + pubkey: delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/redeem.ts b/contracts/sdk/bubblegum/src/generated/instructions/redeem.ts new file mode 100644 index 00000000000..d7dc5342e8a --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/redeem.ts @@ -0,0 +1,139 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { Version, versionBeet } from '../types/Version' + +/** + * @category Instructions + * @category Redeem + * @category generated + */ +export type RedeemInstructionArgs = { + version: Version + root: number[] /* size: 32 */ + dataHash: number[] /* size: 32 */ + creatorHash: number[] /* size: 32 */ + nonce: beet.bignum + index: number +} +/** + * @category Instructions + * @category Redeem + * @category generated + */ +export const redeemStruct = new beet.BeetArgsStruct< + RedeemInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['version', versionBeet], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['dataHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['creatorHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['nonce', beet.u128], + ['index', beet.u32], + ], + 'RedeemInstructionArgs' +) +/** + * Accounts required by the _redeem_ instruction + * + * @property [] authority + * @property [] gummyrollProgram + * @property [_writable_, **signer**] owner + * @property [] delegate + * @property [_writable_] merkleSlab + * @property [_writable_] voucher + * @category Instructions + * @category Redeem + * @category generated + */ +export type RedeemInstructionAccounts = { + authority: web3.PublicKey + gummyrollProgram: web3.PublicKey + owner: web3.PublicKey + delegate: web3.PublicKey + merkleSlab: web3.PublicKey + voucher: web3.PublicKey +} + +export const redeemInstructionDiscriminator = [ + 184, 12, 86, 149, 70, 196, 97, 225, +] + +/** + * Creates a _Redeem_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Redeem + * @category generated + */ +export function createRedeemInstruction( + accounts: RedeemInstructionAccounts, + args: RedeemInstructionArgs +) { + const { authority, gummyrollProgram, owner, delegate, merkleSlab, voucher } = + accounts + + const [data] = redeemStruct.serialize({ + instructionDiscriminator: redeemInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + { + pubkey: voucher, + isWritable: true, + isSigner: false, + }, + { + pubkey: web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/instructions/transfer.ts b/contracts/sdk/bubblegum/src/generated/instructions/transfer.ts new file mode 100644 index 00000000000..45ca7f57d49 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/instructions/transfer.ts @@ -0,0 +1,134 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import { Version, versionBeet } from '../types/Version' + +/** + * @category Instructions + * @category Transfer + * @category generated + */ +export type TransferInstructionArgs = { + version: Version + root: number[] /* size: 32 */ + dataHash: number[] /* size: 32 */ + creatorHash: number[] /* size: 32 */ + nonce: beet.bignum + index: number +} +/** + * @category Instructions + * @category Transfer + * @category generated + */ +export const transferStruct = new beet.BeetArgsStruct< + TransferInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['version', versionBeet], + ['root', beet.uniformFixedSizeArray(beet.u8, 32)], + ['dataHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['creatorHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['nonce', beet.u128], + ['index', beet.u32], + ], + 'TransferInstructionArgs' +) +/** + * Accounts required by the _transfer_ instruction + * + * @property [] authority + * @property [] owner + * @property [] delegate + * @property [] newOwner + * @property [] gummyrollProgram + * @property [_writable_] merkleSlab + * @category Instructions + * @category Transfer + * @category generated + */ +export type TransferInstructionAccounts = { + authority: web3.PublicKey + owner: web3.PublicKey + delegate: web3.PublicKey + newOwner: web3.PublicKey + gummyrollProgram: web3.PublicKey + merkleSlab: web3.PublicKey +} + +export const transferInstructionDiscriminator = [ + 163, 52, 200, 231, 140, 3, 69, 186, +] + +/** + * Creates a _Transfer_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Transfer + * @category generated + */ +export function createTransferInstruction( + accounts: TransferInstructionAccounts, + args: TransferInstructionArgs +) { + const { authority, owner, delegate, newOwner, gummyrollProgram, merkleSlab } = + accounts + + const [data] = transferStruct.serialize({ + instructionDiscriminator: transferInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: authority, + isWritable: false, + isSigner: false, + }, + { + pubkey: owner, + isWritable: false, + isSigner: false, + }, + { + pubkey: delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: newOwner, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyrollProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BGUMzZr2wWfD2yzrXFEWTK2HbdYhqQCP2EZoPEkZBD6o' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/bubblegum/src/generated/types/Collection.ts b/contracts/sdk/bubblegum/src/generated/types/Collection.ts new file mode 100644 index 00000000000..f7402e06465 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/Collection.ts @@ -0,0 +1,26 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js' +import * as beet from '@metaplex-foundation/beet' +import * as beetSolana from '@metaplex-foundation/beet-solana' +export type Collection = { + verified: boolean + key: web3.PublicKey +} + +/** + * @category userTypes + * @category generated + */ +export const collectionBeet = new beet.BeetArgsStruct( + [ + ['verified', beet.bool], + ['key', beetSolana.publicKey], + ], + 'Collection' +) diff --git a/contracts/sdk/bubblegum/src/generated/types/Creator.ts b/contracts/sdk/bubblegum/src/generated/types/Creator.ts new file mode 100644 index 00000000000..59ef0eb0d64 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/Creator.ts @@ -0,0 +1,28 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js' +import * as beetSolana from '@metaplex-foundation/beet-solana' +import * as beet from '@metaplex-foundation/beet' +export type Creator = { + address: web3.PublicKey + verified: boolean + share: number +} + +/** + * @category userTypes + * @category generated + */ +export const creatorBeet = new beet.BeetArgsStruct( + [ + ['address', beetSolana.publicKey], + ['verified', beet.bool], + ['share', beet.u8], + ], + 'Creator' +) diff --git a/contracts/sdk/bubblegum/src/generated/types/InstructionName.ts b/contracts/sdk/bubblegum/src/generated/types/InstructionName.ts new file mode 100644 index 00000000000..1c528f8a3b8 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/InstructionName.ts @@ -0,0 +1,29 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +/** + * @category enums + * @category generated + */ +export enum InstructionName { + Unknown, + Mint, + Redeem, + CancelRedeem, + Transfer, + Delegate, + Decompress, +} + +/** + * @category userTypes + * @category generated + */ +export const instructionNameBeet = beet.fixedScalarEnum( + InstructionName +) as beet.FixedSizeBeet diff --git a/contracts/sdk/bubblegum/src/generated/types/LeafSchema.ts b/contracts/sdk/bubblegum/src/generated/types/LeafSchema.ts new file mode 100644 index 00000000000..87f75f5dccd --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/LeafSchema.ts @@ -0,0 +1,35 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js' +import * as beet from '@metaplex-foundation/beet' +import * as beetSolana from '@metaplex-foundation/beet-solana' +import { Version, versionBeet } from './Version' +export type LeafSchema = { + version: Version + owner: web3.PublicKey + delegate: web3.PublicKey + nonce: beet.bignum + dataHash: number[] /* size: 32 */ + creatorHash: number[] /* size: 32 */ +} + +/** + * @category userTypes + * @category generated + */ +export const leafSchemaBeet = new beet.BeetArgsStruct( + [ + ['version', versionBeet], + ['owner', beetSolana.publicKey], + ['delegate', beetSolana.publicKey], + ['nonce', beet.u128], + ['dataHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ['creatorHash', beet.uniformFixedSizeArray(beet.u8, 32)], + ], + 'LeafSchema' +) diff --git a/contracts/sdk/bubblegum/src/generated/types/MetadataArgs.ts b/contracts/sdk/bubblegum/src/generated/types/MetadataArgs.ts new file mode 100644 index 00000000000..cf5c41ea17b --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/MetadataArgs.ts @@ -0,0 +1,52 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import { TokenStandard, tokenStandardBeet } from './TokenStandard' +import { Collection, collectionBeet } from './Collection' +import { Uses, usesBeet } from './Uses' +import { + TokenProgramVersion, + tokenProgramVersionBeet, +} from './TokenProgramVersion' +import { Creator, creatorBeet } from './Creator' +export type MetadataArgs = { + name: string + symbol: string + uri: string + sellerFeeBasisPoints: number + primarySaleHappened: boolean + isMutable: boolean + editionNonce: beet.COption + tokenStandard: beet.COption + collection: beet.COption + uses: beet.COption + tokenProgramVersion: TokenProgramVersion + creators: Creator[] +} + +/** + * @category userTypes + * @category generated + */ +export const metadataArgsBeet = new beet.FixableBeetArgsStruct( + [ + ['name', beet.utf8String], + ['symbol', beet.utf8String], + ['uri', beet.utf8String], + ['sellerFeeBasisPoints', beet.u16], + ['primarySaleHappened', beet.bool], + ['isMutable', beet.bool], + ['editionNonce', beet.coption(beet.u8)], + ['tokenStandard', beet.coption(tokenStandardBeet)], + ['collection', beet.coption(collectionBeet)], + ['uses', beet.coption(usesBeet)], + ['tokenProgramVersion', tokenProgramVersionBeet], + ['creators', beet.array(creatorBeet)], + ], + 'MetadataArgs' +) diff --git a/contracts/sdk/bubblegum/src/generated/types/TokenProgramVersion.ts b/contracts/sdk/bubblegum/src/generated/types/TokenProgramVersion.ts new file mode 100644 index 00000000000..3da19eb29cb --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/TokenProgramVersion.ts @@ -0,0 +1,24 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +/** + * @category enums + * @category generated + */ +export enum TokenProgramVersion { + Original, + Token2022, +} + +/** + * @category userTypes + * @category generated + */ +export const tokenProgramVersionBeet = beet.fixedScalarEnum( + TokenProgramVersion +) as beet.FixedSizeBeet diff --git a/contracts/sdk/bubblegum/src/generated/types/TokenStandard.ts b/contracts/sdk/bubblegum/src/generated/types/TokenStandard.ts new file mode 100644 index 00000000000..0dddfc94e68 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/TokenStandard.ts @@ -0,0 +1,26 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +/** + * @category enums + * @category generated + */ +export enum TokenStandard { + NonFungible, + FungibleAsset, + Fungible, + NonFungibleEdition, +} + +/** + * @category userTypes + * @category generated + */ +export const tokenStandardBeet = beet.fixedScalarEnum( + TokenStandard +) as beet.FixedSizeBeet diff --git a/contracts/sdk/bubblegum/src/generated/types/UseMethod.ts b/contracts/sdk/bubblegum/src/generated/types/UseMethod.ts new file mode 100644 index 00000000000..3c26540cea1 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/UseMethod.ts @@ -0,0 +1,25 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +/** + * @category enums + * @category generated + */ +export enum UseMethod { + Burn, + Multiple, + Single, +} + +/** + * @category userTypes + * @category generated + */ +export const useMethodBeet = beet.fixedScalarEnum( + UseMethod +) as beet.FixedSizeBeet diff --git a/contracts/sdk/bubblegum/src/generated/types/Uses.ts b/contracts/sdk/bubblegum/src/generated/types/Uses.ts new file mode 100644 index 00000000000..ee3dc2a2de9 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/Uses.ts @@ -0,0 +1,27 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import { UseMethod, useMethodBeet } from './UseMethod' +export type Uses = { + useMethod: UseMethod + remaining: beet.bignum + total: beet.bignum +} + +/** + * @category userTypes + * @category generated + */ +export const usesBeet = new beet.BeetArgsStruct( + [ + ['useMethod', useMethodBeet], + ['remaining', beet.u64], + ['total', beet.u64], + ], + 'Uses' +) diff --git a/contracts/sdk/bubblegum/src/generated/types/Version.ts b/contracts/sdk/bubblegum/src/generated/types/Version.ts new file mode 100644 index 00000000000..f764dee8a07 --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/Version.ts @@ -0,0 +1,24 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +/** + * @category enums + * @category generated + */ +export enum Version { + V0, +} + +/** + * @category userTypes + * @category generated + */ +export const versionBeet = beet.fixedScalarEnum(Version) as beet.FixedSizeBeet< + Version, + Version +> diff --git a/contracts/sdk/bubblegum/src/generated/types/index.ts b/contracts/sdk/bubblegum/src/generated/types/index.ts new file mode 100644 index 00000000000..bde249071ee --- /dev/null +++ b/contracts/sdk/bubblegum/src/generated/types/index.ts @@ -0,0 +1,10 @@ +export * from './Collection' +export * from './Creator' +export * from './InstructionName' +export * from './LeafSchema' +export * from './MetadataArgs' +export * from './TokenProgramVersion' +export * from './TokenStandard' +export * from './UseMethod' +export * from './Uses' +export * from './Version' diff --git a/contracts/sdk/gumball-machine/accounts/index.ts b/contracts/sdk/gumball-machine/accounts/index.ts index db03b446bac..4933ce03bd3 100644 --- a/contracts/sdk/gumball-machine/accounts/index.ts +++ b/contracts/sdk/gumball-machine/accounts/index.ts @@ -1,9 +1,6 @@ -import { - PublicKey -} from '@solana/web3.js'; import * as borsh from 'borsh'; -import { BN } from '@project-serum/anchor'; -import { readPublicKey } from '../../utils'; +import { val } from '../../utils'; +import { GumballMachineHeader, gumballMachineHeaderBeet } from '../src/generated/types/GumballMachineHeader'; /** * Manually create a model for GumballMachine accounts to deserialize manually @@ -13,31 +10,6 @@ export type OnChainGumballMachine = { configData: ConfigData } -export const GUMBALL_MACHINE_HEADER_SIZE = 360; - -type GumballMachineHeader = { - urlBase: Buffer, // [u8; 64] - nameBase: Buffer, // [u8; 32] - symbol: Buffer, // [u8; 8] - sellerFeeBasisPoints: number, // u16 - isMutable: boolean, // u8 - retainAuthority: boolean, // u8 - _padding: Buffer, // [u8; 4], - price: BN, // u64 - goLiveDate: BN, // i64 - mint: PublicKey, - botWallet: PublicKey, - receiver: PublicKey, - authority: PublicKey, - collectionKey: PublicKey, - creatorAddress: PublicKey, - extensionLen: BN, // usize - maxMintSize: BN, // u64 - remaining: BN, // usize - maxItems: BN, // u64 - totalItemsAdded: BN, // usize -} - type ConfigData = { indexArray: Buffer, configLines: Buffer @@ -45,35 +17,17 @@ type ConfigData = { // Deserialize on-chain gumball machine account to OnChainGumballMachine type export function decodeGumballMachine(buffer: Buffer, accountSize: number): OnChainGumballMachine { - let reader = new borsh.BinaryReader(buffer); - - // Deserialize header - let header: GumballMachineHeader = { - urlBase: Buffer.from(reader.readFixedArray(64)), - nameBase: Buffer.from(reader.readFixedArray(32)), - symbol: Buffer.from(reader.readFixedArray(8)), - sellerFeeBasisPoints: reader.readU16(), - isMutable: !!reader.readU8(), - retainAuthority: !!reader.readU8(), - _padding: Buffer.from(reader.readFixedArray(4)), - price: reader.readU64(), - goLiveDate: new BN(reader.readFixedArray(8), null, 'le'), - mint: readPublicKey(reader), - botWallet: readPublicKey(reader), - receiver: readPublicKey(reader), - authority: readPublicKey(reader), - collectionKey: readPublicKey(reader), - creatorAddress: readPublicKey(reader), - extensionLen: new BN(reader.readFixedArray(8), null, 'le'), // Assume 8 byte size of usize...technically could break - maxMintSize: reader.readU64(), - remaining: new BN(reader.readFixedArray(8), null, 'le'), // Assume 8 byte size of usize...technically could break - maxItems: reader.readU64(), - totalItemsAdded: new BN(reader.readFixedArray(8), null, 'le'), // Assume 8 byte size of usize...technically could break - }; + let header: GumballMachineHeader; + let postHeaderOffset: number; + [header, postHeaderOffset] = gumballMachineHeaderBeet.deserialize(buffer); // Deserailize indices and config section - let numIndexArrayBytes = header.maxItems.toNumber() * 4; - let numConfigBytes = header.extensionLen.toNumber() * header.maxItems.toNumber(); + let reader = new borsh.BinaryReader(Buffer.from(buffer)); + + // Read past the header bytes, it would be cleaner to start the buffer at the postHeaderOffset, but could not quickly find right functions + reader.readFixedArray(postHeaderOffset); + let numIndexArrayBytes = 4 * val(header.maxItems).toNumber(); + let numConfigBytes = val(header.extensionLen).toNumber() * val(header.maxItems).toNumber(); let configData: ConfigData = { indexArray: Buffer.from(reader.readFixedArray(numIndexArrayBytes)), configLines: Buffer.from(reader.readFixedArray(numConfigBytes)), diff --git a/contracts/sdk/gumball-machine/idl/gumball_machine.json b/contracts/sdk/gumball-machine/idl/gumball_machine.json new file mode 100644 index 00000000000..c101a58ad24 --- /dev/null +++ b/contracts/sdk/gumball-machine/idl/gumball_machine.json @@ -0,0 +1,583 @@ +{ + "version": "0.1.0", + "name": "gumball_machine", + "instructions": [ + { + "name": "initializeGumballMachine", + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "creator", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "willyWonka", + "isMut": false, + "isSigner": false + }, + { + "name": "bubblegumAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "gummyroll", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + }, + { + "name": "bubblegum", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "maxDepth", + "type": "u32" + }, + { + "name": "maxBufferSize", + "type": "u32" + }, + { + "name": "urlBase", + "type": { + "array": [ + "u8", + 64 + ] + } + }, + { + "name": "nameBase", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "symbol", + "type": { + "array": [ + "u8", + 8 + ] + } + }, + { + "name": "sellerFeeBasisPoints", + "type": "u16" + }, + { + "name": "isMutable", + "type": "bool" + }, + { + "name": "retainAuthority", + "type": "bool" + }, + { + "name": "price", + "type": "u64" + }, + { + "name": "goLiveDate", + "type": "i64" + }, + { + "name": "botWallet", + "type": "publicKey" + }, + { + "name": "receiver", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "collectionKey", + "type": "publicKey" + }, + { + "name": "extensionLen", + "type": "u64" + }, + { + "name": "maxMintSize", + "type": "u64" + }, + { + "name": "maxItems", + "type": "u64" + } + ] + }, + { + "name": "addConfigLines", + "docs": [ + "Add can only append config lines to the the end of the list" + ], + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "newConfigLinesData", + "type": "bytes" + } + ] + }, + { + "name": "updateConfigLines", + "docs": [ + "Update only allows the authority to modify previously appended lines" + ], + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "startingLine", + "type": "u64" + }, + { + "name": "newConfigLinesData", + "type": "bytes" + } + ] + }, + { + "name": "updateHeaderMetadata", + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "urlBase", + "type": { + "option": { + "array": [ + "u8", + 64 + ] + } + } + }, + { + "name": "nameBase", + "type": { + "option": { + "array": [ + "u8", + 32 + ] + } + } + }, + { + "name": "symbol", + "type": { + "option": { + "array": [ + "u8", + 8 + ] + } + } + }, + { + "name": "sellerFeeBasisPoints", + "type": { + "option": "u16" + } + }, + { + "name": "isMutable", + "type": { + "option": "bool" + } + }, + { + "name": "retainAuthority", + "type": { + "option": "bool" + } + }, + { + "name": "price", + "type": { + "option": "u64" + } + }, + { + "name": "goLiveDate", + "type": { + "option": "i64" + } + }, + { + "name": "botWallet", + "type": { + "option": "publicKey" + } + }, + { + "name": "authority", + "type": { + "option": "publicKey" + } + }, + { + "name": "maxMintSize", + "type": { + "option": "u64" + } + } + ] + }, + { + "name": "dispenseNftSol", + "docs": [ + "Request to purchase a random NFT from GumballMachine for a specific project.", + "@notice: the project must have specified the native mint (Wrapped SOL) for \"mint\"", + "in its GumballMachineHeader for this method to succeed. If mint is anything", + "else dispense_nft_token should be used." + ], + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "willyWonka", + "isMut": false, + "isSigner": false + }, + { + "name": "recentBlockhashes", + "isMut": false, + "isSigner": false + }, + { + "name": "instructionSysvarAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "bubblegumAuthority", + "isMut": false, + "isSigner": false, + "docs": [ + "This key must sign for all write operations to the NFT Metadata stored in the Merkle slab" + ] + }, + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "gummyroll", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + }, + { + "name": "bubblegum", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "numItems", + "type": "u64" + } + ] + }, + { + "name": "dispenseNftToken", + "docs": [ + "Request to purchase a random NFT from GumballMachine for a specific project.", + "@notice: the project's mint may be any valid Mint account EXCEPT for Wrapped SOL", + "if the mint is Wrapped SOL then dispense_token_sol should be used, as the", + "project is seeking native SOL as payment." + ], + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": false, + "isSigner": true + }, + { + "name": "payerTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "receiver", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "willyWonka", + "isMut": false, + "isSigner": false + }, + { + "name": "recentBlockhashes", + "isMut": false, + "isSigner": false + }, + { + "name": "instructionSysvarAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "bubblegumAuthority", + "isMut": false, + "isSigner": false, + "docs": [ + "This key must sign for all write operations to the NFT Metadata stored in the Merkle slab" + ] + }, + { + "name": "nonce", + "isMut": true, + "isSigner": false + }, + { + "name": "gummyroll", + "isMut": false, + "isSigner": false + }, + { + "name": "merkleSlab", + "isMut": true, + "isSigner": false + }, + { + "name": "bubblegum", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "numItems", + "type": "u64" + } + ] + }, + { + "name": "destroy", + "docs": [ + "Reclaim gumball_machine lamports to authority" + ], + "accounts": [ + { + "name": "gumballMachine", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": true, + "isSigner": true + } + ], + "args": [] + } + ], + "types": [ + { + "name": "GumballMachineHeader", + "type": { + "kind": "struct", + "fields": [ + { + "name": "urlBase", + "type": { + "array": [ + "u8", + 64 + ] + } + }, + { + "name": "nameBase", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "symbol", + "type": { + "array": [ + "u8", + 8 + ] + } + }, + { + "name": "sellerFeeBasisPoints", + "type": "u16" + }, + { + "name": "isMutable", + "type": "u8" + }, + { + "name": "retainAuthority", + "type": "u8" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "price", + "type": "u64" + }, + { + "name": "goLiveDate", + "type": "i64" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "botWallet", + "type": "publicKey" + }, + { + "name": "receiver", + "type": "publicKey" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "collectionKey", + "type": "publicKey" + }, + { + "name": "creatorAddress", + "type": "publicKey" + }, + { + "name": "extensionLen", + "type": "u64" + }, + { + "name": "maxMintSize", + "type": "u64" + }, + { + "name": "remaining", + "type": "u64" + }, + { + "name": "maxItems", + "type": "u64" + }, + { + "name": "totalItemsAdded", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "address": "BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt" + } +} \ No newline at end of file diff --git a/contracts/sdk/gumball-machine/index.ts b/contracts/sdk/gumball-machine/index.ts index bf5086a4f0a..8d6b9ea50d2 100644 --- a/contracts/sdk/gumball-machine/index.ts +++ b/contracts/sdk/gumball-machine/index.ts @@ -2,3 +2,4 @@ export * from './instructions'; export * from './accounts'; export * from './types'; export * from './utils'; +export * from './src/generated/index' diff --git a/contracts/sdk/gumball-machine/instructions/index.ts b/contracts/sdk/gumball-machine/instructions/index.ts index 84f28fb25e7..6f323925e72 100644 --- a/contracts/sdk/gumball-machine/instructions/index.ts +++ b/contracts/sdk/gumball-machine/instructions/index.ts @@ -8,24 +8,23 @@ import { SYSVAR_SLOT_HASHES_PUBKEY, TransactionInstruction, } from "@solana/web3.js"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - NATIVE_MINT - } from "@solana/spl-token"; import { GumballMachine, - InitGumballMachineProps } from '../types'; +import { + InitializeGumballMachineInstructionArgs, + createInitializeGumballMachineInstruction, + createDispenseNftSolInstruction, + createDispenseNftTokenInstruction +} from "../src/generated"; import { getWillyWonkaPDAKey, getBubblegumAuthorityPDAKey } from '../utils'; /** - * Client side function to faciliate the creation of instructions for: initialize_gumball_machine - * Handles the creation of merkle roll + gumball machine accounts and the initialization of the gumball machine header - * with props from InitGumballMachineProps -> see ../types/index.ts for details + * Wrapper on top of Solita's createInitializeGumballMachineInstruction + * Produces a series of instructions to create the merkle roll + gumball machine accounts and initialize gumball machine * */ export async function createInitializeGumballMachineIxs( payer: Keypair, @@ -33,9 +32,8 @@ export async function createInitializeGumballMachineIxs( gumballMachineAcctSize: number, merkleRollKeypair: Keypair, merkleRollAccountSize: number, - desiredGumballMachineHeader: InitGumballMachineProps, - maxDepth: number, - maxBufferSize: number, + gumballMachineInitArgs: InitializeGumballMachineInstructionArgs, + mint: PublicKey, gummyrollProgramId: PublicKey, bubblegumProgramId: PublicKey, gumballMachine: Program @@ -65,77 +63,24 @@ export async function createInitializeGumballMachineIxs( const willyWonkaPDAKey = await getWillyWonkaPDAKey(gumballMachineAcctKeypair.publicKey, gumballMachine.programId); const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDAKey(merkleRollKeypair.publicKey, bubblegumProgramId); - const initGumballMachineInstr = gumballMachine.instruction.initializeGumballMachine( - maxDepth, - maxBufferSize, - desiredGumballMachineHeader.urlBase, - desiredGumballMachineHeader.nameBase, - desiredGumballMachineHeader.symbol, - desiredGumballMachineHeader.sellerFeeBasisPoints, - desiredGumballMachineHeader.isMutable, - desiredGumballMachineHeader.retainAuthority, - desiredGumballMachineHeader.price, - desiredGumballMachineHeader.goLiveDate, - desiredGumballMachineHeader.botWallet, - desiredGumballMachineHeader.receiver, - desiredGumballMachineHeader.authority, - desiredGumballMachineHeader.collectionKey, - desiredGumballMachineHeader.extensionLen, - desiredGumballMachineHeader.maxMintSize, - desiredGumballMachineHeader.maxItems, + const initGumballMachineInstr = createInitializeGumballMachineInstruction( { - accounts: { - gumballMachine: gumballMachineAcctKeypair.publicKey, - creator: payer.publicKey, - mint: desiredGumballMachineHeader.mint, - willyWonka: willyWonkaPDAKey, - bubblegumAuthority: bubblegumAuthorityPDAKey, - gummyroll: gummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - bubblegum: bubblegumProgramId - }, - signers: [payer], - } + gumballMachine: gumballMachineAcctKeypair.publicKey, + creator: payer.publicKey, + mint, + willyWonka: willyWonkaPDAKey, + bubblegumAuthority: bubblegumAuthorityPDAKey, + gummyroll: gummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + bubblegum: bubblegumProgramId + }, + gumballMachineInitArgs ); return [allocGumballMachineAcctInstr, allocMerkleRollAcctInstr, initGumballMachineInstr]; } /** - * Client side function to create instruction for: update_header_metadata - * Enables the gumball machine authority to update config parameters in the GumballMachine header - * */ -export function createUpdateHeaderMetadataIx( - authority: Keypair, - gumballMachineAcctKey: PublicKey, - newHeader: InitGumballMachineProps, - gumballMachine: Program -): TransactionInstruction { - const updateHeaderMetadataInstr = gumballMachine.instruction.updateHeaderMetadata( - newHeader.urlBase, - newHeader.nameBase, - newHeader.symbol, - newHeader.sellerFeeBasisPoints, - newHeader.isMutable, - newHeader.retainAuthority, - newHeader.price, - newHeader.goLiveDate, - newHeader.botWallet, - newHeader.authority, - newHeader.maxMintSize, - { - accounts: { - gumballMachine: gumballMachineAcctKey, - authority: authority.publicKey - }, - signers: [authority] - } - ); - return updateHeaderMetadataInstr; -} - -/** - * Client side function to create instruction: dispense_nft_sol. - * Enables payer to purchase a compressed NFT from a project seeking SOL + * Wrapper on top of Solita's createDispenseNftSolInstruction. Automatically fetches necessary PDA keys for instruction * */ export async function createDispenseNFTForSolIx( numNFTs: BN, @@ -150,32 +95,29 @@ export async function createDispenseNFTForSolIx( ): Promise { const willyWonkaPDAKey = await getWillyWonkaPDAKey(gumballMachineAcctKeypair.publicKey, gumballMachine.programId); const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDAKey(merkleRollKeypair.publicKey, bubblegumProgramId); - const dispenseInstr = gumballMachine.instruction.dispenseNftSol( - numNFTs, - { - accounts: { - gumballMachine: gumballMachineAcctKeypair.publicKey, - payer: payer.publicKey, - receiver: receiver, - systemProgram: SystemProgram.programId, - willyWonka: willyWonkaPDAKey, - recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY, - instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY, - bubblegumAuthority: bubblegumAuthorityPDAKey, - nonce: noncePDAKey, - gummyroll: gummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - bubblegum: bubblegumProgramId - }, - signers: [payer] - } + const dispenseInstr = createDispenseNftSolInstruction( + { + gumballMachine: gumballMachineAcctKeypair.publicKey, + payer: payer.publicKey, + receiver: receiver, + willyWonka: willyWonkaPDAKey, + recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY, + instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY, + bubblegumAuthority: bubblegumAuthorityPDAKey, + nonce: noncePDAKey, + gummyroll: gummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + bubblegum: bubblegumProgramId + }, + { + numItems: numNFTs + } ); return dispenseInstr; } /** - * Client side function to create instruction: dispense_nft_token. - * Enables payer to purchase a compressed NFT from a project seeking SPL tokens + * Wrapper on top of Solita's createDispenseNftTokenInstruction. Automatically fetches necessary PDA keys for instruction * */ export async function createDispenseNFTForTokensIx( numNFTs: BN, @@ -191,96 +133,24 @@ export async function createDispenseNFTForTokensIx( ): Promise { const willyWonkaPDAKey = await getWillyWonkaPDAKey(gumballMachineAcctKeypair.publicKey, gumballMachine.programId); const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDAKey(merkleRollKeypair.publicKey, bubblegumProgramId); - const dispenseInstr = gumballMachine.instruction.dispenseNftToken( - numNFTs, + const dispenseInstr = createDispenseNftTokenInstruction( { - accounts: { - gumballMachine: gumballMachineAcctKeypair.publicKey, - payer: payer.publicKey, - payerTokens, - receiver, - tokenProgram: TOKEN_PROGRAM_ID, - willyWonka: willyWonkaPDAKey, - recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY, - instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY, - bubblegumAuthority: bubblegumAuthorityPDAKey, - nonce: noncePDAKey, - gummyroll: gummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - bubblegum: bubblegumProgramId - }, - signers: [payer] - } - ); - return dispenseInstr; -} - -/** - * Client side function to create instruction: add_config_lines. - * Enables gumballMachine authority to add config lines -> compressed NFTs that can be minted - * */ -export function createAddConfigLinesIx( - authority: Keypair, - gumballMachineAcctKey: PublicKey, - configLinesToAdd: Buffer, - gumballMachine: Program -): TransactionInstruction { - const addConfigLinesInstr = gumballMachine.instruction.addConfigLines( - configLinesToAdd, - { - accounts: { - gumballMachine: gumballMachineAcctKey, - authority: authority.publicKey - }, - signers: [authority] - } - ) - return addConfigLinesInstr; -} - -/** - * Client side function to create instruction: destroy - * Enables authority for a GumballMachine to pull all lamports out of their GumballMachine account - * effectively "destroying" the GumballMachine for use and returning their funds - * */ -export function createDestroyGumballMachineIx( - gumballMachineAcctKeypair: Keypair, - authorityKeypair: Keypair, - gumballMachine: Program - ): TransactionInstruction { - const destroyInstr = gumballMachine.instruction.destroy( - { - accounts: { - gumballMachine: gumballMachineAcctKeypair.publicKey, - authority: authorityKeypair.publicKey + gumballMachine: gumballMachineAcctKeypair.publicKey, + payer: payer.publicKey, + payerTokens, + receiver, + willyWonka: willyWonkaPDAKey, + recentBlockhashes: SYSVAR_SLOT_HASHES_PUBKEY, + instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY, + bubblegumAuthority: bubblegumAuthorityPDAKey, + nonce: noncePDAKey, + gummyroll: gummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + bubblegum: bubblegumProgramId }, - signers: [authorityKeypair] - } - ); - return destroyInstr; -} - -/** - * Client side function to create instruction: update_config_lines - * Enables authority to update the data stored in certain config lines which have already been added to their GumballMachine - * */ -export function createUpdateConfigLinesIx( - authority: Keypair, - gumballMachineAcctKey: PublicKey, - updatedConfigLines: Buffer, - indexOfFirstLineToUpdate: BN, - gumballMachine: Program -): TransactionInstruction { - const updateConfigLinesInstr = gumballMachine.instruction.updateConfigLines( - indexOfFirstLineToUpdate, - updatedConfigLines, { - accounts: { - gumballMachine: gumballMachineAcctKey, - authority: authority.publicKey - }, - signers: [authority] + numItems: numNFTs } ); - return updateConfigLinesInstr; + return dispenseInstr; } \ No newline at end of file diff --git a/contracts/sdk/gumball-machine/package.json b/contracts/sdk/gumball-machine/package.json new file mode 100644 index 00000000000..62afbda5064 --- /dev/null +++ b/contracts/sdk/gumball-machine/package.json @@ -0,0 +1,10 @@ +{ + "name": "gumball-machine", + "version": "1.0.0", + "main": "index.ts", + "license": "MIT", + "dependencies": { + "@metaplex-foundation/rustbin": "^0.3.1", + "@metaplex-foundation/solita": "^0.8.2" + } +} diff --git a/contracts/sdk/gumball-machine/solita.js b/contracts/sdk/gumball-machine/solita.js new file mode 100644 index 00000000000..4131eaec3c0 --- /dev/null +++ b/contracts/sdk/gumball-machine/solita.js @@ -0,0 +1,67 @@ +const path = require('path'); +const { + rustbinMatch, + confirmAutoMessageConsole, +} = require('@metaplex-foundation/rustbin') +const { spawn } = require('child_process'); +const { Solita } = require('@metaplex-foundation/solita'); +const { writeFile } = require('fs/promises'); +const { fstat, existsSync, realpath, realpathSync } = require('fs'); + +const PROGRAM_NAME = 'gumball-machine'; +const PROGRAM_ID = 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt'; + +const programDir = path.join(__dirname, '..', '..', 'programs', 'gumball-machine'); +const cargoToml = path.join(programDir, 'Cargo.toml') +const generatedIdlDir = path.join(__dirname, 'idl'); +const generatedSDKDir = path.join(__dirname, 'src', 'generated'); +const rootDir = path.join(__dirname, '.crates') + +async function main() { + const anchorExecutable = realpathSync("../../../deps/anchor/target/debug/anchor"); + // const anchorExecutable = ("~/Documents/core/candyland/deps/anchor/target/debug/anchor").replace("~", process.env.HOME); + if (!existsSync(anchorExecutable)) { + console.log(`Could not find: ${anchorExecutable}`); + throw new Error("Please `cd candyland/deps/anchor/anchor-cli` && cargo build`") + } + const anchor = spawn(anchorExecutable, ['build', '--idl', generatedIdlDir], { cwd: programDir }) + .on('error', (err) => { + console.error(err); + // @ts-ignore this err does have a code + if (err.code === 'ENOENT') { + console.error( + 'Ensure that `anchor` is installed and in your path, see:\n https://project-serum.github.io/anchor/getting-started/installation.html#install-anchor\n', + ); + } + process.exit(1); + }) + .on('exit', () => { + console.log('IDL written to: %s', path.join(generatedIdlDir, `${PROGRAM_NAME.replace("-", '_')}.json`)); + generateTypeScriptSDK(); + }); + + anchor.stdout.on('data', (buf) => console.log(buf.toString('utf8'))); + anchor.stderr.on('data', (buf) => console.error(buf.toString('utf8'))); +} + +async function generateTypeScriptSDK() { + console.error('Generating TypeScript SDK to %s', generatedSDKDir); + const generatedIdlPath = path.join(generatedIdlDir, `${PROGRAM_NAME.replace("-", "_")}.json`); + + const idl = require(generatedIdlPath); + if (idl.metadata?.address == null) { + idl.metadata = { ...idl.metadata, address: PROGRAM_ID }; + await writeFile(generatedIdlPath, JSON.stringify(idl, null, 2)); + } + const gen = new Solita(idl, { formatCode: true }); + await gen.renderAndWriteTo(generatedSDKDir); + + console.error('Success!'); + + process.exit(0); +} + +main().catch((err) => { + console.error(err) + process.exit(1) +}) diff --git a/contracts/sdk/gumball-machine/src/generated/index.ts b/contracts/sdk/gumball-machine/src/generated/index.ts new file mode 100644 index 00000000000..41c9d408634 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/index.ts @@ -0,0 +1,19 @@ +import { PublicKey } from '@solana/web3.js' +export * from './instructions' +export * from './types' + +/** + * Program address + * + * @category constants + * @category generated + */ +export const PROGRAM_ADDRESS = 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + +/** + * Program public key + * + * @category constants + * @category generated + */ +export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS) diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/addConfigLines.ts b/contracts/sdk/gumball-machine/src/generated/instructions/addConfigLines.ts new file mode 100644 index 00000000000..eedc4afe9ed --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/addConfigLines.ts @@ -0,0 +1,94 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category AddConfigLines + * @category generated + */ +export type AddConfigLinesInstructionArgs = { + newConfigLinesData: Uint8Array +} +/** + * @category Instructions + * @category AddConfigLines + * @category generated + */ +export const addConfigLinesStruct = new beet.FixableBeetArgsStruct< + AddConfigLinesInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['newConfigLinesData', beet.bytes], + ], + 'AddConfigLinesInstructionArgs' +) +/** + * Accounts required by the _addConfigLines_ instruction + * + * @property [_writable_] gumballMachine + * @property [**signer**] authority + * @category Instructions + * @category AddConfigLines + * @category generated + */ +export type AddConfigLinesInstructionAccounts = { + gumballMachine: web3.PublicKey + authority: web3.PublicKey +} + +export const addConfigLinesInstructionDiscriminator = [ + 223, 50, 224, 227, 151, 8, 115, 106, +] + +/** + * Creates a _AddConfigLines_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category AddConfigLines + * @category generated + */ +export function createAddConfigLinesInstruction( + accounts: AddConfigLinesInstructionAccounts, + args: AddConfigLinesInstructionArgs +) { + const { gumballMachine, authority } = accounts + + const [data] = addConfigLinesStruct.serialize({ + instructionDiscriminator: addConfigLinesInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: authority, + isWritable: false, + isSigner: true, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/destroy.ts b/contracts/sdk/gumball-machine/src/generated/instructions/destroy.ts new file mode 100644 index 00000000000..54c5d33811f --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/destroy.ts @@ -0,0 +1,75 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category Destroy + * @category generated + */ +export const destroyStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number[] /* size: 8 */ +}>( + [['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)]], + 'DestroyInstructionArgs' +) +/** + * Accounts required by the _destroy_ instruction + * + * @property [_writable_] gumballMachine + * @property [_writable_, **signer**] authority + * @category Instructions + * @category Destroy + * @category generated + */ +export type DestroyInstructionAccounts = { + gumballMachine: web3.PublicKey + authority: web3.PublicKey +} + +export const destroyInstructionDiscriminator = [ + 157, 40, 96, 3, 135, 203, 143, 74, +] + +/** + * Creates a _Destroy_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category Destroy + * @category generated + */ +export function createDestroyInstruction(accounts: DestroyInstructionAccounts) { + const { gumballMachine, authority } = accounts + + const [data] = destroyStruct.serialize({ + instructionDiscriminator: destroyInstructionDiscriminator, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: authority, + isWritable: true, + isSigner: true, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftSol.ts b/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftSol.ts new file mode 100644 index 00000000000..d133fc0897e --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftSol.ts @@ -0,0 +1,174 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category DispenseNftSol + * @category generated + */ +export type DispenseNftSolInstructionArgs = { + numItems: beet.bignum +} +/** + * @category Instructions + * @category DispenseNftSol + * @category generated + */ +export const dispenseNftSolStruct = new beet.BeetArgsStruct< + DispenseNftSolInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['numItems', beet.u64], + ], + 'DispenseNftSolInstructionArgs' +) +/** + * Accounts required by the _dispenseNftSol_ instruction + * + * @property [_writable_] gumballMachine + * @property [_writable_, **signer**] payer + * @property [_writable_] receiver + * @property [] willyWonka + * @property [] recentBlockhashes + * @property [] instructionSysvarAccount + * @property [] bubblegumAuthority + * @property [_writable_] nonce + * @property [] gummyroll + * @property [_writable_] merkleSlab + * @property [] bubblegum + * @category Instructions + * @category DispenseNftSol + * @category generated + */ +export type DispenseNftSolInstructionAccounts = { + gumballMachine: web3.PublicKey + payer: web3.PublicKey + receiver: web3.PublicKey + willyWonka: web3.PublicKey + recentBlockhashes: web3.PublicKey + instructionSysvarAccount: web3.PublicKey + bubblegumAuthority: web3.PublicKey + nonce: web3.PublicKey + gummyroll: web3.PublicKey + merkleSlab: web3.PublicKey + bubblegum: web3.PublicKey +} + +export const dispenseNftSolInstructionDiscriminator = [ + 156, 55, 115, 151, 225, 40, 172, 61, +] + +/** + * Creates a _DispenseNftSol_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category DispenseNftSol + * @category generated + */ +export function createDispenseNftSolInstruction( + accounts: DispenseNftSolInstructionAccounts, + args: DispenseNftSolInstructionArgs +) { + const { + gumballMachine, + payer, + receiver, + willyWonka, + recentBlockhashes, + instructionSysvarAccount, + bubblegumAuthority, + nonce, + gummyroll, + merkleSlab, + bubblegum, + } = accounts + + const [data] = dispenseNftSolStruct.serialize({ + instructionDiscriminator: dispenseNftSolInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: receiver, + isWritable: true, + isSigner: false, + }, + { + pubkey: web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: willyWonka, + isWritable: false, + isSigner: false, + }, + { + pubkey: recentBlockhashes, + isWritable: false, + isSigner: false, + }, + { + pubkey: instructionSysvarAccount, + isWritable: false, + isSigner: false, + }, + { + pubkey: bubblegumAuthority, + isWritable: false, + isSigner: false, + }, + { + pubkey: nonce, + isWritable: true, + isSigner: false, + }, + { + pubkey: gummyroll, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + { + pubkey: bubblegum, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftToken.ts b/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftToken.ts new file mode 100644 index 00000000000..fd13e3786e4 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/dispenseNftToken.ts @@ -0,0 +1,183 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from '@solana/spl-token' +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category DispenseNftToken + * @category generated + */ +export type DispenseNftTokenInstructionArgs = { + numItems: beet.bignum +} +/** + * @category Instructions + * @category DispenseNftToken + * @category generated + */ +export const dispenseNftTokenStruct = new beet.BeetArgsStruct< + DispenseNftTokenInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['numItems', beet.u64], + ], + 'DispenseNftTokenInstructionArgs' +) +/** + * Accounts required by the _dispenseNftToken_ instruction + * + * @property [_writable_] gumballMachine + * @property [**signer**] payer + * @property [_writable_] payerTokens + * @property [_writable_] receiver + * @property [] willyWonka + * @property [] recentBlockhashes + * @property [] instructionSysvarAccount + * @property [] bubblegumAuthority + * @property [_writable_] nonce + * @property [] gummyroll + * @property [_writable_] merkleSlab + * @property [] bubblegum + * @category Instructions + * @category DispenseNftToken + * @category generated + */ +export type DispenseNftTokenInstructionAccounts = { + gumballMachine: web3.PublicKey + payer: web3.PublicKey + payerTokens: web3.PublicKey + receiver: web3.PublicKey + willyWonka: web3.PublicKey + recentBlockhashes: web3.PublicKey + instructionSysvarAccount: web3.PublicKey + bubblegumAuthority: web3.PublicKey + nonce: web3.PublicKey + gummyroll: web3.PublicKey + merkleSlab: web3.PublicKey + bubblegum: web3.PublicKey +} + +export const dispenseNftTokenInstructionDiscriminator = [ + 55, 215, 72, 66, 100, 249, 57, 225, +] + +/** + * Creates a _DispenseNftToken_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category DispenseNftToken + * @category generated + */ +export function createDispenseNftTokenInstruction( + accounts: DispenseNftTokenInstructionAccounts, + args: DispenseNftTokenInstructionArgs +) { + const { + gumballMachine, + payer, + payerTokens, + receiver, + willyWonka, + recentBlockhashes, + instructionSysvarAccount, + bubblegumAuthority, + nonce, + gummyroll, + merkleSlab, + bubblegum, + } = accounts + + const [data] = dispenseNftTokenStruct.serialize({ + instructionDiscriminator: dispenseNftTokenInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: payer, + isWritable: false, + isSigner: true, + }, + { + pubkey: payerTokens, + isWritable: true, + isSigner: false, + }, + { + pubkey: receiver, + isWritable: true, + isSigner: false, + }, + { + pubkey: splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: willyWonka, + isWritable: false, + isSigner: false, + }, + { + pubkey: recentBlockhashes, + isWritable: false, + isSigner: false, + }, + { + pubkey: instructionSysvarAccount, + isWritable: false, + isSigner: false, + }, + { + pubkey: bubblegumAuthority, + isWritable: false, + isSigner: false, + }, + { + pubkey: nonce, + isWritable: true, + isSigner: false, + }, + { + pubkey: gummyroll, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + { + pubkey: bubblegum, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/index.ts b/contracts/sdk/gumball-machine/src/generated/instructions/index.ts new file mode 100644 index 00000000000..9a5c377418a --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/index.ts @@ -0,0 +1,7 @@ +export * from './addConfigLines' +export * from './destroy' +export * from './dispenseNftSol' +export * from './dispenseNftToken' +export * from './initializeGumballMachine' +export * from './updateConfigLines' +export * from './updateHeaderMetadata' diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/initializeGumballMachine.ts b/contracts/sdk/gumball-machine/src/generated/instructions/initializeGumballMachine.ts new file mode 100644 index 00000000000..62935efd427 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/initializeGumballMachine.ts @@ -0,0 +1,178 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import * as beetSolana from '@metaplex-foundation/beet-solana' + +/** + * @category Instructions + * @category InitializeGumballMachine + * @category generated + */ +export type InitializeGumballMachineInstructionArgs = { + maxDepth: number + maxBufferSize: number + urlBase: number[] /* size: 64 */ + nameBase: number[] /* size: 32 */ + symbol: number[] /* size: 8 */ + sellerFeeBasisPoints: number + isMutable: boolean + retainAuthority: boolean + price: beet.bignum + goLiveDate: beet.bignum + botWallet: web3.PublicKey + receiver: web3.PublicKey + authority: web3.PublicKey + collectionKey: web3.PublicKey + extensionLen: beet.bignum + maxMintSize: beet.bignum + maxItems: beet.bignum +} +/** + * @category Instructions + * @category InitializeGumballMachine + * @category generated + */ +export const initializeGumballMachineStruct = new beet.BeetArgsStruct< + InitializeGumballMachineInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['maxDepth', beet.u32], + ['maxBufferSize', beet.u32], + ['urlBase', beet.uniformFixedSizeArray(beet.u8, 64)], + ['nameBase', beet.uniformFixedSizeArray(beet.u8, 32)], + ['symbol', beet.uniformFixedSizeArray(beet.u8, 8)], + ['sellerFeeBasisPoints', beet.u16], + ['isMutable', beet.bool], + ['retainAuthority', beet.bool], + ['price', beet.u64], + ['goLiveDate', beet.i64], + ['botWallet', beetSolana.publicKey], + ['receiver', beetSolana.publicKey], + ['authority', beetSolana.publicKey], + ['collectionKey', beetSolana.publicKey], + ['extensionLen', beet.u64], + ['maxMintSize', beet.u64], + ['maxItems', beet.u64], + ], + 'InitializeGumballMachineInstructionArgs' +) +/** + * Accounts required by the _initializeGumballMachine_ instruction + * + * @property [_writable_] gumballMachine + * @property [**signer**] creator + * @property [] mint + * @property [] willyWonka + * @property [] bubblegumAuthority + * @property [] gummyroll + * @property [_writable_] merkleSlab + * @property [] bubblegum + * @category Instructions + * @category InitializeGumballMachine + * @category generated + */ +export type InitializeGumballMachineInstructionAccounts = { + gumballMachine: web3.PublicKey + creator: web3.PublicKey + mint: web3.PublicKey + willyWonka: web3.PublicKey + bubblegumAuthority: web3.PublicKey + gummyroll: web3.PublicKey + merkleSlab: web3.PublicKey + bubblegum: web3.PublicKey +} + +export const initializeGumballMachineInstructionDiscriminator = [ + 206, 247, 43, 231, 98, 153, 124, 110, +] + +/** + * Creates a _InitializeGumballMachine_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category InitializeGumballMachine + * @category generated + */ +export function createInitializeGumballMachineInstruction( + accounts: InitializeGumballMachineInstructionAccounts, + args: InitializeGumballMachineInstructionArgs +) { + const { + gumballMachine, + creator, + mint, + willyWonka, + bubblegumAuthority, + gummyroll, + merkleSlab, + bubblegum, + } = accounts + + const [data] = initializeGumballMachineStruct.serialize({ + instructionDiscriminator: initializeGumballMachineInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: creator, + isWritable: false, + isSigner: true, + }, + { + pubkey: mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: willyWonka, + isWritable: false, + isSigner: false, + }, + { + pubkey: bubblegumAuthority, + isWritable: false, + isSigner: false, + }, + { + pubkey: gummyroll, + isWritable: false, + isSigner: false, + }, + { + pubkey: merkleSlab, + isWritable: true, + isSigner: false, + }, + { + pubkey: bubblegum, + isWritable: false, + isSigner: false, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/updateConfigLines.ts b/contracts/sdk/gumball-machine/src/generated/instructions/updateConfigLines.ts new file mode 100644 index 00000000000..71442e32ad9 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/updateConfigLines.ts @@ -0,0 +1,96 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' + +/** + * @category Instructions + * @category UpdateConfigLines + * @category generated + */ +export type UpdateConfigLinesInstructionArgs = { + startingLine: beet.bignum + newConfigLinesData: Uint8Array +} +/** + * @category Instructions + * @category UpdateConfigLines + * @category generated + */ +export const updateConfigLinesStruct = new beet.FixableBeetArgsStruct< + UpdateConfigLinesInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['startingLine', beet.u64], + ['newConfigLinesData', beet.bytes], + ], + 'UpdateConfigLinesInstructionArgs' +) +/** + * Accounts required by the _updateConfigLines_ instruction + * + * @property [_writable_] gumballMachine + * @property [**signer**] authority + * @category Instructions + * @category UpdateConfigLines + * @category generated + */ +export type UpdateConfigLinesInstructionAccounts = { + gumballMachine: web3.PublicKey + authority: web3.PublicKey +} + +export const updateConfigLinesInstructionDiscriminator = [ + 20, 200, 2, 139, 44, 67, 109, 26, +] + +/** + * Creates a _UpdateConfigLines_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category UpdateConfigLines + * @category generated + */ +export function createUpdateConfigLinesInstruction( + accounts: UpdateConfigLinesInstructionAccounts, + args: UpdateConfigLinesInstructionArgs +) { + const { gumballMachine, authority } = accounts + + const [data] = updateConfigLinesStruct.serialize({ + instructionDiscriminator: updateConfigLinesInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: authority, + isWritable: false, + isSigner: true, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/instructions/updateHeaderMetadata.ts b/contracts/sdk/gumball-machine/src/generated/instructions/updateHeaderMetadata.ts new file mode 100644 index 00000000000..d95f9f66d68 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/instructions/updateHeaderMetadata.ts @@ -0,0 +1,115 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import * as beetSolana from '@metaplex-foundation/beet-solana' + +/** + * @category Instructions + * @category UpdateHeaderMetadata + * @category generated + */ +export type UpdateHeaderMetadataInstructionArgs = { + urlBase: beet.COption + nameBase: beet.COption + symbol: beet.COption + sellerFeeBasisPoints: beet.COption + isMutable: beet.COption + retainAuthority: beet.COption + price: beet.COption + goLiveDate: beet.COption + botWallet: beet.COption + authority: beet.COption + maxMintSize: beet.COption +} +/** + * @category Instructions + * @category UpdateHeaderMetadata + * @category generated + */ +export const updateHeaderMetadataStruct = new beet.FixableBeetArgsStruct< + UpdateHeaderMetadataInstructionArgs & { + instructionDiscriminator: number[] /* size: 8 */ + } +>( + [ + ['instructionDiscriminator', beet.uniformFixedSizeArray(beet.u8, 8)], + ['urlBase', beet.coption(beet.uniformFixedSizeArray(beet.u8, 64))], + ['nameBase', beet.coption(beet.uniformFixedSizeArray(beet.u8, 32))], + ['symbol', beet.coption(beet.uniformFixedSizeArray(beet.u8, 8))], + ['sellerFeeBasisPoints', beet.coption(beet.u16)], + ['isMutable', beet.coption(beet.bool)], + ['retainAuthority', beet.coption(beet.bool)], + ['price', beet.coption(beet.u64)], + ['goLiveDate', beet.coption(beet.i64)], + ['botWallet', beet.coption(beetSolana.publicKey)], + ['authority', beet.coption(beetSolana.publicKey)], + ['maxMintSize', beet.coption(beet.u64)], + ], + 'UpdateHeaderMetadataInstructionArgs' +) +/** + * Accounts required by the _updateHeaderMetadata_ instruction + * + * @property [_writable_] gumballMachine + * @property [**signer**] authority + * @category Instructions + * @category UpdateHeaderMetadata + * @category generated + */ +export type UpdateHeaderMetadataInstructionAccounts = { + gumballMachine: web3.PublicKey + authority: web3.PublicKey +} + +export const updateHeaderMetadataInstructionDiscriminator = [ + 103, 76, 66, 120, 245, 72, 217, 123, +] + +/** + * Creates a _UpdateHeaderMetadata_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category UpdateHeaderMetadata + * @category generated + */ +export function createUpdateHeaderMetadataInstruction( + accounts: UpdateHeaderMetadataInstructionAccounts, + args: UpdateHeaderMetadataInstructionArgs +) { + const { gumballMachine, authority } = accounts + + const [data] = updateHeaderMetadataStruct.serialize({ + instructionDiscriminator: updateHeaderMetadataInstructionDiscriminator, + ...args, + }) + const keys: web3.AccountMeta[] = [ + { + pubkey: gumballMachine, + isWritable: true, + isSigner: false, + }, + { + pubkey: authority, + isWritable: false, + isSigner: true, + }, + ] + + const ix = new web3.TransactionInstruction({ + programId: new web3.PublicKey( + 'BRKyVDRGT7SPBtMhjHN4PVSPVYoc3Wa3QTyuRVM4iZkt' + ), + keys, + data, + }) + return ix +} diff --git a/contracts/sdk/gumball-machine/src/generated/types/GumballMachineHeader.ts b/contracts/sdk/gumball-machine/src/generated/types/GumballMachineHeader.ts new file mode 100644 index 00000000000..032ace3a304 --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/types/GumballMachineHeader.ts @@ -0,0 +1,63 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet' +import * as web3 from '@solana/web3.js' +import * as beetSolana from '@metaplex-foundation/beet-solana' +export type GumballMachineHeader = { + urlBase: number[] /* size: 64 */ + nameBase: number[] /* size: 32 */ + symbol: number[] /* size: 8 */ + sellerFeeBasisPoints: number + isMutable: number + retainAuthority: number + padding: number[] /* size: 4 */ + price: beet.bignum + goLiveDate: beet.bignum + mint: web3.PublicKey + botWallet: web3.PublicKey + receiver: web3.PublicKey + authority: web3.PublicKey + collectionKey: web3.PublicKey + creatorAddress: web3.PublicKey + extensionLen: beet.bignum + maxMintSize: beet.bignum + remaining: beet.bignum + maxItems: beet.bignum + totalItemsAdded: beet.bignum +} + +/** + * @category userTypes + * @category generated + */ +export const gumballMachineHeaderBeet = + new beet.BeetArgsStruct( + [ + ['urlBase', beet.uniformFixedSizeArray(beet.u8, 64)], + ['nameBase', beet.uniformFixedSizeArray(beet.u8, 32)], + ['symbol', beet.uniformFixedSizeArray(beet.u8, 8)], + ['sellerFeeBasisPoints', beet.u16], + ['isMutable', beet.u8], + ['retainAuthority', beet.u8], + ['padding', beet.uniformFixedSizeArray(beet.u8, 4)], + ['price', beet.u64], + ['goLiveDate', beet.i64], + ['mint', beetSolana.publicKey], + ['botWallet', beetSolana.publicKey], + ['receiver', beetSolana.publicKey], + ['authority', beetSolana.publicKey], + ['collectionKey', beetSolana.publicKey], + ['creatorAddress', beetSolana.publicKey], + ['extensionLen', beet.u64], + ['maxMintSize', beet.u64], + ['remaining', beet.u64], + ['maxItems', beet.u64], + ['totalItemsAdded', beet.u64], + ], + 'GumballMachineHeader' + ) diff --git a/contracts/sdk/gumball-machine/src/generated/types/index.ts b/contracts/sdk/gumball-machine/src/generated/types/index.ts new file mode 100644 index 00000000000..9c34097005a --- /dev/null +++ b/contracts/sdk/gumball-machine/src/generated/types/index.ts @@ -0,0 +1 @@ +export * from './GumballMachineHeader' diff --git a/contracts/sdk/gumball-machine/types/index.ts b/contracts/sdk/gumball-machine/types/index.ts index 3f8e8927ea5..5764cfc0605 100644 --- a/contracts/sdk/gumball-machine/types/index.ts +++ b/contracts/sdk/gumball-machine/types/index.ts @@ -1,28 +1 @@ -import { BN } from "@project-serum/anchor"; -import { - PublicKey, -} from "@solana/web3.js"; - -export { GumballMachine } from "../../../target/types/gumball_machine"; - -// @notice: This type is only used to facilitate creating the initialize_gumball_machine instruction on the client side, -// it is not related to deserailizing onchain GumballMachine accounts. See ../instructions/index.ts for usage -export type InitGumballMachineProps = { - urlBase: Buffer, - nameBase: Buffer, - symbol: Buffer, - sellerFeeBasisPoints: number, - isMutable: boolean, - retainAuthority: boolean, - price: BN, - goLiveDate: BN, - mint: PublicKey, - botWallet: PublicKey, - receiver: PublicKey, - authority: PublicKey, - collectionKey: PublicKey, - creatorAddress: PublicKey, - extensionLen: BN, - maxMintSize: BN, - maxItems: BN -} \ No newline at end of file +export { GumballMachine } from "../../../target/types/gumball_machine"; \ No newline at end of file diff --git a/contracts/sdk/utils/index.ts b/contracts/sdk/utils/index.ts index 8ff8ff9c164..e27724e8540 100644 --- a/contracts/sdk/utils/index.ts +++ b/contracts/sdk/utils/index.ts @@ -2,7 +2,24 @@ import { PublicKey } from '@solana/web3.js'; import * as borsh from 'borsh'; +import { bignum } from '@metaplex-foundation/beet' +import { BN } from '@project-serum/anchor'; export function readPublicKey(reader: borsh.BinaryReader): PublicKey { return new PublicKey(reader.readFixedArray(32)); +} + +export function val(num: bignum): BN { + if (BN.isBN(num)) { + return num; + } + return new BN(num); +} + +export function strToByteArray(str: string): number[] { + return [...str].reduce((acc, c, ind) => acc.concat([str.charCodeAt(ind)]), []); +} + +export function strToByteUint8Array(str: string): Uint8Array { + return Uint8Array.from([...str].reduce((acc, c, ind) => acc.concat([str.charCodeAt(ind)]), [])); } \ No newline at end of file diff --git a/contracts/tests/bubblegum-test.ts b/contracts/tests/bubblegum-test.ts index 611328b0d21..fcc32b6685a 100644 --- a/contracts/tests/bubblegum-test.ts +++ b/contracts/tests/bubblegum-test.ts @@ -13,12 +13,21 @@ import { SYSVAR_RENT_PUBKEY, } from "@solana/web3.js"; import { assert } from "chai"; +import { + createMintInstruction, + createDecompressInstruction, + createTransferInstruction, + createDelegateInstruction, + createRedeemInstruction, + createCancelRedeemInstruction +} from '../sdk/bubblegum/src/generated/instructions'; import { buildTree, Tree } from "./merkle-tree"; import { decodeMerkleRoll, getMerkleRollAccountSize, assertOnChainMerkleRollProperties, + createTransferAuthorityIx, } from "../sdk/gummyroll"; import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; import { @@ -26,13 +35,25 @@ import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; -import { logTx } from "./utils"; +import { execute, logTx } from "./utils"; +import { createMint } from "../../deps/solana-program-library/token/js/src"; +import { TokenProgramVersion, Version } from "../sdk/bubblegum/src/generated"; +import { createTransferAuthorityInstruction } from "../sdk/solita/gummyroll/src/generated"; // @ts-ignore let Bubblegum; // @ts-ignore let GummyrollProgramId; +/// Converts to Uint8Array +function bufferToArray(buffer: Buffer): number[] { + const nums = []; + for (let i = 0; i < buffer.length; i++) { + nums.push(buffer.at(i)); + } + return nums; +} + describe("bubblegum", () => { // Configure the client to use the local cluster. let offChainTree: Tree; @@ -149,7 +170,7 @@ describe("bubblegum", () => { await Bubblegum.provider.send(tx, [payer, merkleRollKeypair], { commitment: "confirmed", }); - + await assertOnChainMerkleRollProperties(Bubblegum.provider.connection, MAX_DEPTH, MAX_SIZE, authority, new PublicKey(tree.root), merkleRollKeypair.publicKey); return [merkleRollKeypair, tree, authority, nonce]; @@ -178,26 +199,21 @@ describe("bubblegum", () => { isMutable: false, editionNonce: null, tokenStandard: null, - tokenProgramVersion: { - original: {}, - }, + tokenProgramVersion: TokenProgramVersion.Original, collection: null, uses: null, creators: [], }; - let version = { v0: {} }; - let mintIx = await Bubblegum.instruction.mint(version, metadata, { - accounts: { - mintAuthority: payer.publicKey, - authority: treeAuthority, - nonce: nonceAccount, - gummyrollProgram: GummyrollProgramId, - owner: payer.publicKey, - delegate: payer.publicKey, - merkleSlab: merkleRollKeypair.publicKey, - }, - signers: [payer], - }); + let version = Version.V0; + const mintIx = createMintInstruction({ + mintAuthority: payer.publicKey, + authority: treeAuthority, + nonce: nonceAccount, + gummyrollProgram: GummyrollProgramId, + owner: payer.publicKey, + delegate: payer.publicKey, + merkleSlab: merkleRollKeypair.publicKey, + }, { version, message: metadata }); console.log(" - Minting to tree"); const mintTx = await Bubblegum.provider.send( new Transaction().add(mintIx), @@ -207,8 +223,8 @@ describe("bubblegum", () => { commitment: "confirmed", } ); - const leafHash = Buffer.from(keccak_256.digest(mintIx.data.slice(9))); - const creatorHash = Buffer.from(keccak_256.digest([])); + const dataHash = bufferToArray(Buffer.from(keccak_256.digest(mintIx.data.slice(9)))); + const creatorHash = bufferToArray(Buffer.from(keccak_256.digest([]))); let merkleRollAccount = await Bubblegum.provider.connection.getAccountInfo( merkleRollKeypair.publicKey @@ -220,25 +236,25 @@ describe("bubblegum", () => { console.log(" - Transferring Ownership"); const nonceInfo = await (Bubblegum.provider.connection as web3Connection).getAccountInfo(nonceAccount); const leafNonce = (new BN(nonceInfo.data.slice(8, 24), "le")).sub(new BN(1)); - let transferTx = await Bubblegum.rpc.transfer( - version, - onChainRoot, - leafHash, - creatorHash, - leafNonce, - 0, + let transferIx = createTransferInstruction( { - accounts: { - authority: treeAuthority, - owner: payer.publicKey, - delegate: payer.publicKey, - newOwner: destination.publicKey, - gummyrollProgram: GummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - }, - signers: [payer], + authority: treeAuthority, + owner: payer.publicKey, + delegate: payer.publicKey, + newOwner: destination.publicKey, + gummyrollProgram: GummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + }, + { + version, + root: bufferToArray(onChainRoot), + dataHash, + creatorHash, + nonce: leafNonce, + index: 0, } ); + await execute(Bubblegum.provider, [transferIx], [payer]) merkleRollAccount = await Bubblegum.provider.connection.getAccountInfo( merkleRollKeypair.publicKey @@ -248,25 +264,25 @@ describe("bubblegum", () => { merkleRoll.roll.changeLogs[merkleRoll.roll.activeIndex].root.toBuffer(); console.log(" - Delegating Ownership"); - let delegateTx = await Bubblegum.rpc.delegate( - version, - onChainRoot, - leafHash, - creatorHash, - leafNonce, - 0, + let delegateIx = await createDelegateInstruction( { - accounts: { - authority: treeAuthority, - owner: destination.publicKey, - previousDelegate: destination.publicKey, - newDelegate: delegateKey.publicKey, - gummyrollProgram: GummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - }, - signers: [destination], + authority: treeAuthority, + owner: destination.publicKey, + previousDelegate: destination.publicKey, + newDelegate: delegateKey.publicKey, + gummyrollProgram: GummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + }, + { + version, + root: bufferToArray(onChainRoot), + dataHash, + creatorHash, + nonce: leafNonce, + index: 0, } ); + await execute(Bubblegum.provider, [delegateIx], [destination]); merkleRollAccount = await Bubblegum.provider.connection.getAccountInfo( merkleRollKeypair.publicKey @@ -276,23 +292,21 @@ describe("bubblegum", () => { merkleRoll.roll.changeLogs[merkleRoll.roll.activeIndex].root.toBuffer(); console.log(" - Transferring Ownership (through delegate)"); - let delTransferIx = await Bubblegum.instruction.transfer( - version, - onChainRoot, - leafHash, - creatorHash, - leafNonce, - 0, + let delTransferIx = createTransferInstruction({ + authority: treeAuthority, + owner: destination.publicKey, + delegate: delegateKey.publicKey, + newOwner: payer.publicKey, + gummyrollProgram: GummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + }, { - accounts: { - authority: treeAuthority, - owner: destination.publicKey, - delegate: delegateKey.publicKey, - newOwner: payer.publicKey, - gummyrollProgram: GummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - }, - signers: [delegateKey], + version, + root: bufferToArray(onChainRoot), + dataHash, + creatorHash, + nonce: leafNonce, + index: 0, } ); delTransferIx.keys[2].isSigner = true; @@ -317,24 +331,22 @@ describe("bubblegum", () => { ); console.log(" - Redeeming Leaf", voucher.toBase58()); - let redeemIx = await Bubblegum.instruction.redeem( - version, - onChainRoot, - leafHash, - creatorHash, - leafNonce, - 0, + let redeemIx = createRedeemInstruction( { - accounts: { - authority: treeAuthority, - owner: payer.publicKey, - delegate: payer.publicKey, - gummyrollProgram: GummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - voucher: voucher, - systemProgram: SystemProgram.programId, - }, - signers: [payer], + authority: treeAuthority, + owner: payer.publicKey, + delegate: payer.publicKey, + gummyrollProgram: GummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + voucher: voucher, + }, + { + version, + root: bufferToArray(onChainRoot), + dataHash, + creatorHash, + nonce: leafNonce, + index: 0, } ); let redeemTx = await Bubblegum.provider.send( @@ -345,20 +357,20 @@ describe("bubblegum", () => { } ); console.log(" - Cancelling redeem (reinserting to tree)"); - let cancelRedeemIx = await Bubblegum.instruction.cancelRedeem( - onChainRoot, + + const cancelRedeemIx = createCancelRedeemInstruction( { - accounts: { - authority: treeAuthority, - owner: payer.publicKey, - delegate: payer.publicKey, - gummyrollProgram: GummyrollProgramId, - merkleSlab: merkleRollKeypair.publicKey, - voucher: voucher, - }, - signers: [payer], + authority: treeAuthority, + owner: payer.publicKey, + gummyrollProgram: GummyrollProgramId, + merkleSlab: merkleRollKeypair.publicKey, + voucher: voucher, + }, + { + root: bufferToArray(onChainRoot) } - ); + + ) let cancelRedeemTx = await Bubblegum.provider.send( new Transaction().add(cancelRedeemIx), [payer], @@ -368,26 +380,26 @@ describe("bubblegum", () => { ); console.log(" - Decompressing leaf"); - redeemIx = await Bubblegum.instruction.redeem( - version, - onChainRoot, - leafHash, - creatorHash, - leafNonce, - 0, - { - accounts: { + + redeemIx = + createRedeemInstruction( + { authority: treeAuthority, owner: payer.publicKey, delegate: payer.publicKey, gummyrollProgram: GummyrollProgramId, merkleSlab: merkleRollKeypair.publicKey, voucher: voucher, - systemProgram: SystemProgram.programId, }, - signers: [payer], - } - ); + { + version, + root: bufferToArray(onChainRoot), + dataHash, + creatorHash, + nonce: leafNonce, + index: 0, + } + ) redeemTx = await Bubblegum.provider.send( new Transaction().add(redeemIx), [payer], @@ -431,8 +443,8 @@ describe("bubblegum", () => { )[0]; }; - let decompressIx = await Bubblegum.instruction.decompress(metadata, { - accounts: { + let decompressIx = createDecompressInstruction( + { voucher: voucher, owner: payer.publicKey, tokenAccount: await Token.getAssociatedTokenAddress( @@ -445,14 +457,14 @@ describe("bubblegum", () => { mintAuthority: mintAuthority, metadata: await getMetadata(tokenMint.publicKey), masterEdition: await getMasterEdition(tokenMint.publicKey), - systemProgram: SystemProgram.programId, sysvarRent: SYSVAR_RENT_PUBKEY, tokenMetadataProgram: PROGRAM_ID, - tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, - signers: [payer], - }); + { + metadata, + } + ); decompressIx.keys[3].isSigner = true; let decompressTx = await Bubblegum.provider.send( diff --git a/contracts/tests/gumball-machine-test.ts b/contracts/tests/gumball-machine-test.ts index d0b251cd10a..37846eff8d9 100644 --- a/contracts/tests/gumball-machine-test.ts +++ b/contracts/tests/gumball-machine-test.ts @@ -1,52 +1,54 @@ import * as anchor from "@project-serum/anchor"; -import { keccak_256 } from "js-sha3"; import { BN, Provider, Program } from "@project-serum/anchor"; import { Bubblegum } from "../target/types/bubblegum"; -import { Gummyroll } from "../target/types/gummyroll"; -import { Key, PROGRAM_ID } from "@metaplex-foundation/mpl-token-metadata"; import { PublicKey, Keypair, SystemProgram, Transaction, Connection as web3Connection, - SYSVAR_RENT_PUBKEY, - SYSVAR_INSTRUCTIONS_PUBKEY, - SYSVAR_SLOT_HASHES_PUBKEY, LAMPORTS_PER_SOL, - TransactionInstruction, } from "@solana/web3.js"; import { assert } from "chai"; -import { buildTree, Tree } from "./merkle-tree"; +import { buildTree } from "./merkle-tree"; import { - decodeMerkleRoll, getMerkleRollAccountSize, assertOnChainMerkleRollProperties } from "../sdk/gummyroll"; import { - GUMBALL_MACHINE_HEADER_SIZE, GumballMachine, - InitGumballMachineProps, decodeGumballMachine, OnChainGumballMachine, createDispenseNFTForSolIx, createDispenseNFTForTokensIx, - createAddConfigLinesIx, - createUpdateConfigLinesIx, - createDestroyGumballMachineIx, createInitializeGumballMachineIxs, - createUpdateHeaderMetadataIx, getBubblegumAuthorityPDAKey, } from '../sdk/gumball-machine'; +import { + InitializeGumballMachineInstructionArgs, + createAddConfigLinesInstruction, + createUpdateConfigLinesInstruction, + UpdateHeaderMetadataInstructionArgs, + UpdateConfigLinesInstructionArgs, + createUpdateHeaderMetadataInstruction, + createDestroyInstruction, +} from "../sdk/gumball-machine/src/generated/instructions"; +import { + val, + strToByteArray, + strToByteUint8Array +} from "../sdk/utils/index"; +import { + GumballMachineHeader, + gumballMachineHeaderBeet +} from "../sdk/gumball-machine/src/generated/types/index"; import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet"; -import { getAssociatedTokenAddress, createMint, getOrCreateAssociatedTokenAccount, mintTo, getAccount } from "../../deps/solana-program-library/token/js/src"; +import { createMint, getOrCreateAssociatedTokenAccount, mintTo, getAccount } from "../../deps/solana-program-library/token/js/src"; import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, NATIVE_MINT } from "@solana/spl-token"; -import { logTx, num32ToBuffer } from "./utils"; +import { logTx, num32ToBuffer, arrayEquals } from "./utils"; // @ts-ignore let GumballMachine; @@ -77,17 +79,17 @@ describe("gumball-machine", () => { GummyrollProgramId = anchor.workspace.Gummyroll.programId; BubblegumProgramId = anchor.workspace.Bubblegum.programId; - function assertGumballMachineHeaderProperties(gm: OnChainGumballMachine, expectedHeader: InitGumballMachineProps) { + function assertGumballMachineHeaderProperties(gm: OnChainGumballMachine, expectedHeader: GumballMachineHeader) { assert( - gm.header.urlBase.equals(expectedHeader.urlBase), + arrayEquals(gm.header.urlBase, expectedHeader.urlBase), "Gumball Machine has incorrect url base" ); assert( - gm.header.nameBase.equals(expectedHeader.nameBase), + arrayEquals(gm.header.nameBase, expectedHeader.nameBase), "Gumball Machine has incorrect name base" ); assert( - gm.header.symbol.equals(expectedHeader.symbol), + arrayEquals(gm.header.symbol, expectedHeader.symbol), "Gumball Machine has incorrect symbol" ); assert( @@ -103,11 +105,11 @@ describe("gumball-machine", () => { "Gumball Machine has incorrect retainAuthority" ); assert( - gm.header.price.eq(expectedHeader.price), + val(gm.header.price).eq(val(expectedHeader.price)), "Gumball Machine has incorrect price" ); assert( - gm.header.goLiveDate.eq(expectedHeader.goLiveDate), + val(gm.header.goLiveDate).eq(val(expectedHeader.goLiveDate)), "Gumball Machine has incorrect goLiveDate" ); assert( @@ -135,15 +137,19 @@ describe("gumball-machine", () => { "Gumball Machine set with incorrect creatorAddress" ); assert( - gm.header.extensionLen.eq(expectedHeader.extensionLen), + gm.header.mint.equals(expectedHeader.mint), + "Gumball Machine set with incorrect mint" + ); + assert( + val(gm.header.extensionLen).eq(val(expectedHeader.extensionLen)), "Gumball Machine has incorrect extensionLen" ); assert( - gm.header.maxMintSize.eq(expectedHeader.maxMintSize), + val(gm.header.maxMintSize).eq(val(expectedHeader.maxMintSize)), "Gumball Machine has incorrect maxMintSize" ); assert( - gm.header.maxItems.eq(expectedHeader.maxItems), + val(gm.header.maxItems).eq(val(expectedHeader.maxItems)), "Gumball Machine has incorrect max items" ); } @@ -170,27 +176,49 @@ describe("gumball-machine", () => { gumballMachineAcctSize: number, merkleRollKeypair: Keypair, merkleRollAccountSize: number, - desiredGumballMachineHeader: InitGumballMachineProps, - maxDepth: number, - maxBufferSize: number + gumballMachineInitArgs: InitializeGumballMachineInstructionArgs, + mint: PublicKey ) { const bubblegumAuthorityPDAKey = await getBubblegumAuthorityPDAKey(merkleRollKeypair.publicKey, BubblegumProgramId); - const initializeGumballMachineInstrs = await createInitializeGumballMachineIxs(payer, gumballMachineAcctKeypair, gumballMachineAcctSize, merkleRollKeypair, merkleRollAccountSize, desiredGumballMachineHeader, maxDepth, maxBufferSize, GummyrollProgramId, BubblegumProgramId, GumballMachine); + const initializeGumballMachineInstrs = await createInitializeGumballMachineIxs(payer, gumballMachineAcctKeypair, gumballMachineAcctSize, merkleRollKeypair, merkleRollAccountSize, gumballMachineInitArgs, mint, GummyrollProgramId, BubblegumProgramId, GumballMachine); const tx = new Transaction(); initializeGumballMachineInstrs.forEach((instr) => tx.add(instr)); await GumballMachine.provider.send(tx, [payer, gumballMachineAcctKeypair, merkleRollKeypair], { commitment: "confirmed", }); - const tree = buildTree(Array(2 ** maxDepth).fill(Buffer.alloc(32))); - await assertOnChainMerkleRollProperties(GumballMachine.provider.connection, maxDepth, maxBufferSize, bubblegumAuthorityPDAKey, new PublicKey(tree.root), merkleRollKeypair.publicKey); + const tree = buildTree(Array(2 ** gumballMachineInitArgs.maxDepth).fill(Buffer.alloc(32))); + await assertOnChainMerkleRollProperties(GumballMachine.provider.connection, gumballMachineInitArgs.maxDepth, gumballMachineInitArgs.maxBufferSize, bubblegumAuthorityPDAKey, new PublicKey(tree.root), merkleRollKeypair.publicKey); const onChainGumballMachineAccount = await GumballMachine.provider.connection.getAccountInfo( gumballMachineAcctKeypair.publicKey ); const gumballMachine = decodeGumballMachine(onChainGumballMachineAccount.data, gumballMachineAcctSize); - assertGumballMachineHeaderProperties(gumballMachine, desiredGumballMachineHeader); + + let expectedOnChainHeader: GumballMachineHeader = { + urlBase: gumballMachineInitArgs.urlBase, + nameBase: gumballMachineInitArgs.nameBase, + symbol: gumballMachineInitArgs.symbol, + sellerFeeBasisPoints: gumballMachineInitArgs.sellerFeeBasisPoints, + isMutable: gumballMachineInitArgs.isMutable ? 1 : 0, + retainAuthority: gumballMachineInitArgs.retainAuthority ? 1 : 0, + padding: [0,0,0,0], + price: gumballMachineInitArgs.price, + goLiveDate: gumballMachineInitArgs.goLiveDate, + mint, + botWallet: gumballMachineInitArgs.botWallet, + receiver: gumballMachineInitArgs.receiver, + authority: gumballMachineInitArgs.authority, + collectionKey: gumballMachineInitArgs.collectionKey, // 0x0 -> no collection key + creatorAddress: payer.publicKey, + extensionLen: gumballMachineInitArgs.extensionLen, + maxMintSize: gumballMachineInitArgs.maxMintSize, + remaining: new BN(0), + maxItems: gumballMachineInitArgs.maxItems, + totalItemsAdded: new BN(0) + } + assertGumballMachineHeaderProperties(gumballMachine, expectedOnChainHeader); } async function addConfigLines( @@ -199,10 +227,18 @@ describe("gumball-machine", () => { gumballMachineAcctSize: number, gumballMachineAcctConfigIndexArrSize: number, gumballMachineAcctConfigLinesSize: number, - configLinesToAdd: Buffer, + configLinesToAdd: Uint8Array, allExpectedInitializedConfigLines: Buffer ) { - const addConfigLinesInstr = createAddConfigLinesIx(authority, gumballMachineAcctKey, configLinesToAdd, GumballMachine); + const addConfigLinesInstr = createAddConfigLinesInstruction( + { + gumballMachine: gumballMachineAcctKey, + authority: authority.publicKey + }, + { + newConfigLinesData: configLinesToAdd + } + ); const tx = new Transaction().add(addConfigLinesInstr) await GumballMachine.provider.send(tx, [authority], { commitment: "confirmed", @@ -216,7 +252,7 @@ describe("gumball-machine", () => { const expectedIndexArrBuffer = [...Array(gumballMachineAcctConfigIndexArrSize/4).keys()].reduce( (prevVal, curVal) => Buffer.concat([prevVal, Buffer.from(num32ToBuffer(curVal))]), Buffer.from([]) - ) + ); assertGumballMachineConfigProperties(gumballMachine, expectedIndexArrBuffer, allExpectedInitializedConfigLines, gumballMachineAcctConfigLinesSize); } @@ -231,7 +267,14 @@ describe("gumball-machine", () => { allExpectedInitializedConfigLines: Buffer, indexOfFirstLineToUpdate: BN ) { - const updateConfigLinesInstr = createUpdateConfigLinesIx(authority, gumballMachineAcctKey, updatedConfigLines, indexOfFirstLineToUpdate, GumballMachine); + const args: UpdateConfigLinesInstructionArgs = { startingLine: indexOfFirstLineToUpdate, newConfigLinesData: updatedConfigLines }; + const updateConfigLinesInstr = createUpdateConfigLinesInstruction( + { + authority: authority.publicKey, + gumballMachine: gumballMachineAcctKey, + }, + args + ); const tx = new Transaction().add(updateConfigLinesInstr) await GumballMachine.provider.send(tx, [authority], { commitment: "confirmed", @@ -254,20 +297,25 @@ describe("gumball-machine", () => { authority: Keypair, gumballMachineAcctKey: PublicKey, gumballMachineAcctSize, - newHeader: InitGumballMachineProps, + newHeader: UpdateHeaderMetadataInstructionArgs, + resultingExpectedOnChainHeader: GumballMachineHeader ) { - const updateHeaderMetadataInstr = createUpdateHeaderMetadataIx(authority, gumballMachineAcctKey, newHeader, GumballMachine); - + const updateHeaderMetadataInstr = createUpdateHeaderMetadataInstruction( + { + gumballMachine: gumballMachineAcctKey, + authority: authority.publicKey + }, + newHeader + ); const tx = new Transaction().add(updateHeaderMetadataInstr) await GumballMachine.provider.send(tx, [authority], { commitment: "confirmed", }); - const onChainGumballMachineAccount = await GumballMachine.provider.connection.getAccountInfo( gumballMachineAcctKey ); const gumballMachine = decodeGumballMachine(onChainGumballMachineAccount.data, gumballMachineAcctSize); - assertGumballMachineHeaderProperties(gumballMachine, newHeader); + assertGumballMachineHeaderProperties(gumballMachine, resultingExpectedOnChainHeader); } async function dispenseCompressedNFTForSol( @@ -292,9 +340,7 @@ describe("gumball-machine", () => { const tx = new Transaction().add(dispenseInstr); await GumballMachine.provider.send(tx, [payer], { commitment: "confirmed", - }); - - // TODO(sorend): assert that the effects of the mint are as expected + }); } async function dispenseCompressedNFTForTokens( @@ -321,9 +367,7 @@ describe("gumball-machine", () => { const tx = new Transaction().add(dispenseInstr); await GumballMachine.provider.send(tx, [payer], { commitment: "confirmed", - }); - - // TODO(sorend): assert that the effects of the mint are as expected + }); } async function destroyGumballMachine( @@ -332,7 +376,12 @@ describe("gumball-machine", () => { ) { const originalGumballMachineAcctBalance = await connection.getBalance(gumballMachineAcctKeypair.publicKey); const originalAuthorityAcctBalance = await connection.getBalance(authorityKeypair.publicKey); - const destroyInstr = createDestroyGumballMachineIx(gumballMachineAcctKeypair, authorityKeypair, GumballMachine); + const destroyInstr = createDestroyInstruction( + { + gumballMachine: gumballMachineAcctKeypair.publicKey, + authority: authorityKeypair.publicKey + } + ); const tx = new Transaction().add(destroyInstr); await GumballMachine.provider.send(tx, [authorityKeypair], { commitment: "confirmed", @@ -351,7 +400,7 @@ describe("gumball-machine", () => { } describe("Testing gumball-machine", async () => { - let baseGumballMachineHeader: InitGumballMachineProps; + let baseGumballMachineInitProps: InitializeGumballMachineInstructionArgs; let creatorAddress: Keypair; let gumballMachineAcctKeypair: Keypair; let merkleRollKeypair: Keypair; @@ -359,7 +408,7 @@ describe("gumball-machine", () => { let nftBuyer: Keypair; const GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE = 1000; const GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE = 7000; - const GUMBALL_MACHINE_ACCT_SIZE = GUMBALL_MACHINE_HEADER_SIZE + GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE + GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE; + const GUMBALL_MACHINE_ACCT_SIZE = gumballMachineHeaderBeet.byteSize + GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE + GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE; const MERKLE_ROLL_ACCT_SIZE = getMerkleRollAccountSize(3,8); before(async () => { @@ -401,24 +450,24 @@ describe("gumball-machine", () => { gumballMachineAcctKeypair = Keypair.generate(); merkleRollKeypair = Keypair.generate(); - baseGumballMachineHeader = { - urlBase: Buffer.from("https://arweave.net/Rmg4pcIv-0FQ7M7X838p2r592Q4NU63Fj7o7XsvBHEEl"), - nameBase: Buffer.from("zfgfsxrwieciemyavrpkuqehkmhqmnim"), - symbol: Buffer.from("12345678"), + baseGumballMachineInitProps = { + maxDepth: 3, + maxBufferSize: 8, + urlBase: strToByteArray("https://arweave.net/Rmg4pcIv-0FQ7M7X838p2r592Q4NU63Fj7o7XsvBHEEl"), + nameBase: strToByteArray("zfgfsxrwieciemyavrpkuqehkmhqmnim"), + symbol: strToByteArray("12345678"), sellerFeeBasisPoints: 100, isMutable: true, retainAuthority: true, price: new BN(10), goLiveDate: new BN(1234.0), - mint: NATIVE_MINT, botWallet: Keypair.generate().publicKey, receiver: creatorPaymentWallet.publicKey, authority: creatorAddress.publicKey, collectionKey: SystemProgram.programId, // 0x0 -> no collection key - creatorAddress: creatorAddress.publicKey, extensionLen: new BN(28), maxMintSize: new BN(10), - maxItems: new BN(250) + maxItems: new BN(250), }; // Give creator enough funds to produce accounts for NFT @@ -427,14 +476,14 @@ describe("gumball-machine", () => { "confirmed" ); - await initializeGumballMachine(creatorAddress, gumballMachineAcctKeypair, GUMBALL_MACHINE_ACCT_SIZE, merkleRollKeypair, MERKLE_ROLL_ACCT_SIZE, baseGumballMachineHeader, 3, 8); - await addConfigLines(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE, Buffer.from("uluvnpwncgchwnbqfpbtdlcpdthc"), Buffer.from("uluvnpwncgchwnbqfpbtdlcpdthc")); + await initializeGumballMachine(creatorAddress, gumballMachineAcctKeypair, GUMBALL_MACHINE_ACCT_SIZE, merkleRollKeypair, MERKLE_ROLL_ACCT_SIZE, baseGumballMachineInitProps, NATIVE_MINT); + await addConfigLines(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE, strToByteUint8Array("uluvnpwncgchwnbqfpbtdlcpdthc"), Buffer.from("uluvnpwncgchwnbqfpbtdlcpdthc")); }); describe("dispense nft sol instruction", async () => { beforeEach(async () => { // Give the recipient address enough money to not get rent exempt await GumballMachine.provider.connection.confirmTransaction( - await GumballMachine.provider.connection.requestAirdrop(baseGumballMachineHeader.receiver, LAMPORTS_PER_SOL), + await GumballMachine.provider.connection.requestAirdrop(baseGumballMachineInitProps.receiver, LAMPORTS_PER_SOL), "confirmed" ); @@ -450,7 +499,7 @@ describe("gumball-machine", () => { let dummyInstr; beforeEach(async () => { - dispenseNFTForSolInstr = await createDispenseNFTForSolIx(new BN(1), nftBuyer, baseGumballMachineHeader.receiver, gumballMachineAcctKeypair, merkleRollKeypair, noncePDAKey, GummyrollProgramId, BubblegumProgramId, GumballMachine); + dispenseNFTForSolInstr = await createDispenseNFTForSolIx(new BN(1), nftBuyer, baseGumballMachineInitProps.receiver, gumballMachineAcctKeypair, merkleRollKeypair, noncePDAKey, GummyrollProgramId, BubblegumProgramId, GumballMachine); dummyNewAcctKeypair = Keypair.generate(); dummyInstr = SystemProgram.createAccount({ fromPubkey: payer.publicKey, @@ -482,7 +531,7 @@ describe("gumball-machine", () => { it("Can dispense single NFT paid in sol", async () => { // Give the recipient address enough money to not get rent exempt await GumballMachine.provider.connection.confirmTransaction( - await GumballMachine.provider.connection.requestAirdrop(baseGumballMachineHeader.receiver, LAMPORTS_PER_SOL), + await GumballMachine.provider.connection.requestAirdrop(baseGumballMachineInitProps.receiver, LAMPORTS_PER_SOL), "confirmed" ); @@ -493,21 +542,21 @@ describe("gumball-machine", () => { ); const nftBuyerBalanceBeforePurchase = await connection.getBalance(nftBuyer.publicKey); - const creatorBalanceBeforePurchase = await connection.getBalance(baseGumballMachineHeader.receiver); + const creatorBalanceBeforePurchase = await connection.getBalance(baseGumballMachineInitProps.receiver); // Purchase the compressed NFT with SOL - await dispenseCompressedNFTForSol(new BN(1), nftBuyer, baseGumballMachineHeader.receiver, gumballMachineAcctKeypair, merkleRollKeypair, noncePDAKey); + await dispenseCompressedNFTForSol(new BN(1), nftBuyer, baseGumballMachineInitProps.receiver, gumballMachineAcctKeypair, merkleRollKeypair, noncePDAKey); const nftBuyerBalanceAfterPurchase = await connection.getBalance(nftBuyer.publicKey); - const creatorBalanceAfterPurchase = await connection.getBalance(baseGumballMachineHeader.receiver); + const creatorBalanceAfterPurchase = await connection.getBalance(baseGumballMachineInitProps.receiver); // Assert on how the creator and buyer's balances changed assert( - await creatorBalanceAfterPurchase === (creatorBalanceBeforePurchase + baseGumballMachineHeader.price.toNumber()), + await creatorBalanceAfterPurchase === (creatorBalanceBeforePurchase + val(baseGumballMachineInitProps.price).toNumber()), "Creator balance did not update as expected after NFT purchase" ); assert( - await nftBuyerBalanceAfterPurchase === (nftBuyerBalanceBeforePurchase - baseGumballMachineHeader.price.toNumber()), + await nftBuyerBalanceAfterPurchase === (nftBuyerBalanceBeforePurchase - val(baseGumballMachineInitProps.price).toNumber()), "NFT purchaser balance did not decrease as expected after NFT purchase" ); }); @@ -518,33 +567,49 @@ describe("gumball-machine", () => { await updateConfigLines(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE, Buffer.from("aaavnpwncgchwnbqfpbtdlcpdaaa"), Buffer.from("aaavnpwncgchwnbqfpbtdlcpdaaa"), new BN(0)); }); it("Can update gumball header", async () => { - const newGumballMachineHeader: InitGumballMachineProps = { - urlBase: Buffer.from("https://arweave.net/bzdjillretjcraaxawlnhqrhmexzbsixyajrlzhfcvcc"), - nameBase: Buffer.from("wmqeslreeondhmcmtfebrwqnqcoasbye"), - symbol: Buffer.from("abcdefgh"), + const newGumballMachineHeader: UpdateHeaderMetadataInstructionArgs = { + urlBase: strToByteArray("https://arweave.net/bzdjillretjcraaxawlnhqrhmexzbsixyajrlzhfcvcc"), + nameBase: strToByteArray("wmqeslreeondhmcmtfebrwqnqcoasbye"), + symbol: strToByteArray("abcdefgh"), sellerFeeBasisPoints: 50, isMutable: false, retainAuthority: false, price: new BN(100), goLiveDate: new BN(5678.0), - mint: baseGumballMachineHeader.mint, // Cannot be modified after init botWallet: Keypair.generate().publicKey, - receiver: baseGumballMachineHeader.receiver, // FOR NOW: Cannot be modified after init authority: Keypair.generate().publicKey, - collectionKey: baseGumballMachineHeader.collectionKey, // Cannot be modified after init - creatorAddress: baseGumballMachineHeader.creatorAddress, // Cannot be modified after init - extensionLen: baseGumballMachineHeader.extensionLen, // Cannot be modified after init - maxMintSize: new BN(15), - maxItems: baseGumballMachineHeader.maxItems // Cannot be modified after init + maxMintSize: new BN(15) }; - await updateHeaderMetadata(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, newGumballMachineHeader); + + const expectedOnChainHeader: GumballMachineHeader = { + urlBase: newGumballMachineHeader.urlBase, + nameBase: newGumballMachineHeader.nameBase, + symbol: newGumballMachineHeader.symbol, + sellerFeeBasisPoints: newGumballMachineHeader.sellerFeeBasisPoints, + isMutable: newGumballMachineHeader.isMutable ? 1 : 0, + retainAuthority: newGumballMachineHeader.retainAuthority ? 1 : 0, + padding: [0,0,0,0], + price: newGumballMachineHeader.price, + goLiveDate: newGumballMachineHeader.goLiveDate, + mint: NATIVE_MINT, + botWallet: newGumballMachineHeader.botWallet, + receiver: baseGumballMachineInitProps.receiver, + authority: newGumballMachineHeader.authority, + collectionKey: baseGumballMachineInitProps.collectionKey, + creatorAddress: creatorAddress.publicKey, + extensionLen: baseGumballMachineInitProps.extensionLen, + maxMintSize: newGumballMachineHeader.maxMintSize, + remaining: new BN(0), + maxItems: baseGumballMachineInitProps.maxItems, + totalItemsAdded: new BN(0) + } + await updateHeaderMetadata(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, newGumballMachineHeader, expectedOnChainHeader); }); it("Can destroy gumball machine and reclaim lamports", async () => { await destroyGumballMachine(gumballMachineAcctKeypair, creatorAddress); }); }); }); - describe("spl token projects", async () => { let someMint: PublicKey; let creatorReceiverTokenAccount; @@ -566,28 +631,28 @@ describe("gumball-machine", () => { someMint = await createMint(connection, payer, payer.publicKey, null, 9); creatorReceiverTokenAccount = await getOrCreateAssociatedTokenAccount(connection, payer, someMint, creatorAddress.publicKey); - - baseGumballMachineHeader = { - urlBase: Buffer.from("https://arweave.net/Rmg4pcIv-0FQ7M7X838p2r592Q4NU63Fj7o7XsvBHEEl"), - nameBase: Buffer.from("zfgfsxrwieciemyavrpkuqehkmhqmnim"), - symbol: Buffer.from("12345678"), + + baseGumballMachineInitProps = { + maxDepth: 3, + maxBufferSize: 8, + urlBase: strToByteArray("https://arweave.net/Rmg4pcIv-0FQ7M7X838p2r592Q4NU63Fj7o7XsvBHEEl"), + nameBase: strToByteArray("zfgfsxrwieciemyavrpkuqehkmhqmnim"), + symbol: strToByteArray("12345678"), sellerFeeBasisPoints: 100, isMutable: true, retainAuthority: true, price: new BN(10), goLiveDate: new BN(1234.0), - mint: someMint, botWallet: botWallet.publicKey, receiver: creatorReceiverTokenAccount.address, authority: creatorAddress.publicKey, - collectionKey: SystemProgram.programId, // 0x0 -> no collection - creatorAddress: creatorAddress.publicKey, + collectionKey: SystemProgram.programId, // 0x0 -> no collection key extensionLen: new BN(28), maxMintSize: new BN(10), - maxItems: new BN(250) + maxItems: new BN(250), }; - await initializeGumballMachine(creatorAddress, gumballMachineAcctKeypair, GUMBALL_MACHINE_ACCT_SIZE, merkleRollKeypair, MERKLE_ROLL_ACCT_SIZE, baseGumballMachineHeader, 3, 8); + await initializeGumballMachine(creatorAddress, gumballMachineAcctKeypair, GUMBALL_MACHINE_ACCT_SIZE, merkleRollKeypair, MERKLE_ROLL_ACCT_SIZE, baseGumballMachineInitProps, someMint); await addConfigLines(creatorAddress, gumballMachineAcctKeypair.publicKey, GUMBALL_MACHINE_ACCT_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_INDEX_ARRAY_SIZE, GUMBALL_MACHINE_ACCT_CONFIG_LINES_SIZE, Buffer.from("uluvnpwncgchwnbqfpbtdlcpdthc" + "aauvnpwncgchwnbqfpbtdlcpdthc"), Buffer.from("uluvnpwncgchwnbqfpbtdlcpdthc" + "aauvnpwncgchwnbqfpbtdlcpdthc")); // Create and fund the NFT pruchaser @@ -637,12 +702,12 @@ describe("gumball-machine", () => { let newBuyerTokenAccount = await getAccount(connection, nftBuyerTokenAccount.address); assert( - Number(newCreatorTokenAccount.amount) === Number(creatorReceiverTokenAccount.amount) + baseGumballMachineHeader.price.toNumber(), + Number(newCreatorTokenAccount.amount) === Number(creatorReceiverTokenAccount.amount) + val(baseGumballMachineInitProps.price).toNumber(), "The creator did not receive their payment as expected" ); assert( - Number(newBuyerTokenAccount.amount) === Number(buyerTokenAccount.amount) - baseGumballMachineHeader.price.toNumber(), + Number(newBuyerTokenAccount.amount) === Number(buyerTokenAccount.amount) - val(baseGumballMachineInitProps.price).toNumber(), "The nft buyer did not pay for the nft as expected" ); @@ -654,12 +719,12 @@ describe("gumball-machine", () => { newBuyerTokenAccount = await getAccount(connection, nftBuyerTokenAccount.address); assert( - Number(newCreatorTokenAccount.amount) === Number(creatorReceiverTokenAccount.amount) + baseGumballMachineHeader.price.toNumber(), + Number(newCreatorTokenAccount.amount) === Number(creatorReceiverTokenAccount.amount) + val(baseGumballMachineInitProps.price).toNumber(), "The creator did not receive their payment as expected" ); assert( - Number(newBuyerTokenAccount.amount) === Number(buyerTokenAccount.amount) - baseGumballMachineHeader.price.toNumber(), + Number(newBuyerTokenAccount.amount) === Number(buyerTokenAccount.amount) - val(baseGumballMachineInitProps.price).toNumber(), "The nft buyer did not pay for the nft as expected" ); }); diff --git a/contracts/tests/utils.ts b/contracts/tests/utils.ts index 9fbc8bca11a..c728bb466e8 100644 --- a/contracts/tests/utils.ts +++ b/contracts/tests/utils.ts @@ -40,3 +40,9 @@ export function num32ToBuffer(num: number) { return Buffer.from([byte1, byte2, byte3, byte4]) } +export function arrayEquals(a, b) { + return Array.isArray(a) && + Array.isArray(b) && + a.length === b.length && + a.every((val, index) => val === b[index]); +} diff --git a/contracts/yarn.lock b/contracts/yarn.lock index 669b7bd812d..31675b78b30 100644 --- a/contracts/yarn.lock +++ b/contracts/yarn.lock @@ -69,7 +69,7 @@ "@metaplex-foundation/beet" ">=0.1.0" "@solana/web3.js" "^1.31.0" -"@metaplex-foundation/beet@>=0.1.0": +"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.2.0.tgz#e543e17fd1c4dc1251e9aea481a7429bc73f70b8" integrity sha512-H570hkJxmx/FxET1OggPPLkPL7psYQa71rNI9NJjYRM8WXdrEvmI/IRIEUW2KR6RqwWWN3FvlRHnKoQUV/lQtA==