cli: add support for custom spl-token mints (#388)
This commit is contained in:
parent
308fb29a7d
commit
e9841d4bb1
|
@ -21,6 +21,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@project-serum/anchor": "^0.13.2",
|
||||
"@solana/spl-token": "^0.1.8",
|
||||
"arweave": "^1.10.16",
|
||||
"bn.js": "^5.2.0",
|
||||
"commander": "^8.1.0",
|
||||
|
|
|
@ -6,6 +6,7 @@ import * as anchor from '@project-serum/anchor';
|
|||
import BN from 'bn.js';
|
||||
|
||||
import { fromUTF8Array, parsePrice } from './helpers/various';
|
||||
import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
||||
import { PublicKey } from '@solana/web3.js';
|
||||
import { CACHE_PATH, CONFIG_ARRAY_START, CONFIG_LINE_SIZE, EXTENSION_JSON, EXTENSION_PNG, } from './helpers/constants';
|
||||
import { getCandyMachineAddress, loadAnchorProgram, loadWalletKey, } from './helpers/accounts';
|
||||
|
@ -144,16 +145,54 @@ programCommand('verify')
|
|||
});
|
||||
|
||||
programCommand('create_candy_machine')
|
||||
.option('-p, --price <string>', 'SOL price', '1')
|
||||
.option('-p, --price <string>', 'Price denominated in SOL or spl-token override', '1')
|
||||
.option('-t, --spl-token <string>', 'SPL token used to price NFT mint. To use SOL leave this empty.')
|
||||
.option('-t, --spl-token-account <string>', 'SPL token account that receives mint payments. Only required if spl-token is specified.')
|
||||
.action(async (directory, cmd) => {
|
||||
const { keypair, env, price, cacheName } = cmd.opts();
|
||||
const { keypair, env, price, cacheName, splToken, splTokenAccount } = cmd.opts();
|
||||
|
||||
const lamports = parsePrice(price);
|
||||
let parsedPrice = parsePrice(price);
|
||||
const cacheContent = loadCache(cacheName, env);
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
|
||||
let wallet = walletKeyPair.publicKey;
|
||||
const remainingAccounts = [];
|
||||
if (splToken || splTokenAccount) {
|
||||
if (!splToken) {
|
||||
throw new Error("If spl-token-account is set, spl-token must also be set")
|
||||
}
|
||||
const splTokenKey = new PublicKey(splToken);
|
||||
const splTokenAccountKey = new PublicKey(splTokenAccount);
|
||||
if (!splTokenAccount) {
|
||||
throw new Error("If spl-token is set, spl-token-account must also be set")
|
||||
}
|
||||
|
||||
const token = new Token(
|
||||
anchorProgram.provider.connection,
|
||||
splTokenKey,
|
||||
TOKEN_PROGRAM_ID,
|
||||
walletKeyPair
|
||||
);
|
||||
|
||||
const mintInfo = await token.getMintInfo();
|
||||
if (!mintInfo.isInitialized) {
|
||||
throw new Error(`The specified spl-token is not initialized`);
|
||||
}
|
||||
const tokenAccount = await token.getAccountInfo(splTokenAccountKey);
|
||||
if (!tokenAccount.isInitialized) {
|
||||
throw new Error(`The specified spl-token-account is not initialized`);
|
||||
}
|
||||
if (!tokenAccount.mint.equals(splTokenKey)) {
|
||||
throw new Error(`The spl-token-account's mint (${tokenAccount.mint.toString()}) does not match specified spl-token ${splTokenKey.toString()}`);
|
||||
}
|
||||
|
||||
wallet = splTokenAccountKey;
|
||||
parsedPrice = parsePrice(price, 10 ** mintInfo.decimals);
|
||||
remainingAccounts.push({ pubkey: splTokenKey, isWritable: false, isSigner: false });
|
||||
}
|
||||
|
||||
const config = new PublicKey(cacheContent.program.config);
|
||||
const [candyMachine, bump] = await getCandyMachineAddress(
|
||||
config,
|
||||
|
@ -163,14 +202,14 @@ programCommand('create_candy_machine')
|
|||
bump,
|
||||
{
|
||||
uuid: cacheContent.program.uuid,
|
||||
price: new anchor.BN(lamports),
|
||||
price: new anchor.BN(parsedPrice),
|
||||
itemsAvailable: new anchor.BN(Object.keys(cacheContent.items).length),
|
||||
goLiveDate: null,
|
||||
},
|
||||
{
|
||||
accounts: {
|
||||
candyMachine,
|
||||
wallet: walletKeyPair.publicKey,
|
||||
wallet,
|
||||
config: config,
|
||||
authority: walletKeyPair.publicKey,
|
||||
payer: walletKeyPair.publicKey,
|
||||
|
@ -178,6 +217,7 @@ programCommand('create_candy_machine')
|
|||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
signers: [],
|
||||
remainingAccounts,
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -215,12 +255,14 @@ programCommand('set_start_date')
|
|||
});
|
||||
|
||||
programCommand('mint_one_token')
|
||||
.option('-t, --spl-token-account <string>', 'SPL token account to payfrom')
|
||||
.action(async (directory, cmd) => {
|
||||
const {keypair, env, cacheName} = cmd.opts();
|
||||
const {keypair, env, cacheName, splTokenAccount} = cmd.opts();
|
||||
|
||||
const cacheContent = loadCache(cacheName, env);
|
||||
const configAddress = new PublicKey(cacheContent.program.config);
|
||||
const tx = await mint(keypair, env, configAddress);
|
||||
const splTokenAccountKey = splTokenAccount ? new PublicKey(splTokenAccount) : undefined;
|
||||
const tx = await mint(keypair, env, configAddress, splTokenAccountKey);
|
||||
|
||||
log.info('Done', tx);
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import * as anchor from "@project-serum/anchor";
|
|||
import { MintLayout, Token } from "@solana/spl-token";
|
||||
import { createAssociatedTokenAccountInstruction } from "../helpers/instructions";
|
||||
|
||||
export async function mint(keypair: string, env: string, configAddress: PublicKey): Promise<string> {
|
||||
export async function mint(keypair: string, env: string, configAddress: PublicKey, splTokenAccountKey?: PublicKey): Promise<string> {
|
||||
const mint = Keypair.generate();
|
||||
|
||||
const userKeyPair = loadWalletKey(keypair);
|
||||
|
@ -28,10 +28,36 @@ export async function mint(keypair: string, env: string, configAddress: PublicKe
|
|||
configAddress,
|
||||
uuid,
|
||||
);
|
||||
const candyMachine = await anchorProgram.account.candyMachine.fetch(
|
||||
const candyMachine : any = await anchorProgram.account.candyMachine.fetch(
|
||||
candyMachineAddress,
|
||||
);
|
||||
|
||||
const remainingAccounts = [];
|
||||
if (splTokenAccountKey) {
|
||||
const candyMachineTokenMintKey = candyMachine.tokenMint;
|
||||
if (!candyMachineTokenMintKey) {
|
||||
throw new Error('Candy machine data does not have token mint configured. Can\'t use spl-token-account');
|
||||
}
|
||||
const token = new Token(
|
||||
anchorProgram.provider.connection,
|
||||
candyMachine.tokenMint,
|
||||
TOKEN_PROGRAM_ID,
|
||||
userKeyPair
|
||||
);
|
||||
|
||||
const tokenAccount = await token.getAccountInfo(splTokenAccountKey);
|
||||
if (!candyMachine.tokenMint.equals(tokenAccount.mint)) {
|
||||
throw new Error(`Specified spl-token-account's mint (${tokenAccount.mint.toString()}) does not match candy machine's token mint (${candyMachine.tokenMint.toString()})`);
|
||||
}
|
||||
|
||||
if (!tokenAccount.owner.equals(userKeyPair.publicKey)) {
|
||||
throw new Error(`Specified spl-token-account's owner (${tokenAccount.owner.toString()}) does not match user public key (${userKeyPair.publicKey})`);
|
||||
}
|
||||
|
||||
remainingAccounts.push({ pubkey: splTokenAccountKey, isWritable: true, isSigner: false });
|
||||
remainingAccounts.push({ pubkey: userKeyPair.publicKey, isWritable: false, isSigner: true });
|
||||
}
|
||||
|
||||
const metadataAddress = await getMetadata(mint.publicKey);
|
||||
const masterEdition = await getMasterEdition(mint.publicKey);
|
||||
return await anchorProgram.rpc.mintNft({
|
||||
|
@ -53,6 +79,7 @@ export async function mint(keypair: string, env: string, configAddress: PublicKe
|
|||
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
|
||||
},
|
||||
signers: [mint, userKeyPair],
|
||||
remainingAccounts,
|
||||
instructions: [
|
||||
anchor.web3.SystemProgram.createAccount({
|
||||
fromPubkey: userKeyPair.publicKey,
|
||||
|
|
|
@ -48,6 +48,6 @@ export function fromUTF8Array(data: number[]) {
|
|||
return str;
|
||||
}
|
||||
|
||||
export function parsePrice(price) {
|
||||
return Math.ceil(parseFloat(price) * LAMPORTS_PER_SOL);
|
||||
export function parsePrice(price: string, mantissa: number = LAMPORTS_PER_SOL) {
|
||||
return Math.ceil(parseFloat(price) * mantissa);
|
||||
}
|
||||
|
|
14
js/yarn.lock
14
js/yarn.lock
|
@ -1818,6 +1818,18 @@
|
|||
buffer-layout "^1.2.0"
|
||||
dotenv "10.0.0"
|
||||
|
||||
"@solana/spl-token@^0.1.8":
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6"
|
||||
integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.5"
|
||||
"@solana/web3.js" "^1.21.0"
|
||||
bn.js "^5.1.0"
|
||||
buffer "6.0.3"
|
||||
buffer-layout "^1.2.0"
|
||||
dotenv "10.0.0"
|
||||
|
||||
"@solana/wallet-adapter-base@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@solana/wallet-adapter-base/-/wallet-adapter-base-0.4.1.tgz#3264220c7eef5abaf7eca0e77cc51403f4f0fcf6"
|
||||
|
@ -7981,7 +7993,7 @@ log-update@^4.0.0:
|
|||
slice-ansi "^4.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
loglevel@^1.6.8:
|
||||
loglevel@^1.6.8, loglevel@^1.7.1:
|
||||
version "1.7.1"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
|
||||
integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
|
||||
|
|
Loading…
Reference in New Issue