Mooore stuff
This commit is contained in:
parent
210a956a02
commit
0d08cc0b1c
|
@ -20,13 +20,17 @@ import {
|
|||
getFairLaunchTicketSeqLookup,
|
||||
getFairLaunchLotteryBitmap,
|
||||
} from './helpers/accounts';
|
||||
import { sleep } from './helpers/various';
|
||||
import { chunks, getMultipleAccounts, sleep } from './helpers/various';
|
||||
program.version('0.0.1');
|
||||
|
||||
if (!fs.existsSync(CACHE_PATH)) {
|
||||
fs.mkdirSync(CACHE_PATH);
|
||||
}
|
||||
|
||||
const FAIR_LAUNCH_TICKET_AMOUNT_LOC = 8 + 32 + 32;
|
||||
const FAIR_LAUNCH_TICKET_STATE_LOC = FAIR_LAUNCH_TICKET_AMOUNT_LOC + 8;
|
||||
const FAIR_LAUNCH_TICKET_SEQ_LOC = FAIR_LAUNCH_TICKET_STATE_LOC + 1 + 1;
|
||||
|
||||
program
|
||||
.command('new_fair_launch')
|
||||
.option(
|
||||
|
@ -154,6 +158,111 @@ program
|
|||
console.log(`create fair launch Done: ${fairLaunch.toBase58()}`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('update_fair_launch')
|
||||
.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')
|
||||
.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 (!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);
|
||||
}
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadFairLaunchProgram(walletKeyPair, env);
|
||||
const tokenMint = (
|
||||
await getTokenMint(walletKeyPair.publicKey, realUuid)
|
||||
)[0];
|
||||
const fairLaunch = (await getFairLaunch(tokenMint))[0];
|
||||
|
||||
await anchorProgram.rpc.updateFairLaunch(
|
||||
{
|
||||
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: {
|
||||
fairLaunch,
|
||||
authority: walletKeyPair.publicKey,
|
||||
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
console.log(`Updated fair launch Done: ${fairLaunch.toBase58()}`);
|
||||
});
|
||||
|
||||
program
|
||||
.command('purchase_ticket')
|
||||
.option(
|
||||
|
@ -320,9 +429,8 @@ program
|
|||
)
|
||||
)[0];
|
||||
|
||||
const fairLaunchLotteryBitmap = ( //@ts-ignore
|
||||
await getFairLaunchLotteryBitmap(fairLaunchObj.tokenMint)
|
||||
)[0];
|
||||
const fairLaunchLotteryBitmap = //@ts-ignore
|
||||
(await getFairLaunchLotteryBitmap(fairLaunchObj.tokenMint))[0];
|
||||
|
||||
const remainingAccounts = [];
|
||||
const instructions = [];
|
||||
|
@ -450,6 +558,181 @@ program
|
|||
`checked fair launch lottery bitmap, exists: ${fairLaunchLotteryBitmap.toBase58()}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const seqKeys = [];
|
||||
//@ts-ignore
|
||||
for (let i = 0; i < fairLaunchObj.number_tickets_sold; i++) {
|
||||
seqKeys.push(
|
||||
await getFairLaunchTicketSeqLookup(
|
||||
//@ts-ignore
|
||||
fairLaunchObj.tokenMint,
|
||||
new anchor.BN(i),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const ticketKeys: anchor.web3.PublicKey[][] = await Promise.all(
|
||||
chunks(Array.from(Array(seqKeys.length).keys()), 1000).map(
|
||||
async allIndexesInSlice => {
|
||||
let ticketKeys = [];
|
||||
for (let i = 0; i < allIndexesInSlice.length; i += 100) {
|
||||
console.log(
|
||||
'Pulling ticket seqs for slice',
|
||||
allIndexesInSlice[i],
|
||||
allIndexesInSlice[i + 100],
|
||||
);
|
||||
const slice = allIndexesInSlice
|
||||
.slice(i, i + 100)
|
||||
.map(index => seqKeys[index]);
|
||||
const result = await getMultipleAccounts(
|
||||
anchorProgram.provider.connection,
|
||||
slice,
|
||||
'recent',
|
||||
);
|
||||
ticketKeys = ticketKeys.concat(
|
||||
result.array.map(
|
||||
a =>
|
||||
new anchor.web3.PublicKey(
|
||||
new Uint8Array(a.data.slice(8, 8 + 32)),
|
||||
),
|
||||
),
|
||||
);
|
||||
return ticketKeys;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const ticketsFlattened = ticketKeys.flat();
|
||||
|
||||
const states: { seq: anchor.BN; eligible: boolean }[][] = await Promise.all(
|
||||
chunks(Array.from(Array(ticketsFlattened.length).keys()), 1000).map(
|
||||
async allIndexesInSlice => {
|
||||
let states = [];
|
||||
for (let i = 0; i < allIndexesInSlice.length; i += 100) {
|
||||
console.log(
|
||||
'Pulling states for slice',
|
||||
allIndexesInSlice[i],
|
||||
allIndexesInSlice[i + 100],
|
||||
);
|
||||
const slice = allIndexesInSlice
|
||||
.slice(i, i + 100)
|
||||
.map(index => ticketsFlattened[index]);
|
||||
const result = await getMultipleAccounts(
|
||||
anchorProgram.provider.connection,
|
||||
slice,
|
||||
'recent',
|
||||
);
|
||||
states = states.concat(
|
||||
result.array.map(a => ({
|
||||
seq: new anchor.BN(
|
||||
a.data.slice(
|
||||
FAIR_LAUNCH_TICKET_SEQ_LOC,
|
||||
FAIR_LAUNCH_TICKET_SEQ_LOC + 8,
|
||||
),
|
||||
undefined,
|
||||
'le',
|
||||
),
|
||||
eligible:
|
||||
a.data[FAIR_LAUNCH_TICKET_STATE_LOC] == 1 &&
|
||||
new anchor.BN(
|
||||
a.data.slice(
|
||||
FAIR_LAUNCH_TICKET_AMOUNT_LOC,
|
||||
FAIR_LAUNCH_TICKET_AMOUNT_LOC + 8,
|
||||
),
|
||||
undefined,
|
||||
'le',
|
||||
//@ts-ignore
|
||||
).toNumber() >= fairLaunchObj.currentMedian.toNumber(),
|
||||
})),
|
||||
);
|
||||
return states;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const statesFlat = states.flat();
|
||||
|
||||
//@ts-ignore;
|
||||
let numWinnersRemaining = fairLaunchObj.data.numberOfTokens;
|
||||
|
||||
let chosen: { seq: anchor.BN; eligible: boolean; chosen: boolean }[];
|
||||
if (numWinnersRemaining >= statesFlat.length) {
|
||||
console.log('More or equal nfts than winners, everybody wins.');
|
||||
chosen = statesFlat.map(s => ({ ...s, chosen: true }));
|
||||
} else {
|
||||
console.log('Doing lottery.');
|
||||
chosen = statesFlat.map(s => ({ ...s, chosen: false }));
|
||||
while (numWinnersRemaining > 0) {
|
||||
const rand = Math.round(Math.random() * (chosen.length - 1));
|
||||
chosen[rand].chosen = true;
|
||||
numWinnersRemaining--;
|
||||
}
|
||||
}
|
||||
const sorted = chosen.sort((a, b) => a.seq.toNumber() - b.seq.toNumber());
|
||||
console.log('Lottery results', sorted);
|
||||
|
||||
await Promise.all(
|
||||
// each 8 entries is 1 byte, we want to send up 1000 bytes at a time.
|
||||
// be specific here.
|
||||
chunks(Array.from(Array(sorted.length).keys()), 8 * 1000).map(
|
||||
async allIndexesInSlice => {
|
||||
const bytes = [];
|
||||
const correspondingArrayOfBits = [];
|
||||
const startingOffset = allIndexesInSlice[0];
|
||||
let positionFromRight = 7;
|
||||
let currByte = 0;
|
||||
let currByteAsBits = [];
|
||||
for (let i = 0; i < allIndexesInSlice.length; i++) {
|
||||
if (chosen[allIndexesInSlice[i]].chosen) {
|
||||
const mask = Math.pow(2, positionFromRight);
|
||||
currByte = currByte & mask;
|
||||
currByteAsBits.push(1);
|
||||
} else {
|
||||
currByteAsBits.push(0);
|
||||
}
|
||||
positionFromRight--;
|
||||
if (positionFromRight < 0) {
|
||||
bytes.push(currByte);
|
||||
correspondingArrayOfBits.push(currByteAsBits);
|
||||
currByte = 0;
|
||||
currByteAsBits = [];
|
||||
positionFromRight = 7;
|
||||
}
|
||||
}
|
||||
|
||||
if (positionFromRight != 7) {
|
||||
// grab the last one if the loop hasnt JUST ended exactly right before on an additional add.
|
||||
bytes.push(currByte);
|
||||
correspondingArrayOfBits.push(currByteAsBits);
|
||||
}
|
||||
|
||||
console.log(
|
||||
'Setting bytes array for',
|
||||
startingOffset,
|
||||
'to',
|
||||
allIndexesInSlice[allIndexesInSlice.length - 1],
|
||||
'as (with split out by bits for ease of reading)',
|
||||
bytes.map((e, i) => [e, correspondingArrayOfBits[i]]),
|
||||
);
|
||||
|
||||
await anchorProgram.rpc.updateFairLaunchLotteryBitmap(
|
||||
startingOffset,
|
||||
bytes,
|
||||
{
|
||||
accounts: {
|
||||
fairLaunch,
|
||||
fairLaunchLotteryBitmap,
|
||||
authority: walletKeyPair.publicKey,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
console.log('All done');
|
||||
});
|
||||
|
||||
program
|
||||
|
@ -729,4 +1012,94 @@ program
|
|||
//@ts-ignore
|
||||
console.log('Sequence', fairLaunchTicketObj.seq.toNumber());
|
||||
});
|
||||
/*
|
||||
program
|
||||
.command('show_lottery')
|
||||
.option(
|
||||
'-e, --env <string>',
|
||||
'Solana cluster env name',
|
||||
'devnet', //mainnet-beta, testnet, devnet
|
||||
)
|
||||
.option(
|
||||
'-k, --keypair <path>',
|
||||
`Solana wallet location`,
|
||||
'--keypair not provided',
|
||||
)
|
||||
.option('-f, --fair-launch <string>', 'fair launch id')
|
||||
.action(async (options, cmd) => {
|
||||
const { env, fairLaunch, keypair } = cmd.opts();
|
||||
|
||||
const walletKeyPair = loadWalletKey(keypair);
|
||||
const anchorProgram = await loadFairLaunchProgram(walletKeyPair, env);
|
||||
|
||||
const fairLaunchObj = await anchorProgram.account.fairLaunch.fetch(
|
||||
fairLaunch,
|
||||
);
|
||||
|
||||
const fairLaunchLottery = (
|
||||
await getFairLaunchLotteryBitmap(
|
||||
//@ts-ignore
|
||||
fairLaunchObj.tokenMint,
|
||||
)
|
||||
)[0];
|
||||
|
||||
const fairLaunchLotteryBitmapObj =
|
||||
await anchorProgram.account.fairLaunchLotteryBitmap.fetch(
|
||||
fairLaunchLottery,
|
||||
);
|
||||
|
||||
const seqKeys = [];
|
||||
//@ts-ignore
|
||||
for (let i = 0; i < fairLaunchObj.number_tickets_sold; i++) {
|
||||
seqKeys.push(
|
||||
await getFairLaunchTicketSeqLookup(
|
||||
//@ts-ignore
|
||||
fairLaunchObj.tokenMint,
|
||||
new anchor.BN(i),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const ticketKeys: { seq: anchor.BN; ticket: anchor.web3.PublicKey }[][] =
|
||||
await Promise.all(
|
||||
chunks(Array.from(Array(seqKeys.length).keys()), 1000).map(
|
||||
async allIndexesInSlice => {
|
||||
let ticketKeys = [];
|
||||
for (let i = 0; i < allIndexesInSlice.length; i += 100) {
|
||||
console.log(
|
||||
'Pulling ticket seqs for slice',
|
||||
allIndexesInSlice[i],
|
||||
allIndexesInSlice[i + 100],
|
||||
);
|
||||
const slice = allIndexesInSlice
|
||||
.slice(i, i + 100)
|
||||
.map(index => seqKeys[index]);
|
||||
const result = await getMultipleAccounts(
|
||||
anchorProgram.provider.connection,
|
||||
slice,
|
||||
'recent',
|
||||
);
|
||||
ticketKeys = ticketKeys.concat(
|
||||
result.array.map(a => ({
|
||||
ticket: new anchor.web3.PublicKey(
|
||||
new Uint8Array(a.data.slice(8, 8 + 32)),
|
||||
),
|
||||
seq: new anchor.BN(
|
||||
a.data.slice(8 + 32 + 32, 8 + 32 + 32 + 8),
|
||||
undefined,
|
||||
'le',
|
||||
),
|
||||
})),
|
||||
);
|
||||
|
||||
return ticketKeys;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const ticketsFlattened = ticketKeys
|
||||
.flat()
|
||||
.sort((a, b) => a.seq.toNumber() - b.seq.toNumber());
|
||||
});*/
|
||||
program.parse(process.argv);
|
||||
|
|
|
@ -4,6 +4,7 @@ import path from 'path';
|
|||
import fs from 'fs';
|
||||
import FormData from 'form-data';
|
||||
import fetch from 'node-fetch';
|
||||
import { AccountInfo } from '@solana/web3.js';
|
||||
|
||||
export const getUnixTs = () => {
|
||||
return new Date().getTime() / 1000;
|
||||
|
@ -91,3 +92,59 @@ export async function upload(data: FormData, manifest, index) {
|
|||
)
|
||||
).json();
|
||||
}
|
||||
|
||||
export const getMultipleAccounts = async (
|
||||
connection: any,
|
||||
keys: string[],
|
||||
commitment: string,
|
||||
) => {
|
||||
const result = await Promise.all(
|
||||
chunks(keys, 99).map(chunk =>
|
||||
getMultipleAccountsCore(connection, chunk, commitment),
|
||||
),
|
||||
);
|
||||
|
||||
const array = result
|
||||
.map(
|
||||
a =>
|
||||
//@ts-ignore
|
||||
a.array.map(acc => {
|
||||
if (!acc) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { data, ...rest } = acc;
|
||||
const obj = {
|
||||
...rest,
|
||||
data: Buffer.from(data[0], 'base64'),
|
||||
} as AccountInfo<Buffer>;
|
||||
return obj;
|
||||
}) as AccountInfo<Buffer>[],
|
||||
)
|
||||
//@ts-ignore
|
||||
.flat();
|
||||
return { keys, array };
|
||||
};
|
||||
|
||||
const getMultipleAccountsCore = async (
|
||||
connection: any,
|
||||
keys: string[],
|
||||
commitment: string,
|
||||
) => {
|
||||
const args = connection._buildArgs([keys], commitment, 'base64');
|
||||
|
||||
const unsafeRes = await connection._rpcRequest('getMultipleAccounts', args);
|
||||
if (unsafeRes.error) {
|
||||
throw new Error(
|
||||
'failed to get info about account ' + unsafeRes.error.message,
|
||||
);
|
||||
}
|
||||
|
||||
if (unsafeRes.result.value) {
|
||||
const array = unsafeRes.result.value as AccountInfo<string[]>[];
|
||||
return { keys, array };
|
||||
}
|
||||
|
||||
// TODO: fix
|
||||
throw new Error();
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"noLib": false,
|
||||
"preserveConstEnums": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"lib": ["dom", "es6"]
|
||||
"lib": ["dom", "es2019"]
|
||||
},
|
||||
"exclude": ["node_modules", "typings/browser", "typings/browser.d.ts"],
|
||||
"atom": {
|
||||
|
|
|
@ -138,6 +138,11 @@ pub mod fair_launch {
|
|||
data: FairLaunchData,
|
||||
) -> ProgramResult {
|
||||
let fair_launch = &mut ctx.accounts.fair_launch;
|
||||
let clock = &mut ctx.accounts.clock;
|
||||
|
||||
if clock.unix_timestamp > fair_launch.data.phase_one_start {
|
||||
return Err(ErrorCode::CannotUpdateFairLaunchDataOnceInProgress.into());
|
||||
}
|
||||
|
||||
assert_data_valid(&data)?;
|
||||
fair_launch.data = data;
|
||||
|
@ -760,6 +765,7 @@ pub struct UpdateFairLaunch<'info> {
|
|||
fair_launch: ProgramAccount<'info, FairLaunch>,
|
||||
#[account(signer)]
|
||||
authority: AccountInfo<'info>,
|
||||
clock: Sysvar<'info, Clock>,
|
||||
}
|
||||
|
||||
/// Limited Update that only sets phase 3 dates once bitmap is in place and fully setup.
|
||||
|
@ -1083,4 +1089,6 @@ pub enum ErrorCode {
|
|||
CannotCashOutUntilAllRefundsAndPunchesHaveBeenProcessed,
|
||||
#[msg("Cannot cash out until phase three")]
|
||||
CannotCashOutUntilPhaseThree,
|
||||
#[msg("Cannot update fair launch variables once it is in progress")]
|
||||
CannotUpdateFairLaunchDataOnceInProgress,
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue