COmmit to reproduce
This commit is contained in:
parent
43601098c2
commit
46693dc6dd
|
@ -34,7 +34,7 @@ import {
|
|||
getMasterEdition,
|
||||
getMetadata,
|
||||
getTokenWallet,
|
||||
loadAnchorProgram,
|
||||
loadCandyProgram,
|
||||
loadWalletKey,
|
||||
} from './helpers/accounts';
|
||||
import { Config } from './types';
|
||||
|
@ -124,7 +124,7 @@ program
|
|||
const SIZE = images.length;
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
const anchorProgram = await loadCandyProgram(walletKeyPair, env);
|
||||
|
||||
let config = cacheContent.program.config
|
||||
? new PublicKey(cacheContent.program.config)
|
||||
|
@ -321,7 +321,7 @@ program
|
|||
|
||||
const cacheContent = loadCache(cacheName, env);
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
const anchorProgram = await loadCandyProgram(walletKeyPair, env);
|
||||
|
||||
const configAddress = new PublicKey(cacheContent.program.config);
|
||||
const config = await anchorProgram.provider.connection.getAccountInfo(
|
||||
|
@ -413,7 +413,7 @@ program
|
|||
const cacheContent = loadCache(cacheName, env);
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
const anchorProgram = await loadCandyProgram(walletKeyPair, env);
|
||||
|
||||
const config = new PublicKey(cacheContent.program.config);
|
||||
const [candyMachine, bump] = await getCandyMachineAddress(
|
||||
|
@ -466,7 +466,7 @@ program
|
|||
const secondsSinceEpoch = (date ? Date.parse(date) : Date.now()) / 1000;
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
const anchorProgram = await loadCandyProgram(walletKeyPair, env);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [candyMachine, _] = await getCandyMachineAddress(
|
||||
|
@ -507,7 +507,7 @@ program
|
|||
const mint = Keypair.generate();
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadAnchorProgram(walletKeyPair, env);
|
||||
const anchorProgram = await loadCandyProgram(walletKeyPair, env);
|
||||
const userTokenAccountAddress = await getTokenWallet(
|
||||
walletKeyPair.publicKey,
|
||||
mint.publicKey,
|
||||
|
|
|
@ -1,756 +1,178 @@
|
|||
#!/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 { LAMPORTS_PER_SOL } from '@solana/web3.js';
|
||||
|
||||
import { sendTransactionWithRetryWithKeypair, fromUTF8Array } from './helper';
|
||||
import { CACHE_PATH, TOKEN_PROGRAM_ID } from './helpers/constants';
|
||||
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;
|
||||
loadFairLaunchProgram,
|
||||
loadWalletKey,
|
||||
getTokenMint,
|
||||
getFairLaunch,
|
||||
getTreasury,
|
||||
} from './helpers/accounts';
|
||||
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));
|
||||
},
|
||||
.command('new_fair_launch')
|
||||
.option(
|
||||
'-e, --env <string>',
|
||||
'Solana cluster env name',
|
||||
'devnet', //mainnet-beta, testnet, devnet
|
||||
)
|
||||
.option(
|
||||
'-u, --url',
|
||||
'Solana cluster url',
|
||||
'https://api.mainnet-beta.solana.com/',
|
||||
'-k, --keypair <path>',
|
||||
`Solana wallet location`,
|
||||
'--keypair not provided',
|
||||
)
|
||||
.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 || {};
|
||||
.option('-u, --uuid <string>', 'uuid')
|
||||
.option('-f, --fee <string>', 'price range end', '2')
|
||||
.option('-s, --price-range-start <string>', 'price range start', '1')
|
||||
.option('-e, --price-range-end <string>', 'price range end', '2')
|
||||
.option(
|
||||
'-pos, --phase-one-start-date <string>',
|
||||
'timestamp - eg "04 Dec 1995 00:12:00 GMT"',
|
||||
)
|
||||
.option(
|
||||
'-poe, --phase-one-end-date <string>',
|
||||
'timestamp - eg "04 Dec 1995 00:12:00 GMT"',
|
||||
)
|
||||
.option(
|
||||
'-pte, --phase-two-end-date <string>',
|
||||
'timestamp - eg "04 Dec 1995 00:12:00 GMT"',
|
||||
)
|
||||
.option('-ts, --tick-size <string>', 'tick size', '0.1')
|
||||
.option('-n, --number-of-tokens <number>', 'Number of tokens to sell')
|
||||
.option(
|
||||
'-mint, --token-mint <string>',
|
||||
'token mint to take as payment instead of sol',
|
||||
)
|
||||
.action(async (_, cmd) => {
|
||||
const {
|
||||
keypair,
|
||||
env,
|
||||
priceRangeStart,
|
||||
priceRangeEnd,
|
||||
phaseOneStartDate,
|
||||
phaseOneEndDate,
|
||||
phaseTwoEndDate,
|
||||
tickSize,
|
||||
numberOfTokens,
|
||||
fee,
|
||||
mint,
|
||||
uuid,
|
||||
} = cmd.opts();
|
||||
const parsedNumber = parseInt(numberOfTokens);
|
||||
let priceRangeStartNumber = parseFloat(priceRangeStart);
|
||||
let priceRangeEndNumber = parseFloat(priceRangeEnd);
|
||||
let tickSizeNumber = parseFloat(tickSize);
|
||||
let feeNumber = parseFloat(fee);
|
||||
const realUuid = uuid.slice(0, 6);
|
||||
const phaseOneStartDateActual =
|
||||
(phaseOneStartDate ? Date.parse(phaseOneStartDate) : Date.now()) / 1000;
|
||||
const phaseOneEndDateActual =
|
||||
(phaseOneEndDate ? Date.parse(phaseOneEndDate) : Date.now() + 86400000) /
|
||||
1000;
|
||||
const phaseTwoEndDateActual =
|
||||
(phaseTwoEndDate
|
||||
? Date.parse(phaseTwoEndDate)
|
||||
: Date.now() + 2 * 86400000) / 1000;
|
||||
|
||||
if (!cacheContent.program) {
|
||||
cacheContent.program = {};
|
||||
if (!mint) {
|
||||
priceRangeStartNumber = Math.ceil(
|
||||
priceRangeStartNumber * LAMPORTS_PER_SOL,
|
||||
);
|
||||
priceRangeEndNumber = Math.ceil(priceRangeEndNumber * LAMPORTS_PER_SOL);
|
||||
tickSizeNumber = Math.ceil(tickSizeNumber * LAMPORTS_PER_SOL);
|
||||
feeNumber = Math.ceil(feeNumber * LAMPORTS_PER_SOL);
|
||||
}
|
||||
|
||||
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 walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadFairLaunchProgram(walletKeyPair, env);
|
||||
const [tokenMint, tokenBump] = await getTokenMint(
|
||||
walletKeyPair.publicKey,
|
||||
realUuid,
|
||||
);
|
||||
|
||||
// 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
const [fairLaunch, fairLaunchBump] = await getFairLaunch(tokenMint);
|
||||
const [treasury, treasuryBump] = await getTreasury(tokenMint);
|
||||
console.log('Mint is', mint);
|
||||
const remainingAccounts = !mint
|
||||
? []
|
||||
: [
|
||||
{
|
||||
pubkey: new anchor.web3.PublicKey(mint),
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
),
|
||||
);
|
||||
} 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/`,
|
||||
);
|
||||
];
|
||||
await anchorProgram.rpc.initializeFairLaunch(
|
||||
fairLaunchBump,
|
||||
treasuryBump,
|
||||
tokenBump,
|
||||
|
||||
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,
|
||||
uuid: realUuid,
|
||||
priceRangeStart: new anchor.BN(priceRangeStartNumber),
|
||||
priceRangeEnd: new anchor.BN(priceRangeEndNumber),
|
||||
phaseOneStart: new anchor.BN(phaseOneStartDateActual),
|
||||
phaseOneEnd: new anchor.BN(phaseOneEndDateActual),
|
||||
phaseTwoEnd: new anchor.BN(phaseTwoEndDateActual),
|
||||
tickSize: new anchor.BN(tickSizeNumber),
|
||||
numberOfTokens: new anchor.BN(parsedNumber),
|
||||
fee: new anchor.BN(feeNumber),
|
||||
},
|
||||
{
|
||||
accounts: {
|
||||
candyMachine,
|
||||
wallet: walletKey.publicKey,
|
||||
config: config,
|
||||
authority: walletKey.publicKey,
|
||||
payer: walletKey.publicKey,
|
||||
fairLaunch,
|
||||
tokenMint,
|
||||
treasury,
|
||||
authority: walletKeyPair.publicKey,
|
||||
payer: walletKeyPair.publicKey,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
systemProgram: anchor.web3.SystemProgram.programId,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
remainingAccounts,
|
||||
signers: [],
|
||||
},
|
||||
);
|
||||
|
||||
console.log(`Done: CANDYMACHINE: ${candyMachine.toBase58()}`);
|
||||
console.log(`create fair launch Done: ${fairLaunch.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/`,
|
||||
.command('show')
|
||||
.option(
|
||||
'-e, --env <string>',
|
||||
'Solana cluster env name',
|
||||
'devnet', //mainnet-beta, testnet, devnet
|
||||
)
|
||||
.option(
|
||||
'-k, --keypair <path>',
|
||||
`Solana wallet location`,
|
||||
'--keypair not provided',
|
||||
)
|
||||
.option('-u, --uuid <string>', 'uuid')
|
||||
.action(async (files: string[], options, cmd) => {
|
||||
const { env, uuid, keypair } = cmd.opts();
|
||||
const realUuid = uuid.slice(0, 6);
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadFairLaunchProgram(walletKeyPair, env);
|
||||
const tokenMint = (
|
||||
await getTokenMint(walletKeyPair.publicKey, realUuid)
|
||||
)[0];
|
||||
const fairLaunch = (await getFairLaunch(tokenMint))[0];
|
||||
|
||||
const fairLaunchObj = await anchorProgram.account.fairLaunch.fetch(
|
||||
fairLaunch,
|
||||
);
|
||||
|
||||
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);
|
||||
console.log('Fair launch object', fairLaunchObj);
|
||||
});
|
||||
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);
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import {Keypair, PublicKey, SystemProgram} from '@solana/web3.js';
|
||||
import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js';
|
||||
import {
|
||||
CANDY_MACHINE,
|
||||
CANDY_MACHINE_PROGRAM_ID,
|
||||
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
||||
TOKEN_METADATA_PROGRAM_ID,
|
||||
TOKEN_PROGRAM_ID,
|
||||
FAIR_LAUNCH_PROGRAM_ID,
|
||||
} from './constants';
|
||||
import * as anchor from '@project-serum/anchor';
|
||||
import fs from 'fs';
|
||||
import BN from "bn.js";
|
||||
import {createConfigAccount} from "./instructions";
|
||||
import BN from 'bn.js';
|
||||
import { createConfigAccount } from './instructions';
|
||||
|
||||
export const createConfig = async function (
|
||||
anchorProgram: anchor.Program,
|
||||
|
@ -93,6 +94,39 @@ export const getConfig = async (
|
|||
);
|
||||
};
|
||||
|
||||
export const getTokenMint = async (
|
||||
authority: anchor.web3.PublicKey,
|
||||
uuid: string,
|
||||
): Promise<[anchor.web3.PublicKey, number]> => {
|
||||
return await anchor.web3.PublicKey.findProgramAddress(
|
||||
[
|
||||
Buffer.from('fair_launch'),
|
||||
authority.toBuffer(),
|
||||
Buffer.from('mint'),
|
||||
Buffer.from(uuid),
|
||||
],
|
||||
FAIR_LAUNCH_PROGRAM_ID,
|
||||
);
|
||||
};
|
||||
|
||||
export const getFairLaunch = async (
|
||||
tokenMint: anchor.web3.PublicKey,
|
||||
): Promise<[anchor.web3.PublicKey, number]> => {
|
||||
return await anchor.web3.PublicKey.findProgramAddress(
|
||||
[Buffer.from('fair_launch'), tokenMint.toBuffer()],
|
||||
FAIR_LAUNCH_PROGRAM_ID,
|
||||
);
|
||||
};
|
||||
|
||||
export const getTreasury = async (
|
||||
tokenMint: anchor.web3.PublicKey,
|
||||
): Promise<[anchor.web3.PublicKey, number]> => {
|
||||
return await anchor.web3.PublicKey.findProgramAddress(
|
||||
[Buffer.from('fair_launch'), tokenMint.toBuffer(), Buffer.from('treasury')],
|
||||
FAIR_LAUNCH_PROGRAM_ID,
|
||||
);
|
||||
};
|
||||
|
||||
export const getMetadata = async (
|
||||
mint: anchor.web3.PublicKey,
|
||||
): Promise<anchor.web3.PublicKey> => {
|
||||
|
@ -130,7 +164,7 @@ export function loadWalletKey(keypair): Keypair {
|
|||
);
|
||||
}
|
||||
|
||||
export async function loadAnchorProgram(walletKeyPair: Keypair, env: string) {
|
||||
export async function loadCandyProgram(walletKeyPair: Keypair, env: string) {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${env}.solana.com/`,
|
||||
);
|
||||
|
@ -142,3 +176,19 @@ export async function loadAnchorProgram(walletKeyPair: Keypair, env: string) {
|
|||
|
||||
return new anchor.Program(idl, CANDY_MACHINE_PROGRAM_ID, provider);
|
||||
}
|
||||
|
||||
export async function loadFairLaunchProgram(
|
||||
walletKeyPair: Keypair,
|
||||
env: string,
|
||||
) {
|
||||
const solConnection = new anchor.web3.Connection(
|
||||
`https://api.${env}.solana.com/`,
|
||||
);
|
||||
const walletWrapper = new anchor.Wallet(walletKeyPair);
|
||||
const provider = new anchor.Provider(solConnection, walletWrapper, {
|
||||
preflightCommitment: 'recent',
|
||||
});
|
||||
const idl = await anchor.Program.fetchIdl(FAIR_LAUNCH_PROGRAM_ID, provider);
|
||||
|
||||
return new anchor.Program(idl, FAIR_LAUNCH_PROGRAM_ID, provider);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey(
|
|||
export const TOKEN_PROGRAM_ID = new PublicKey(
|
||||
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
|
||||
);
|
||||
export const FAIR_LAUNCH_PROGRAM_ID = new PublicKey(
|
||||
'7HmfyvWK7LDohUL9TDAuGv9VFZHUce1SgYMkwti1xWwF',
|
||||
);
|
||||
|
||||
export const CONFIG_ARRAY_START =
|
||||
32 + // authority
|
||||
|
|
|
@ -8,14 +8,16 @@ use {
|
|||
},
|
||||
anchor_lang::{
|
||||
prelude::*,
|
||||
|
||||
solana_program::{program_pack::Pack, system_program, system_instruction, program::{invoke, invoke_signed}},
|
||||
solana_program::{
|
||||
program::{invoke, invoke_signed},
|
||||
program_pack::Pack,
|
||||
system_instruction, system_program,
|
||||
},
|
||||
AnchorDeserialize, AnchorSerialize,
|
||||
},
|
||||
anchor_spl::token::Mint,
|
||||
spl_token::{instruction::initialize_account2, state::Account},
|
||||
std::{str::FromStr, convert::TryFrom},
|
||||
|
||||
std::str::FromStr,
|
||||
};
|
||||
|
||||
pub const PREFIX: &str = "fair_launch";
|
||||
|
@ -57,6 +59,13 @@ pub mod fair_launch {
|
|||
fair_launch.treasury = *treasury_info.key;
|
||||
assert_derivation(ctx.program_id, treasury_info, treasury_seeds)?;
|
||||
|
||||
let signer_seeds = &[
|
||||
PREFIX.as_bytes(),
|
||||
token_mint_key.as_ref(),
|
||||
TREASURY.as_bytes(),
|
||||
&[fair_launch.treasury_bump],
|
||||
];
|
||||
|
||||
if ctx.remaining_accounts.len() > 0 {
|
||||
let treasury_mint_info = &ctx.remaining_accounts[0];
|
||||
let _treasury_mint: spl_token::state::Mint = assert_initialized(&treasury_mint_info)?;
|
||||
|
@ -67,13 +76,6 @@ pub mod fair_launch {
|
|||
|
||||
// make the treasury token account
|
||||
|
||||
let signer_seeds = &[
|
||||
PREFIX.as_bytes(),
|
||||
token_mint_key.as_ref(),
|
||||
TREASURY.as_bytes(),
|
||||
&[fair_launch.treasury_bump],
|
||||
];
|
||||
|
||||
create_or_allocate_account_raw(
|
||||
*ctx.accounts.token_program.key,
|
||||
treasury_info,
|
||||
|
@ -104,10 +106,16 @@ pub mod fair_launch {
|
|||
// Nothing to do but check that it does not already exist, we can begin transferring sol to it.
|
||||
if !treasury_info.data_is_empty()
|
||||
|| treasury_info.lamports() > 0
|
||||
|| treasury_info.owner != ctx.program_id
|
||||
|| treasury_info.owner != ctx.accounts.system_program.key
|
||||
{
|
||||
return Err(ErrorCode::TreasuryAlreadyExists.into());
|
||||
}
|
||||
|
||||
invoke_signed(
|
||||
&system_instruction::assign(treasury_info.key, &ctx.program_id),
|
||||
&[ctx.accounts.system_program.clone(), treasury_info.clone()],
|
||||
&[signer_seeds],
|
||||
)?;
|
||||
}
|
||||
|
||||
// now we do the counts.
|
||||
|
@ -273,6 +281,10 @@ pub mod fair_launch {
|
|||
|
||||
adjust_counts(fair_launch, amount, None)?;
|
||||
|
||||
let charged_amount = amount
|
||||
.checked_add(fair_launch.data.fee)
|
||||
.ok_or(ErrorCode::NumericalOverflowError)?;
|
||||
|
||||
if let Some(treasury_mint) = fair_launch.treasury_mint {
|
||||
let treasury_mint_info = &ctx.remaining_accounts[0];
|
||||
let _treasury_mint: spl_token::state::Mint = assert_initialized(&treasury_mint_info)?;
|
||||
|
@ -310,7 +322,7 @@ pub mod fair_launch {
|
|||
],
|
||||
)?;
|
||||
|
||||
if buyer_token_account.amount < amount {
|
||||
if buyer_token_account.amount < charged_amount {
|
||||
return Err(ErrorCode::NotEnoughTokens.into());
|
||||
}
|
||||
|
||||
|
@ -320,15 +332,15 @@ pub mod fair_launch {
|
|||
authority: transfer_authority_info.clone(),
|
||||
authority_signer_seeds: &[],
|
||||
token_program: token_program.clone(),
|
||||
amount: amount,
|
||||
amount: charged_amount,
|
||||
})?;
|
||||
} else {
|
||||
if buyer.lamports() < amount {
|
||||
if buyer.lamports() < charged_amount {
|
||||
return Err(ErrorCode::NotEnoughSOL.into());
|
||||
}
|
||||
|
||||
invoke(
|
||||
&system_instruction::transfer(buyer.key, ctx.accounts.treasury.key, amount),
|
||||
&system_instruction::transfer(buyer.key, ctx.accounts.treasury.key, charged_amount),
|
||||
&[
|
||||
buyer.clone(),
|
||||
ctx.accounts.treasury.clone(),
|
||||
|
@ -393,9 +405,8 @@ pub mod fair_launch {
|
|||
],
|
||||
)?;
|
||||
|
||||
|
||||
if fair_launch_ticket.state.clone() as u8 != FairLaunchTicketState::Unpunched as u8 {
|
||||
return Err(ErrorCode::InvalidFairLaunchTicketState.into())
|
||||
return Err(ErrorCode::InvalidFairLaunchTicketState.into());
|
||||
}
|
||||
|
||||
if fair_launch.phase_three_started {
|
||||
|
@ -421,9 +432,8 @@ pub mod fair_launch {
|
|||
}
|
||||
}
|
||||
} else if !buyer.is_signer {
|
||||
return Err(ErrorCode::DuringPhaseTwoAndOneBuyerMustBeSigner.into())
|
||||
return Err(ErrorCode::DuringPhaseTwoAndOneBuyerMustBeSigner.into());
|
||||
}
|
||||
|
||||
|
||||
if amount != 0 {
|
||||
assert_valid_amount(&fair_launch.data, amount)?;
|
||||
|
@ -568,7 +578,7 @@ pub mod fair_launch {
|
|||
let token_mint = &ctx.accounts.token_mint;
|
||||
|
||||
if fair_launch_ticket.state.clone() as u8 != FairLaunchTicketState::Unpunched as u8 {
|
||||
return Err(ErrorCode::InvalidFairLaunchTicketState.into())
|
||||
return Err(ErrorCode::InvalidFairLaunchTicketState.into());
|
||||
}
|
||||
|
||||
let (mask, index) = get_mask_and_index_for_seq(fair_launch_ticket.seq)?;
|
||||
|
@ -633,12 +643,14 @@ pub mod fair_launch {
|
|||
let treasury = &mut ctx.accounts.treasury;
|
||||
let authority = &mut ctx.accounts.authority;
|
||||
|
||||
if fair_launch.number_tickets_sold > fair_launch.number_tickets_dropped + fair_launch.number_tickets_punched {
|
||||
return Err(ErrorCode::CannotCashOutUntilAllRefundsAndPunchesHaveBeenProcessed.into())
|
||||
}
|
||||
if fair_launch.number_tickets_sold
|
||||
> fair_launch.number_tickets_dropped + fair_launch.number_tickets_punched
|
||||
{
|
||||
return Err(ErrorCode::CannotCashOutUntilAllRefundsAndPunchesHaveBeenProcessed.into());
|
||||
}
|
||||
|
||||
if !fair_launch.phase_three_started {
|
||||
return Err(ErrorCode::CannotCashOutUntilPhaseThree.into())
|
||||
return Err(ErrorCode::CannotCashOutUntilPhaseThree.into());
|
||||
}
|
||||
|
||||
let signer_seeds = [
|
||||
|
@ -652,7 +664,8 @@ pub mod fair_launch {
|
|||
let _treasury_mint: spl_token::state::Mint = assert_initialized(&treasury_mint_info)?;
|
||||
|
||||
let authority_token_account_info = &ctx.remaining_accounts[1];
|
||||
let authority_token_account: Account = assert_initialized(&authority_token_account_info)?;
|
||||
let authority_token_account: Account =
|
||||
assert_initialized(&authority_token_account_info)?;
|
||||
let treasury_account: Account = assert_initialized(treasury)?;
|
||||
|
||||
let token_program = &ctx.remaining_accounts[2];
|
||||
|
@ -707,7 +720,6 @@ pub mod fair_launch {
|
|||
)?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -719,6 +731,7 @@ pub struct InitializeFairLaunch<'info> {
|
|||
fair_launch: ProgramAccount<'info, FairLaunch>,
|
||||
#[account(init, seeds=[PREFIX.as_bytes(), authority.key.as_ref(), MINT.as_bytes(), data.uuid.as_bytes()], mint::authority=fair_launch, mint::decimals=0, payer=payer, bump=token_mint_bump)]
|
||||
token_mint: CpiAccount<'info, Mint>,
|
||||
#[account(mut)]
|
||||
treasury: AccountInfo<'info>,
|
||||
#[account(constraint= authority.data_is_empty() && authority.lamports() > 0)]
|
||||
authority: AccountInfo<'info>,
|
||||
|
@ -902,6 +915,7 @@ pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator
|
|||
8 + // phase two end
|
||||
8 + // tick size
|
||||
8 + // number of tokens
|
||||
8 + // fee
|
||||
8 + // number of tickets unseq'ed
|
||||
8 + // number of tickets sold
|
||||
8 + // number of tickets dropped
|
||||
|
@ -934,6 +948,7 @@ pub struct FairLaunchData {
|
|||
pub phase_two_end: i64,
|
||||
pub tick_size: u64,
|
||||
pub number_of_tokens: u64,
|
||||
pub fee: u64,
|
||||
}
|
||||
|
||||
#[account]
|
||||
|
@ -1060,5 +1075,5 @@ pub enum ErrorCode {
|
|||
#[msg("Cannot cash out until all refunds and punches (permissionless calls) have been processed. Use the CLI.")]
|
||||
CannotCashOutUntilAllRefundsAndPunchesHaveBeenProcessed,
|
||||
#[msg("Cannot cash out until phase three")]
|
||||
CannotCashOutUntilPhaseThree
|
||||
CannotCashOutUntilPhaseThree,
|
||||
}
|
||||
|
|
|
@ -183,8 +183,8 @@ pub fn assert_data_valid(data: &FairLaunchData) -> ProgramResult {
|
|||
}
|
||||
|
||||
let difference = data
|
||||
.price_range_start
|
||||
.checked_sub(data.price_range_end)
|
||||
.price_range_end
|
||||
.checked_sub(data.price_range_start)
|
||||
.ok_or(ErrorCode::NumericalOverflowError)?;
|
||||
let possible_valid_user_prices = difference
|
||||
.checked_div(data.tick_size)
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue