More work
This commit is contained in:
parent
a1cc3b94c3
commit
fb58acaf54
|
@ -0,0 +1,756 @@
|
|||
#!/usr/bin/env node
|
||||
import * as fs from 'fs';
|
||||
import Arweave from 'arweave';
|
||||
import * as path from 'path';
|
||||
import fetch from 'node-fetch';
|
||||
import FormData from 'form-data';
|
||||
import { program } from 'commander';
|
||||
import * as anchor from '@project-serum/anchor';
|
||||
import BN from 'bn.js';
|
||||
import { MintLayout, Token } from '@solana/spl-token';
|
||||
|
||||
import { sendTransactionWithRetryWithKeypair, fromUTF8Array } from './helper';
|
||||
import {
|
||||
LAMPORTS_PER_SOL,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import { token } from '@project-serum/anchor/dist/utils';
|
||||
|
||||
const CACHE_PATH = './.cache';
|
||||
const PAYMENT_WALLET = new anchor.web3.PublicKey(
|
||||
'HvwC9QSAzvGXhhVrgPmauVwFWcYZhne3hVot9EbHuFTm',
|
||||
);
|
||||
const ENV = 'devnet';
|
||||
const CANDY_MACHINE = 'candy_machine';
|
||||
|
||||
const programId = new anchor.web3.PublicKey(
|
||||
'cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ',
|
||||
);
|
||||
const TOKEN_METADATA_PROGRAM_ID = new PublicKey(
|
||||
'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s',
|
||||
);
|
||||
|
||||
const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey(
|
||||
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
|
||||
);
|
||||
const TOKEN_PROGRAM_ID = new PublicKey(
|
||||
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
|
||||
);
|
||||
const getTokenWallet = async function (wallet: PublicKey, mint: PublicKey) {
|
||||
return (
|
||||
await PublicKey.findProgramAddress(
|
||||
[wallet.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
||||
)
|
||||
)[0];
|
||||
};
|
||||
|
||||
export function createAssociatedTokenAccountInstruction(
|
||||
associatedTokenAddress: PublicKey,
|
||||
payer: PublicKey,
|
||||
walletAddress: PublicKey,
|
||||
splTokenMintAddress: PublicKey,
|
||||
) {
|
||||
const keys = [
|
||||
{
|
||||
pubkey: payer,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: associatedTokenAddress,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: walletAddress,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: splTokenMintAddress,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: SystemProgram.programId,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: TOKEN_PROGRAM_ID,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{
|
||||
pubkey: SYSVAR_RENT_PUBKEY,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
];
|
||||
return new TransactionInstruction({
|
||||
keys,
|
||||
programId: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
||||
data: Buffer.from([]),
|
||||
});
|
||||
}
|
||||
|
||||
function chunks(array, size) {
|
||||
return Array.apply(0, new Array(Math.ceil(array.length / size))).map(
|
||||
(_, index) => array.slice(index * size, (index + 1) * size),
|
||||
);
|
||||
}
|
||||
|
||||
const configArrayStart =
|
||||
32 + // authority
|
||||
4 +
|
||||
6 + // uuid + u32 len
|
||||
4 +
|
||||
10 + // u32 len + symbol
|
||||
2 + // seller fee basis points
|
||||
1 +
|
||||
4 +
|
||||
5 * 34 + // optional + u32 len + actual vec
|
||||
8 + //max supply
|
||||
1 + //is mutable
|
||||
1 + // retain authority
|
||||
4; // max number of lines;
|
||||
const configLineSize = 4 + 32 + 4 + 200;
|
||||
program.version('0.0.1');
|
||||
|
||||
if (!fs.existsSync(CACHE_PATH)) {
|
||||
fs.mkdirSync(CACHE_PATH);
|
||||
}
|
||||
|
||||
const getCandyMachine = async (config: anchor.web3.PublicKey, uuid: string) => {
|
||||
return await anchor.web3.PublicKey.findProgramAddress(
|
||||
[Buffer.from(CANDY_MACHINE), config.toBuffer(), Buffer.from(uuid)],
|
||||
programId,
|
||||
);
|
||||
};
|
||||
|
||||
const getConfig = async (authority: anchor.web3.PublicKey, uuid: string) => {
|
||||
return await anchor.web3.PublicKey.findProgramAddress(
|
||||
[Buffer.from(CANDY_MACHINE), authority.toBuffer(), Buffer.from(uuid)],
|
||||
programId,
|
||||
);
|
||||
};
|
||||
|
||||
const getMetadata = async (
|
||||
mint: anchor.web3.PublicKey,
|
||||
): Promise<anchor.web3.PublicKey> => {
|
||||
return (
|
||||
await anchor.web3.PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('metadata'),
|
||||
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
|
||||
mint.toBuffer(),
|
||||
],
|
||||
TOKEN_METADATA_PROGRAM_ID,
|
||||
)
|
||||
)[0];
|
||||
};
|
||||
|
||||
const getMasterEdition = async (
|
||||
mint: anchor.web3.PublicKey,
|
||||
): Promise<anchor.web3.PublicKey> => {
|
||||
return (
|
||||
await anchor.web3.PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('metadata'),
|
||||
TOKEN_METADATA_PROGRAM_ID.toBuffer(),
|
||||
mint.toBuffer(),
|
||||
Buffer.from('edition'),
|
||||
],
|
||||
TOKEN_METADATA_PROGRAM_ID,
|
||||
)
|
||||
)[0];
|
||||
};
|
||||
|
||||
const createConfig = async function (
|
||||
anchorProgram: anchor.Program,
|
||||
payerWallet: anchor.web3.Keypair,
|
||||
configData: {
|
||||
maxNumberOfLines: BN;
|
||||
symbol: string;
|
||||
sellerFeeBasisPoints: number;
|
||||
isMutable: boolean;
|
||||
maxSupply: BN;
|
||||
retainAuthority: boolean;
|
||||
creators: {
|
||||
address: anchor.web3.PublicKey;
|
||||
verified: boolean;
|
||||
share: number;
|
||||
}[];
|
||||
},
|
||||
) {
|
||||
const size =
|
||||
configArrayStart +
|
||||
4 +
|
||||
configData.maxNumberOfLines.toNumber() * configLineSize +
|
||||
4 +
|
||||
Math.ceil(configData.maxNumberOfLines.toNumber() / 8);
|
||||
|
||||
const config = anchor.web3.Keypair.generate();
|
||||
const uuid = config.publicKey.toBase58().slice(0, 6);
|
||||
|
||||
return {
|
||||
config: config.publicKey,
|
||||
uuid,
|
||||
txId: await anchorProgram.rpc.initializeConfig(
|
||||
{
|
||||
uuid,
|
||||
...configData,
|
||||
},
|
||||
{
|
||||
accounts: {
|
||||
config: config.publicKey,
|
||||
authority: payerWallet.publicKey,
|
||||
payer: payerWallet.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
signers: [payerWallet, config],
|
||||
instructions: [
|
||||
anchor.web3.SystemProgram.createAccount({
|
||||
fromPubkey: payerWallet.publicKey,
|
||||
newAccountPubkey: config.publicKey,
|
||||
space: size,
|
||||
lamports:
|
||||
await anchorProgram.provider.connection.getMinimumBalanceForRentExemption(
|
||||
size,
|
||||
),
|
||||
programId: programId,
|
||||
}),
|
||||
],
|
||||
},
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
program
|
||||
.command('upload')
|
||||
.argument(
|
||||
'<directory>',
|
||||
'Directory containing images named from 0-n',
|
||||
val => {
|
||||
return fs.readdirSync(`${val}`).map(file => path.join(val, file));
|
||||
},
|
||||
)
|
||||
.option(
|
||||
'-u, --url',
|
||||
'Solana cluster url',
|
||||
'https://api.mainnet-beta.solana.com/',
|
||||
)
|
||||
.option('-k, --keypair <path>', 'Solana wallet')
|
||||
// .argument('[second]', 'integer argument', (val) => parseInt(val), 1000)
|
||||
.option('-s, --start-with', 'Image index to start with', '0')
|
||||
.option('-n, --number', 'Number of images to upload', '10000')
|
||||
.option('-c, --cache-name <path>', 'Cache file name')
|
||||
.action(async (files: string[], options, cmd) => {
|
||||
const extension = '.png';
|
||||
const { startWith, keypair } = cmd.opts();
|
||||
const cacheName = program.getOptionValue('cacheName') || 'temp';
|
||||
const cachePath = path.join(CACHE_PATH, cacheName);
|
||||
const savedContent = fs.existsSync(cachePath)
|
||||
? JSON.parse(fs.readFileSync(cachePath).toString())
|
||||
: undefined;
|
||||
const cacheContent = savedContent || {};
|
||||
|
||||
if (!cacheContent.program) {
|
||||
cacheContent.program = {};
|
||||
}
|
||||
|
||||
let existingInCache = [];
|
||||
if (!cacheContent.items) {
|
||||
cacheContent.items = {};
|
||||
} else {
|
||||
existingInCache = Object.keys(cacheContent.items);
|
||||
}
|
||||
|
||||
const seen = {};
|
||||
const newFiles = [];
|
||||
|
||||
files.forEach(f => {
|
||||
if (!seen[f.replace(extension, '').split('/').pop()]) {
|
||||
seen[f.replace(extension, '').split('/').pop()] = true;
|
||||
newFiles.push(f);
|
||||
}
|
||||
});
|
||||
existingInCache.forEach(f => {
|
||||
if (!seen[f]) {
|
||||
seen[f] = true;
|
||||
newFiles.push(f + '.png');
|
||||
}
|
||||
});
|
||||
|
||||
const images = newFiles.filter(val => path.extname(val) === extension);
|
||||
const SIZE = images.length; // images.length;
|
||||
const walletKey = anchor.web3.Keypair.fromSecretKey(
|
||||
new Uint8Array(JSON.parse(fs.readFileSync(keypair).toString())),
|
||||
);
|
||||
|
||||
// const conversionRates = JSON.parse(
|
||||
// await (
|
||||
// await fetch(
|
||||
// 'https://api.coingecko.com/api/v3/simple/price?ids=solana,arweave&vs_currencies=usd',
|
||||
// )
|
||||
// ).text(),
|
||||
// );
|
||||
// const baseCost = fetch(``);
|
||||
// const increment = fetch(``);
|
||||
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${ENV}.solana.com/`,
|
||||
);
|
||||
|
||||
const walletWrapper = new anchor.Wallet(walletKey);
|
||||
const provider = new anchor.Provider(solConnection, walletWrapper, {
|
||||
preflightCommitment: 'recent',
|
||||
});
|
||||
const idl = await anchor.Program.fetchIdl(programId, provider);
|
||||
const anchorProgram = new anchor.Program(idl, programId, provider);
|
||||
let config = cacheContent.program.config
|
||||
? new anchor.web3.PublicKey(cacheContent.program.config)
|
||||
: undefined;
|
||||
|
||||
const block = await solConnection.getRecentBlockhash();
|
||||
for (let i = 0; i < SIZE; i++) {
|
||||
const image = images[i];
|
||||
const imageName = path.basename(image);
|
||||
const index = imageName.replace(extension, '');
|
||||
|
||||
console.log(`Processing file: ${index}`);
|
||||
|
||||
const storageCost = 10;
|
||||
|
||||
let link = cacheContent?.items?.[index]?.link;
|
||||
if (!link || !cacheContent.program.uuid) {
|
||||
const imageBuffer = Buffer.from(fs.readFileSync(image));
|
||||
const manifestPath = image.replace(extension, '.json');
|
||||
const manifestContent = fs
|
||||
.readFileSync(manifestPath)
|
||||
.toString()
|
||||
.replace(imageName, 'image.png')
|
||||
.replace(imageName, 'image.png');
|
||||
const manifest = JSON.parse(manifestContent);
|
||||
|
||||
const manifestBuffer = Buffer.from(JSON.stringify(manifest));
|
||||
const sizeInBytes = imageBuffer.length + manifestBuffer.length;
|
||||
|
||||
if (i === 0 && !cacheContent.program.uuid) {
|
||||
// initialize config
|
||||
try {
|
||||
const res = await createConfig(anchorProgram, walletKey, {
|
||||
maxNumberOfLines: new BN(SIZE),
|
||||
symbol: manifest.symbol,
|
||||
sellerFeeBasisPoints: manifest.seller_fee_basis_points,
|
||||
isMutable: true,
|
||||
maxSupply: new BN(0),
|
||||
retainAuthority: true,
|
||||
creators: manifest.properties.creators.map(creator => {
|
||||
return {
|
||||
address: new anchor.web3.PublicKey(creator.address),
|
||||
verified: false,
|
||||
share: creator.share,
|
||||
};
|
||||
}),
|
||||
});
|
||||
cacheContent.program.uuid = res.uuid;
|
||||
cacheContent.program.config = res.config.toBase58();
|
||||
config = res.config;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(CACHE_PATH, cacheName),
|
||||
JSON.stringify(cacheContent),
|
||||
);
|
||||
} catch (exx) {
|
||||
console.error('Error deploying config to Solana network.', exx);
|
||||
// console.error(exx);
|
||||
}
|
||||
}
|
||||
|
||||
if (!link) {
|
||||
let instructions = [
|
||||
anchor.web3.SystemProgram.transfer({
|
||||
fromPubkey: walletKey.publicKey,
|
||||
toPubkey: PAYMENT_WALLET,
|
||||
lamports: storageCost,
|
||||
}),
|
||||
];
|
||||
|
||||
const tx = await sendTransactionWithRetryWithKeypair(
|
||||
solConnection,
|
||||
walletKey,
|
||||
instructions,
|
||||
[],
|
||||
'single',
|
||||
);
|
||||
console.info('transaction for arweave payment:', tx);
|
||||
|
||||
// data.append('tags', JSON.stringify(tags));
|
||||
// payment transaction
|
||||
const data = new FormData();
|
||||
data.append('transaction', tx['txid']);
|
||||
data.append('env', ENV);
|
||||
data.append('file[]', fs.createReadStream(image), `image.png`);
|
||||
data.append('file[]', manifestBuffer, 'metadata.json');
|
||||
try {
|
||||
const result = await (
|
||||
await fetch(
|
||||
'https://us-central1-principal-lane-200702.cloudfunctions.net/uploadFile3',
|
||||
{
|
||||
method: 'POST',
|
||||
body: data,
|
||||
},
|
||||
)
|
||||
).json();
|
||||
|
||||
const metadataFile = result.messages?.find(
|
||||
m => m.filename === 'manifest.json',
|
||||
);
|
||||
if (metadataFile?.transactionId) {
|
||||
link = `https://arweave.net/${metadataFile.transactionId}`;
|
||||
console.log(`File uploaded: ${link}`);
|
||||
}
|
||||
|
||||
cacheContent.items[index] = {
|
||||
link,
|
||||
name: manifest.name,
|
||||
onChain: false,
|
||||
};
|
||||
fs.writeFileSync(
|
||||
path.join(CACHE_PATH, cacheName),
|
||||
JSON.stringify(cacheContent),
|
||||
);
|
||||
} catch (er) {
|
||||
console.error(`Error uploading file ${index}`, er);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const keys = Object.keys(cacheContent.items);
|
||||
try {
|
||||
await Promise.all(
|
||||
chunks(Array.from(Array(keys.length).keys()), 1000).map(
|
||||
async allIndexesInSlice => {
|
||||
for (
|
||||
let offset = 0;
|
||||
offset < allIndexesInSlice.length;
|
||||
offset += 10
|
||||
) {
|
||||
const indexes = allIndexesInSlice.slice(offset, offset + 10);
|
||||
const onChain = indexes.filter(i => {
|
||||
const index = keys[i];
|
||||
return cacheContent.items[index]?.onChain;
|
||||
});
|
||||
const ind = keys[indexes[0]];
|
||||
|
||||
if (onChain.length != indexes.length) {
|
||||
console.log(
|
||||
'Writing indices ',
|
||||
ind,
|
||||
'-',
|
||||
keys[indexes[indexes.length - 1]],
|
||||
);
|
||||
const txId = await anchorProgram.rpc.addConfigLines(
|
||||
ind,
|
||||
indexes.map(i => ({
|
||||
uri: cacheContent.items[keys[i]].link,
|
||||
name: cacheContent.items[keys[i]].name,
|
||||
})),
|
||||
{
|
||||
accounts: {
|
||||
config,
|
||||
authority: walletKey.publicKey,
|
||||
},
|
||||
signers: [walletKey],
|
||||
},
|
||||
);
|
||||
indexes.forEach(i => {
|
||||
cacheContent.items[keys[i]] = {
|
||||
...cacheContent.items[keys[i]],
|
||||
onChain: true,
|
||||
};
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(CACHE_PATH, cacheName),
|
||||
JSON.stringify(cacheContent),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
fs.writeFileSync(
|
||||
path.join(CACHE_PATH, cacheName),
|
||||
JSON.stringify(cacheContent),
|
||||
);
|
||||
}
|
||||
console.log('Done');
|
||||
// TODO: start candy machine
|
||||
});
|
||||
program
|
||||
.command('set_start_date')
|
||||
.option('-k, --keypair <path>', 'Solana wallet')
|
||||
.option('-c, --cache-name <path>', 'Cache file name')
|
||||
.option('-d, --date <string>', 'timestamp - eg "04 Dec 1995 00:12:00 GMT"')
|
||||
.action(async (directory, cmd) => {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${ENV}.solana.com/`,
|
||||
);
|
||||
|
||||
const { keypair } = cmd.opts();
|
||||
|
||||
const cacheName = cmd.getOptionValue('cacheName') || 'temp';
|
||||
const cachePath = path.join(CACHE_PATH, cacheName);
|
||||
const cachedContent = fs.existsSync(cachePath)
|
||||
? JSON.parse(fs.readFileSync(cachePath).toString())
|
||||
: undefined;
|
||||
|
||||
const date = cmd.getOptionValue('date');
|
||||
const secondsSinceEpoch = (date ? Date.parse(date) : Date.now()) / 1000;
|
||||
const walletKey = anchor.web3.Keypair.fromSecretKey(
|
||||
new Uint8Array(JSON.parse(fs.readFileSync(keypair).toString())),
|
||||
);
|
||||
const walletWrapper = new anchor.Wallet(walletKey);
|
||||
const provider = new anchor.Provider(solConnection, walletWrapper, {
|
||||
preflightCommitment: 'recent',
|
||||
});
|
||||
const idl = await anchor.Program.fetchIdl(programId, provider);
|
||||
const anchorProgram = new anchor.Program(idl, programId, provider);
|
||||
const [candyMachine, _] = await getCandyMachine(
|
||||
new anchor.web3.PublicKey(cachedContent.program.config),
|
||||
cachedContent.program.uuid,
|
||||
);
|
||||
const tx = await anchorProgram.rpc.updateCandyMachine(
|
||||
null,
|
||||
new anchor.BN(secondsSinceEpoch),
|
||||
{
|
||||
accounts: {
|
||||
candyMachine,
|
||||
authority: walletKey.publicKey,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
console.log('Done', secondsSinceEpoch, tx);
|
||||
});
|
||||
|
||||
program
|
||||
.command('create_candy_machine')
|
||||
.option('-k, --keypair <path>', 'Solana wallet')
|
||||
.option('-c, --cache-name <path>', 'Cache file name')
|
||||
.option('-p, --price <string>', 'SOL price')
|
||||
.action(async (directory, cmd) => {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${ENV}.solana.com/`,
|
||||
);
|
||||
|
||||
const { keypair } = cmd.opts();
|
||||
const solPriceStr = cmd.getOptionValue('price') || '1';
|
||||
|
||||
const lamports = parseInt(solPriceStr) * LAMPORTS_PER_SOL;
|
||||
|
||||
const cacheName = program.getOptionValue('cacheName') || 'temp';
|
||||
const cachePath = path.join(CACHE_PATH, cacheName);
|
||||
const cachedContent = fs.existsSync(cachePath)
|
||||
? JSON.parse(fs.readFileSync(cachePath).toString())
|
||||
: undefined;
|
||||
|
||||
const walletKey = anchor.web3.Keypair.fromSecretKey(
|
||||
new Uint8Array(JSON.parse(fs.readFileSync(keypair).toString())),
|
||||
);
|
||||
const walletWrapper = new anchor.Wallet(walletKey);
|
||||
const provider = new anchor.Provider(solConnection, walletWrapper, {
|
||||
preflightCommitment: 'recent',
|
||||
});
|
||||
const idl = await anchor.Program.fetchIdl(programId, provider);
|
||||
const anchorProgram = new anchor.Program(idl, programId, provider);
|
||||
const config = new anchor.web3.PublicKey(cachedContent.program.config);
|
||||
const [candyMachine, bump] = await getCandyMachine(
|
||||
config,
|
||||
cachedContent.program.uuid,
|
||||
);
|
||||
const tx = await anchorProgram.rpc.initializeCandyMachine(
|
||||
bump,
|
||||
{
|
||||
uuid: cachedContent.program.uuid,
|
||||
price: new anchor.BN(lamports),
|
||||
itemsAvailable: new anchor.BN(Object.keys(cachedContent.items).length),
|
||||
goLiveDate: null,
|
||||
},
|
||||
{
|
||||
accounts: {
|
||||
candyMachine,
|
||||
wallet: walletKey.publicKey,
|
||||
config: config,
|
||||
authority: walletKey.publicKey,
|
||||
payer: walletKey.publicKey,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
signers: [],
|
||||
},
|
||||
);
|
||||
|
||||
console.log(`Done: CANDYMACHINE: ${candyMachine.toBase58()}`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('mint_one_token')
|
||||
.option('-k, --keypair <path>', `The purchaser's wallet key`)
|
||||
.option('-c, --cache-name <path>', 'Cache file name')
|
||||
.action(async (directory, cmd) => {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${ENV}.solana.com/`,
|
||||
);
|
||||
|
||||
const { keypair } = cmd.opts();
|
||||
const solPriceStr = program.getOptionValue('price') || '1';
|
||||
const lamports = parseInt(solPriceStr) * LAMPORTS_PER_SOL;
|
||||
|
||||
const cacheName = program.getOptionValue('cacheName') || 'temp';
|
||||
const cachePath = path.join(CACHE_PATH, cacheName);
|
||||
const cachedContent = fs.existsSync(cachePath)
|
||||
? JSON.parse(fs.readFileSync(cachePath).toString())
|
||||
: undefined;
|
||||
const mint = anchor.web3.Keypair.generate();
|
||||
|
||||
const walletKey = anchor.web3.Keypair.fromSecretKey(
|
||||
new Uint8Array(JSON.parse(fs.readFileSync(keypair).toString())),
|
||||
);
|
||||
const token = await getTokenWallet(walletKey.publicKey, mint.publicKey);
|
||||
const walletWrapper = new anchor.Wallet(walletKey);
|
||||
const provider = new anchor.Provider(solConnection, walletWrapper, {
|
||||
preflightCommitment: 'recent',
|
||||
});
|
||||
const idl = await anchor.Program.fetchIdl(programId, provider);
|
||||
const anchorProgram = new anchor.Program(idl, programId, provider);
|
||||
const config = new anchor.web3.PublicKey(cachedContent.program.config);
|
||||
const [candyMachine, bump] = await getCandyMachine(
|
||||
config,
|
||||
cachedContent.program.uuid,
|
||||
);
|
||||
const candy = await anchorProgram.account.candyMachine.fetch(candyMachine);
|
||||
const metadata = await getMetadata(mint.publicKey);
|
||||
const masterEdition = await getMasterEdition(mint.publicKey);
|
||||
const tx = await anchorProgram.rpc.mintNft({
|
||||
accounts: {
|
||||
config: config,
|
||||
candyMachine: candyMachine,
|
||||
payer: walletKey.publicKey,
|
||||
//@ts-ignore
|
||||
wallet: candy.wallet,
|
||||
mint: mint.publicKey,
|
||||
metadata,
|
||||
masterEdition,
|
||||
mintAuthority: walletKey.publicKey,
|
||||
updateAuthority: walletKey.publicKey,
|
||||
tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
systemProgram: SystemProgram.programId,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
|
||||
},
|
||||
signers: [mint, walletKey],
|
||||
instructions: [
|
||||
anchor.web3.SystemProgram.createAccount({
|
||||
fromPubkey: walletKey.publicKey,
|
||||
newAccountPubkey: mint.publicKey,
|
||||
space: MintLayout.span,
|
||||
lamports: await provider.connection.getMinimumBalanceForRentExemption(
|
||||
MintLayout.span,
|
||||
),
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
}),
|
||||
Token.createInitMintInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mint.publicKey,
|
||||
0,
|
||||
walletKey.publicKey,
|
||||
walletKey.publicKey,
|
||||
),
|
||||
createAssociatedTokenAccountInstruction(
|
||||
token,
|
||||
walletKey.publicKey,
|
||||
walletKey.publicKey,
|
||||
mint.publicKey,
|
||||
),
|
||||
Token.createMintToInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mint.publicKey,
|
||||
token,
|
||||
walletKey.publicKey,
|
||||
[],
|
||||
1,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
console.log('Done', tx);
|
||||
});
|
||||
program
|
||||
.command('verify')
|
||||
.option('-c, --cache-name <path>', 'Cache file name')
|
||||
.action(async (directory, second, options) => {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${ENV}.solana.com/`,
|
||||
);
|
||||
const cacheName = program.getOptionValue('cacheName') || 'temp';
|
||||
const cachePath = path.join(CACHE_PATH, cacheName);
|
||||
const cachedContent = fs.existsSync(cachePath)
|
||||
? JSON.parse(fs.readFileSync(cachePath).toString())
|
||||
: undefined;
|
||||
|
||||
const config = await solConnection.getAccountInfo(
|
||||
new PublicKey(cachedContent.program.config),
|
||||
);
|
||||
const number = new BN(config.data.slice(247, 247 + 4), undefined, 'le');
|
||||
console.log('Number', number.toNumber());
|
||||
|
||||
const keys = Object.keys(cachedContent.items);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
console.log('Looking at key ', i);
|
||||
const key = keys[i];
|
||||
const thisSlice = config.data.slice(
|
||||
configArrayStart + 4 + configLineSize * i,
|
||||
configArrayStart + 4 + configLineSize * (i + 1),
|
||||
);
|
||||
const name = fromUTF8Array([...thisSlice.slice(4, 36)]);
|
||||
const uri = fromUTF8Array([...thisSlice.slice(40, 240)]);
|
||||
const cacheItem = cachedContent.items[key];
|
||||
if (!name.match(cacheItem.name) || !uri.match(cacheItem.link)) {
|
||||
console.log(
|
||||
'Name',
|
||||
name,
|
||||
'or uri',
|
||||
uri,
|
||||
'didnt match cache values of',
|
||||
cacheItem.name,
|
||||
'and',
|
||||
cacheItem.link,
|
||||
' marking to rerun for image',
|
||||
key,
|
||||
);
|
||||
cacheItem.onChain = false;
|
||||
} else {
|
||||
console.log('Name', name, 'with', uri, 'checked out');
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(CACHE_PATH, cacheName),
|
||||
JSON.stringify(cachedContent),
|
||||
);
|
||||
});
|
||||
|
||||
program.command('find-wallets').action(() => {});
|
||||
|
||||
program.parse(process.argv);
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue