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: added new actions for metadata program #56

Merged
merged 3 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ const rates = await new Coingecko().getRate([Currency.AR, Currency.SOL], Currenc
- [x] MintNewEditionFromMasterEditionViaToken
- [ ] MintNewEditionFromMasterEditionViaVaultProxy
- [ ] Actions
- [ ] Create
- [ ] Update
- [ ] Sign
- [X] Create
- [X] Update
- [X] Sign
- [ ] Send
- [X] Mint
- [X] Mint Master Edition
- [X] Mint Limited Edition from Master
- [ ] Burn
- [ ] Metaplex
- [ ] Accounts
Expand Down
42 changes: 42 additions & 0 deletions src/actions/createMasterEdition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { CreateMasterEdition, MasterEdition, Metadata } from '../programs/metadata';
import { sendTransaction } from './transactions';
import BN from 'bn.js';

interface CreateMasterEditionParams {
connection: Connection;
wallet: Wallet;
editionMint: PublicKey;
updateAuthority?: PublicKey;
maxSupply?: BN;
}

/*
* NOTE 1: a metadata account must already exist
* NOTE 2: must have exactly 1 editionMint token with 0 decimals outstanding
*/
export const createMasterEdition = async (
{ connection, wallet, editionMint, updateAuthority, maxSupply } = {} as CreateMasterEditionParams,
): Promise<string> => {
const metadata = await Metadata.getPDA(editionMint);
const edition = await MasterEdition.getPDA(editionMint);

const createMetadataTx = new CreateMasterEdition(
{ feePayer: wallet.publicKey },
{
edition,
metadata,
updateAuthority: updateAuthority ? updateAuthority : wallet.publicKey,
mint: editionMint,
mintAuthority: wallet.publicKey,
maxSupply,
},
);
return sendTransaction({
connection,
signers: [],
txs: [createMetadataTx],
wallet,
});
};
35 changes: 35 additions & 0 deletions src/actions/createMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { CreateMetadata, Metadata, MetadataDataData } from '../programs/metadata';
import { sendTransaction } from './transactions';

interface CreateMetadataParams {
connection: Connection;
wallet: Wallet;
editionMint: PublicKey; // can be any mint with 0 decimals
metadataData: MetadataDataData;
updateAuthority?: PublicKey;
}

export const createMetadata = async (
{ connection, wallet, editionMint, metadataData, updateAuthority } = {} as CreateMetadataParams,
): Promise<string> => {
const metadata = await Metadata.getPDA(editionMint);

const createMetadataTx = new CreateMetadata(
{ feePayer: wallet.publicKey },
{
metadata,
metadataData,
updateAuthority: updateAuthority ? updateAuthority : wallet.publicKey,
mint: editionMint,
mintAuthority: wallet.publicKey,
},
);
return sendTransaction({
connection,
signers: [],
txs: [createMetadataTx],
wallet,
});
};
85 changes: 85 additions & 0 deletions src/actions/mintEditionFromMaster.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { ASSOCIATED_TOKEN_PROGRAM_ID, Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { Wallet } from '../wallet';
import {
Edition,
EditionMarker,
MasterEdition,
Metadata,
MintNewEditionFromMasterEditionViaToken,
} from '../programs/metadata';
import { Account } from '../Account';
import BN from 'bn.js';
import { prepareTokenAccountAndMintTx } from './shared';
import { sendTransaction } from './transactions';

interface MintEditionFromMasterParams {
connection: Connection;
wallet: Wallet;
masterEditionMint: PublicKey;
updateAuthority?: PublicKey;
}

interface MintEditionFromMasterResponse {
txId: string;
mint: PublicKey;
metadata: PublicKey;
edition: PublicKey;
}

export const mintEditionFromMaser = async (
ilmoi marked this conversation as resolved.
Show resolved Hide resolved
{ connection, wallet, masterEditionMint, updateAuthority } = {} as MintEditionFromMasterParams,
): Promise<MintEditionFromMasterResponse> => {
const masterPDA = await MasterEdition.getPDA(masterEditionMint);
const masterMetaPDA = await Metadata.getPDA(masterEditionMint);
const masterInfo = await Account.getInfo(connection, masterPDA);
const masterData = new MasterEdition(masterPDA, masterInfo).data;

//take the current outstanding supply and increment by 1
const editionValue = masterData.supply.add(new BN(1));

const { mint, createMintTx, createAssociatedTokenAccountTx, mintToTx } =
await prepareTokenAccountAndMintTx(connection, wallet.publicKey);

const tokenAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
masterEditionMint,
wallet.publicKey,
);

const metadataPDA = await Metadata.getPDA(mint.publicKey);
const editionMarker = await EditionMarker.getPDA(masterEditionMint, editionValue);
const editionPDA = await Edition.getPDA(mint.publicKey);

const newEditionFromMasterTx = new MintNewEditionFromMasterEditionViaToken(
{ feePayer: wallet.publicKey },
{
edition: editionPDA, //empty, created inside program
metadata: metadataPDA, //empty, created inside program
updateAuthority: updateAuthority ? updateAuthority : wallet.publicKey,
ilmoi marked this conversation as resolved.
Show resolved Hide resolved
mint: mint.publicKey,
mintAuthority: wallet.publicKey,
masterEdition: masterPDA,
masterMetadata: masterMetaPDA,
editionMarker, // empty if this is the 1st limited edition being created
tokenOwner: wallet.publicKey,
tokenAccount,
editionValue,
},
);

const txId = await sendTransaction({
connection,
signers: [mint],
txs: [createMintTx, createAssociatedTokenAccountTx, mintToTx, newEditionFromMasterTx],
wallet,
});

return {
txId,
mint: mint.publicKey,
metadata: metadataPDA,
edition: editionPDA,
};
};
51 changes: 8 additions & 43 deletions src/actions/mintNFT.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { MintLayout, TOKEN_PROGRAM_ID, Token } from '@solana/spl-token';
import { Keypair, PublicKey } from '@solana/web3.js';
import { ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { PublicKey } from '@solana/web3.js';
import BN from 'bn.js';
import { Connection } from './../Connection';
import { MintTo, CreateAssociatedTokenAccount, CreateMint } from './../programs';
import { Connection } from '../Connection';
import {
CreateMasterEdition,
CreateMetadata,
Creator,
MasterEdition,
Metadata,
MetadataDataData,
} from './../programs/metadata';
import { Wallet } from './../wallet';
} from '../programs/metadata';
import { Wallet } from '../wallet';
import { sendTransaction } from './transactions';
import { lookup } from './../utils/metadata';
import { lookup } from '../utils/metadata';
import { prepareTokenAccountAndMintTx } from './shared';

interface MintNFTParams {
connection: Connection;
Expand All @@ -36,13 +34,12 @@ export const mintNFT = async ({
uri,
maxSupply,
}: MintNFTParams): Promise<MintNFTResponse> => {
const mint = Keypair.generate();
const { mint, createMintTx, createAssociatedTokenAccountTx, mintToTx } =
await prepareTokenAccountAndMintTx(connection, wallet.publicKey);

const metadataPDA = await Metadata.getPDA(mint.publicKey);
const editionPDA = await MasterEdition.getPDA(mint.publicKey);

const mintRent = await connection.getMinimumBalanceForRentExemption(MintLayout.span);

const {
name,
symbol,
Expand All @@ -64,14 +61,6 @@ export const mintNFT = async ({
return memo;
}, []);

const createMintTx = new CreateMint(
{ feePayer: wallet.publicKey },
{
newAccountPubkey: mint.publicKey,
lamports: mintRent,
},
);

const metadataData = new MetadataDataData({
name,
symbol,
Expand All @@ -93,30 +82,6 @@ export const mintNFT = async ({
},
);

const recipient = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mint.publicKey,
wallet.publicKey,
);

const createAssociatedTokenAccountTx = new CreateAssociatedTokenAccount(
{ feePayer: wallet.publicKey },
{
associatedTokenAddress: recipient,
splTokenMintAddress: mint.publicKey,
},
);

const mintToTx = new MintTo(
{ feePayer: wallet.publicKey },
{
mint: mint.publicKey,
dest: recipient,
amount: 1,
},
);

const masterEditionTx = new CreateMasterEdition(
{ feePayer: wallet.publicKey },
{
Expand Down
46 changes: 46 additions & 0 deletions src/actions/shared/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
MintLayout,
Token,
TOKEN_PROGRAM_ID,
} from '@solana/spl-token';
import { CreateAssociatedTokenAccount, CreateMint, MintTo } from '../../programs';

export async function prepareTokenAccountAndMintTx(connection: Connection, owner: PublicKey) {
const mint = Keypair.generate();
const mintRent = await connection.getMinimumBalanceForRentExemption(MintLayout.span);
const createMintTx = new CreateMint(
{ feePayer: owner },
{
newAccountPubkey: mint.publicKey,
lamports: mintRent,
},
);

const recipient = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mint.publicKey,
owner,
);

const createAssociatedTokenAccountTx = new CreateAssociatedTokenAccount(
{ feePayer: owner },
{
associatedTokenAddress: recipient,
splTokenMintAddress: mint.publicKey,
},
);

const mintToTx = new MintTo(
{ feePayer: owner },
{
mint: mint.publicKey,
dest: recipient,
amount: 1,
},
);

return { mint, createMintTx, createAssociatedTokenAccountTx, mintToTx };
}
30 changes: 30 additions & 0 deletions src/actions/signMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { Metadata, SignMetadata } from '../programs/metadata';
import { sendTransaction } from './transactions';

interface SignMetadataParams {
connection: Connection;
wallet: Wallet;
editionMint: PublicKey;
signer?: Keypair;
}

export const signMetadata = async (
{ connection, wallet, editionMint, signer } = {} as SignMetadataParams,
): Promise<string> => {
const metadata = await Metadata.getPDA(editionMint);
const signTx = new SignMetadata(
{ feePayer: wallet.publicKey },
{
metadata,
creator: signer ? signer.publicKey : wallet.publicKey,
},
);
return await sendTransaction({
connection,
signers: [signer ? signer : undefined],
ilmoi marked this conversation as resolved.
Show resolved Hide resolved
txs: [signTx],
wallet,
});
};
48 changes: 48 additions & 0 deletions src/actions/updateMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { Wallet } from '../wallet';
import { Metadata, MetadataDataData, UpdateMetadata } from '../programs/metadata';
import { sendTransaction } from './transactions';

interface UpdateMetadataParams {
connection: Connection;
wallet: Wallet;
editionMint: PublicKey;
newMetadataData?: MetadataDataData;
newUpdateAuthority?: PublicKey;
primarySaleHappened?: boolean;
}

/*
* Can be used to update any of the below 3:
* 1) data inside metadata, but only if it's mutable (which is only possible for MasterEditions)
* 2) updateAuthority
* 3) whether primary sale has happened (can only be set true, never back false)
*/
export const updateMetadata = async (
{
connection,
wallet,
editionMint,
newMetadataData,
newUpdateAuthority,
primarySaleHappened,
} = {} as UpdateMetadataParams,
): Promise<string> => {
const metadata = await Metadata.getPDA(editionMint);
const updateTx = new UpdateMetadata(
{ feePayer: wallet.publicKey },
{
metadata,
updateAuthority: wallet.publicKey,
metadataData: newMetadataData,
newUpdateAuthority,
primarySaleHappened,
},
);
return sendTransaction({
connection,
signers: [],
txs: [updateTx],
wallet,
});
};
Loading