Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: setStoreV2 with StoreConfig #90

Merged
merged 5 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './transactions';
export * from './initStore';
export * from './initStoreV2';
export * from './mintNFT';
export * from './mintEditionFromMaster';
export * from './closeVault';
Expand Down
42 changes: 42 additions & 0 deletions src/actions/initStoreV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { Connection } from '../Connection';
import { sendTransaction } from './transactions';
import { SetStoreV2, Store, StoreConfig } from '../programs/metaplex';

interface IInitStoreV2Params {
connection: Connection;
wallet: Wallet;
isPublic?: boolean;
settingsUri?: string | null;
}

interface IInitStoreV2Response {
storeId: PublicKey;
configId: PublicKey;
txId: string;
}

export const initStoreV2 = async ({
connection,
wallet,
settingsUri = null,
isPublic = true,
}: IInitStoreV2Params): Promise<IInitStoreV2Response> => {
const storeId = await Store.getPDA(wallet.publicKey);
const configId = await StoreConfig.getPDA(storeId);
const tx = new SetStoreV2(
{ feePayer: wallet.publicKey },
{
admin: new PublicKey(wallet.publicKey),
store: storeId,
config: configId,
isPublic,
settingsUri,
},
);

const txId = await sendTransaction({ connection, wallet, txs: [tx] });

return { storeId, configId, txId };
};
4 changes: 4 additions & 0 deletions src/programs/metaplex/MetaplexProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ export enum MetaplexKey {
AuctionManagerV2 = 10,
BidRedemptionTicketV2 = 11,
AuctionWinnerTokenTypeTrackerV1 = 12,
StoreIndexerV1 = 13,
AuctionCacheV1 = 14,
StoreConfigV1 = 15,
}

export class MetaplexProgram extends Program {
static readonly PREFIX = 'metaplex';
static readonly CONFIG = 'config';
static readonly TOTALS = 'totals';
static readonly PUBKEY = new PublicKey(config.programs.metaplex);
}
54 changes: 54 additions & 0 deletions src/programs/metaplex/accounts/StoreConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ERROR_INVALID_ACCOUNT_DATA, ERROR_INVALID_OWNER } from '@metaplex/errors';
import { AnyPublicKey } from '@metaplex/types';
import { Borsh } from '@metaplex/utils';
import { AccountInfo, PublicKey } from '@solana/web3.js';
import { Buffer } from 'buffer';
import { Account } from '../../../Account';
import { MetaplexKey, MetaplexProgram } from '../MetaplexProgram';

type Args = {
settingsUri: string;
};
export class StoreConfigData extends Borsh.Data<Args> {
static readonly SCHEMA = this.struct([
['key', 'u8'],
['settingsUri', { kind: 'option', type: 'string' }],
]);

key: MetaplexKey = MetaplexKey.StoreConfigV1;
settingsUri: string;

constructor(args: Args) {
super(args);
this.key = MetaplexKey.StoreConfigV1;
}
}

export class StoreConfig extends Account<StoreConfigData> {
constructor(pubkey: AnyPublicKey, info: AccountInfo<Buffer>) {
super(pubkey, info);

if (!this.assertOwner(MetaplexProgram.PUBKEY)) {
throw ERROR_INVALID_OWNER();
}

if (!StoreConfig.isCompatible(this.info.data)) {
throw ERROR_INVALID_ACCOUNT_DATA();
}

this.data = StoreConfigData.deserialize(this.info.data);
}

static isCompatible(data: Buffer) {
return data[0] === MetaplexKey.StoreConfigV1;
}

static async getPDA(store: AnyPublicKey) {
return MetaplexProgram.findProgramAddress([
Buffer.from(MetaplexProgram.PREFIX),
MetaplexProgram.PUBKEY.toBuffer(),
Buffer.from(MetaplexProgram.CONFIG),
new PublicKey(store).toBuffer(),
]);
}
}
1 change: 1 addition & 0 deletions src/programs/metaplex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './accounts/PayoutTicket';
export * from './accounts/PrizeTrackingTicket';
export * from './accounts/SafetyDepositConfig';
export * from './accounts/Store';
export * from './accounts/StoreConfig';
export * from './accounts/WhitelistedCreator';
export * from './accounts/AuctionWinnerTokenTypeTracker';
export * from './MetaplexProgram';
Expand Down
99 changes: 99 additions & 0 deletions src/programs/metaplex/transactions/SetStoreV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
import {
PublicKey,
SystemProgram,
SYSVAR_RENT_PUBKEY,
TransactionCtorFields,
TransactionInstruction,
} from '@solana/web3.js';
import { Borsh } from '@metaplex/utils';
import { Transaction } from '../../../Transaction';
import { VaultProgram } from '../../vault';
import { MetadataProgram } from '../../metadata';
import { AuctionProgram } from '../../auction';
import { MetaplexProgram } from '../MetaplexProgram';
import { ParamsWithStore } from '@metaplex/types';

export class SetStoreV2Args extends Borsh.Data<{ public: boolean; settingsUri: string | null }> {
static readonly SCHEMA = this.struct([
['instruction', 'u8'],
['public', 'u8'],
['settingsUri', { kind: 'option', type: 'string' }],
]);

instruction = 23;
public: boolean;
settingsUri: string | null;
}

type SetStoreV2Params = {
admin: PublicKey;
config: PublicKey;
isPublic: boolean;
settingsUri: string | null;
};

export class SetStoreV2 extends Transaction {
constructor(options: TransactionCtorFields, params: ParamsWithStore<SetStoreV2Params>) {
super(options);
const { feePayer } = options;
const { admin, store, config, isPublic, settingsUri } = params;

const data = SetStoreV2Args.serialize({ public: isPublic, settingsUri });

this.add(
new TransactionInstruction({
keys: [
{
pubkey: store,
isSigner: false,
isWritable: true,
},
{
pubkey: config,
isSigner: false,
isWritable: true,
},
{
pubkey: admin,
isSigner: true,
isWritable: false,
},
{
pubkey: feePayer,
isSigner: true,
isWritable: false,
},
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
{
pubkey: VaultProgram.PUBKEY,
isSigner: false,
isWritable: false,
},
{
pubkey: MetadataProgram.PUBKEY,
isSigner: false,
isWritable: false,
},
{
pubkey: AuctionProgram.PUBKEY,
isSigner: false,
isWritable: false,
},
{
pubkey: SystemProgram.programId,
isSigner: false,
isWritable: false,
},
{
pubkey: SYSVAR_RENT_PUBKEY,
isSigner: false,
isWritable: false,
},
],
programId: MetaplexProgram.PUBKEY,
data,
}),
);
}
}
1 change: 1 addition & 0 deletions src/programs/metaplex/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './SetStore';
export * from './SetStoreV2';
export * from './SetWhitelistedCreator';
export * from './StartAuction';
export * from './InitAuctionManagerV2';
Expand Down
60 changes: 60 additions & 0 deletions test/actions/initStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Keypair } from '@solana/web3.js';
import { Connection, NodeWallet } from '../../src';
import { initStore, initStoreV2 } from '../../src/actions';
import { FEE_PAYER, NETWORK } from '../utils';
import { Store } from '../../src/programs/metaplex';
import { uri } from './shared';

describe('init Store', () => {
const connection = new Connection(NETWORK);
const wallet = new NodeWallet(FEE_PAYER);
let mint: Keypair;

beforeEach(() => {
mint = Keypair.generate();
jest.spyOn(Keypair, 'generate').mockReturnValue(mint);
});

test('creates store with initStore', async () => {
const storeResponse = await initStore({
connection,
wallet,
isPublic: false,
});

const storeId = await Store.getPDA(wallet.publicKey);

expect(storeResponse).toMatchObject({
storeId,
});
});

test('creates store with initStoreV2', async () => {
const storeResponse = await initStoreV2({
connection,
wallet,
isPublic: false,
settingsUri: uri,
});

const storeId = await Store.getPDA(wallet.publicKey);

expect(storeResponse).toMatchObject({
storeId,
});
});

test('creates store with initStoreV2 without storeV2', async () => {
const storeResponse = await initStoreV2({
connection,
wallet,
isPublic: false,
});

const storeId = await Store.getPDA(wallet.publicKey);

expect(storeResponse).toMatchObject({
storeId,
});
});
});