solmash-whitelist-sdk-v2
Version:
A sdk for interacting Solmash Whitelist program
544 lines (469 loc) • 16 kB
text/typescript
import assert from "assert";
import BigNumber from "bignumber.js";
import { BN, utils } from "@project-serum/anchor";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import {
Connection,
LAMPORTS_PER_SOL,
PublicKey,
Signer,
Transaction,
VersionedTransaction,
} from "@solana/web3.js";
import { SOLMASH_SEEDS, TEN_BIGNUM } from "./constants";
import { AuctionInfo } from "./definitions/auctionInfo";
import { BuyerInfo } from "./definitions/buyerInfo";
import { SolmashWhitelistInstructions } from "./instructions";
import { SolmashWhitelistPayload } from "./payload";
import { getDecimals } from "./utils";
export class SolmashWhitelistService {
private readonly _programId: PublicKey;
static deriveAuctionAddress(auctionName: string, programId: string | PublicKey): PublicKey {
const _programId = new PublicKey(programId);
const [auction] = PublicKey.findProgramAddressSync(
[
Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.auctionSeed)),
Buffer.from(utils.bytes.utf8.encode(auctionName)),
],
_programId,
);
return auction;
}
static deriveAuctionVaultAddress(
auction: string | PublicKey,
programId: string | PublicKey,
): PublicKey {
const _auction = new PublicKey(auction);
const _programId = new PublicKey(programId);
const [auctionVault] = PublicKey.findProgramAddressSync(
[Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.auctionVaultSeed)), _auction.toBuffer()],
_programId,
);
return auctionVault;
}
static deriveBuyerPdaAddress(
auction: string | PublicKey,
buyer: string | PublicKey,
programId: string | PublicKey,
): PublicKey {
const _auction = new PublicKey(auction);
const _buyer = new PublicKey(buyer);
const _programId = new PublicKey(programId);
const [buyerPda] = PublicKey.findProgramAddressSync(
[
Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.buyerSeed)),
_buyer.toBuffer(),
_auction.toBuffer(),
],
_programId,
);
return buyerPda;
}
constructor(
readonly instructions: SolmashWhitelistInstructions,
readonly _connection: Connection,
private readonly _signTransaction: <T extends Transaction | VersionedTransaction>(
transaction: T,
) => Promise<T>,
) {
this._programId = this.instructions.program.programId;
}
private _createPayload(tx: Transaction, signers: Signer[]) {
const errorMap: Map<number, string> = new Map();
for (let i = 0; i < this.instructions.program.idl.errors.length; i++) {
const error = this.instructions.program.idl.errors[i];
errorMap.set(error.code, error.msg);
}
return new SolmashWhitelistPayload(
this._connection,
this._signTransaction,
errorMap,
tx,
signers,
);
}
async initAuction(params: {
creatorAddress: string | PublicKey;
auctionTokenAddress: string | PublicKey;
bidTokenAddress: string | PublicKey;
claimTime: number;
enabled: boolean;
name: string;
preSaleEndTime: number;
preSaleStartTime: number;
ticketPriceInBid: string;
ticketPriceInSol: string;
ticketsInPool: string;
tokenQuantityPerTicket: string;
}) {
const creator = new PublicKey(params.creatorAddress);
const auctionToken = new PublicKey(params.auctionTokenAddress);
const bidToken = new PublicKey(params.bidTokenAddress);
const auction = SolmashWhitelistService.deriveAuctionAddress(params.name, this._programId);
console.debug("auction:", auction.toString());
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
console.debug("auction vault:", auctionVault.toString());
const auctionVaultTokenAccount = getAssociatedTokenAddressSync(
auctionToken,
auctionVault,
true,
);
const bidTokenDecimals = await getDecimals(this._connection, bidToken);
const auctionTokenDecimals = await getDecimals(this._connection, auctionToken);
const ticketPriceInSol = BigNumber(params.ticketPriceInSol).times(LAMPORTS_PER_SOL).toFixed(0);
const ticketPriceInBid = BigNumber(params.ticketPriceInBid)
.times(TEN_BIGNUM.pow(bidTokenDecimals))
.toFixed(0);
const tokenQuantityPerTicket = BigNumber(params.tokenQuantityPerTicket)
.times(TEN_BIGNUM.pow(auctionTokenDecimals))
.toFixed(0);
const ix = await this.instructions.getInitAuctionInstruction(
auction,
auctionVault,
auctionVaultTokenAccount,
creator,
{
auctionToken,
bidToken,
claimTime: new BN(params.claimTime),
enabled: params.enabled,
name: params.name,
preSaleEndTime: new BN(params.preSaleEndTime),
preSaleStartTime: new BN(params.preSaleStartTime),
ticketPriceInBid: new BN(ticketPriceInBid),
ticketPriceInSol: new BN(ticketPriceInSol),
ticketsInPool: new BN(params.ticketsInPool),
tokenQuantityPerTicket: new BN(tokenQuantityPerTicket),
},
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async addToken(params: {
auctionAddress: string | PublicKey;
creatorAddress: string | PublicKey;
}) {
const creator = new PublicKey(params.creatorAddress);
const auction = new PublicKey(params.auctionAddress);
const auctionData = await this.instructions.program.account.auction.fetch(auction, "confirmed");
const auctionToken = auctionData.auctionToken;
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const auctionVaultTokenAccount = getAssociatedTokenAddressSync(
auctionToken,
auctionVault,
true,
);
const creatorAuctionTokenAccount = getAssociatedTokenAddressSync(auctionToken, creator);
const ix = await this.instructions.getAddTokenInstruction(
auction,
auctionToken,
auctionVault,
auctionVaultTokenAccount,
creator,
creatorAuctionTokenAccount,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async withdrawFunds(params: {
auctionAddress: string | PublicKey;
creatorAddress: string | PublicKey;
withdrawSol: boolean;
}) {
const { auctionAddress, creatorAddress, withdrawSol } = params;
const auction = new PublicKey(auctionAddress);
const auctionData = await this.instructions.program.account.auction.fetch(auction, "confirmed");
const auctionToken = auctionData.auctionToken;
const bidToken = auctionData.bidToken;
const creator = new PublicKey(creatorAddress);
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const auctionBidTokenAccount = getAssociatedTokenAddressSync(bidToken, auctionVault, true);
const auctionVaultTokenAccount = getAssociatedTokenAddressSync(
auctionToken,
auctionVault,
true,
);
const createAuctionTokenAccount = getAssociatedTokenAddressSync(auctionToken, creator);
const creatorBidTokenAccount = getAssociatedTokenAddressSync(bidToken, creator);
const ix = await this.instructions.getWithdrawFundsInstruction(
auction,
auctionBidTokenAccount,
auctionToken,
auctionVault,
auctionVaultTokenAccount,
bidToken,
creator,
createAuctionTokenAccount,
creatorBidTokenAccount,
withdrawSol,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
});
tx.add(ix);
return this._createPayload(tx, []);
}
async whitelist(params: {
auctionAddress: string;
creatorAddress: string;
whitelistUserAddressList: string[];
}) {
const { auctionAddress, creatorAddress, whitelistUserAddressList } = params;
const auction = new PublicKey(auctionAddress);
const creator = new PublicKey(creatorAddress);
assert(whitelistUserAddressList.length, "Empty whitelist user list");
const ixs = await Promise.all(
whitelistUserAddressList.map(async (user) => {
const whitelistUser = new PublicKey(user);
const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(
auction,
whitelistUser,
this._programId,
);
const ix = await this.instructions.getWhitelistInstruction(
auction,
buyerPda,
creator,
whitelistUser,
);
return ix;
}),
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
}).add(...ixs);
return this._createPayload(tx, []);
}
// async preSaleBuyUsingSpl(params: {
// auctionAddress: string;
// bidTokenAddress: string;
// buyerAddress: string;
// }) {
// const { auctionAddress, bidTokenAddress, buyerAddress } = params;
// const auction = new PublicKey(auctionAddress);
// const bidToken = new PublicKey(bidTokenAddress);
// const buyer = new PublicKey(buyerAddress);
// const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
// auction,
// this._programId,
// );
// const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(auction, buyer, this._programId);
// const auctionVaultBidTokenAccount = getAssociatedTokenAddressSync(bidToken, auctionVault, true);
// const buyerBidTokenAccount = getAssociatedTokenAddressSync(bidToken, buyer);
// const auctionVaultBidTokenAccountInfo = await this._connection.getAccountInfo(
// auctionVaultBidTokenAccount,
// "confirmed",
// );
// const ix = await this.instructions.getPreSaleBuyUsingSplInstruction(
// auction,
// auctionVault,
// auctionVaultBidTokenAccount,
// bidToken,
// buyer,
// buyerBidTokenAccount,
// buyerPda,
// );
// const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
// const tx = new Transaction({
// feePayer: buyer,
// blockhash,
// lastValidBlockHeight,
// });
// if (!auctionVaultBidTokenAccountInfo) {
// tx.add(
// createAssociatedTokenAccountInstruction(
// buyer,
// auctionVaultBidTokenAccount,
// auctionVault,
// bidToken,
// ),
// );
// }
// tx.add(ix);
// return this._createPayload(tx, []);
// }
async preSaleBuyUsingSol(params: { auctionAddress: string; buyerAddress: string }) {
const { auctionAddress, buyerAddress } = params;
const auction = new PublicKey(auctionAddress);
const buyer = new PublicKey(buyerAddress);
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(auction, buyer, this._programId);
const ix = await this.instructions.getPreSaleBuyUsingSolInstruction(
auction,
auctionVault,
buyer,
buyerPda,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: buyer,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async claimTokens(params: { auctionAddress: string; buyerAddress: string }) {
const { auctionAddress, buyerAddress } = params;
const auction = new PublicKey(auctionAddress);
const auctionData = await this.instructions.program.account.auction.fetch(auction, "confirmed");
const auctionToken = auctionData.auctionToken;
const buyer = new PublicKey(buyerAddress);
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(auction, buyer, this._programId);
const auctionVaultTokenAccount = getAssociatedTokenAddressSync(
auctionToken,
auctionVault,
true,
);
const buyerAuctionTokenAccount = getAssociatedTokenAddressSync(auctionToken, buyer);
const ix = await this.instructions.getClaimTokensInstruction(
auction,
auctionToken,
auctionVault,
auctionVaultTokenAccount,
buyer,
buyerAuctionTokenAccount,
buyerPda,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: buyer,
blockhash,
lastValidBlockHeight,
});
tx.add(ix);
return this._createPayload(tx, []);
}
async changeTime(params: {
auctionAddress: string;
creatorAddress: string;
newStartTime: number;
newEndTime: number;
}) {
const auction = new PublicKey(params.auctionAddress);
const creator = new PublicKey(params.creatorAddress);
const newStartTime = new BN(params.newStartTime);
const newEndTime = new BN(params.newEndTime);
const ix = await this.instructions.getChangeTimeInstruction(
auction,
creator,
newStartTime,
newEndTime,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async getAuctionInfo(auctionAddress: string): Promise<AuctionInfo> {
const auction = new PublicKey(auctionAddress);
const decoded = await this.instructions.program.account.auction.fetch(auction);
const bidTokenDecimals = await getDecimals(this._connection, decoded.bidToken);
const auctionTokenDecimals = await getDecimals(this._connection, decoded.auctionToken);
const accumulatedBidToken = BigNumber(decoded.accumulatedBid.toString())
.div(TEN_BIGNUM.pow(bidTokenDecimals))
.toFixed();
const accumulatedSol = BigNumber(decoded.accumulatedSol.toString())
.div(LAMPORTS_PER_SOL)
.toFixed();
const ticketPriceInBid = BigNumber(decoded.ticketPriceInBid.toString())
.div(TEN_BIGNUM.pow(bidTokenDecimals))
.toFixed();
const ticketPriceInSol = BigNumber(decoded.ticketPriceInSol.toString())
.div(LAMPORTS_PER_SOL)
.toFixed();
const tokenQuantityPerTicket = BigNumber(decoded.tokenQuantityPerTicket.toString())
.div(TEN_BIGNUM.pow(auctionTokenDecimals))
.toFixed();
return {
accumulatedBidToken,
accumulatedSol,
auctionToken: decoded.auctionToken.toString(),
bidToken: decoded.bidToken.toString(),
enabled: decoded.enabled,
name: decoded.name,
owner: decoded.owner.toString(),
preSaleEndTime: decoded.preSaleEndTime.toNumber(),
preSaleStartTime: decoded.preSaleStartTime.toNumber(),
ticketPriceInBid,
ticketPriceInSol,
ticketsInPool: decoded.ticketsInPool.toString(),
tokenQuantityPerTicket,
claimTime: decoded.claimTime.toNumber(),
remainingTickets: decoded.remainingTickets.toString(),
ticketsClaimed: decoded.ticketsClaimed.toString(),
};
}
async getBuyerInfo(auctionAddress: string, buyerAddress: string): Promise<BuyerInfo> {
const auction = new PublicKey(auctionAddress);
const buyer = new PublicKey(buyerAddress);
const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(auction, buyer, this._programId);
const decoded = await this.instructions.program.account.buyer.fetch(buyerPda);
return {
auctionAddress: decoded.auctionAddress.toString(),
buyerAddress: decoded.buyerAddress.toString(),
claimed: decoded.claimed,
participated: decoded.participated,
whitelisted: decoded.whitelisted,
blacklisted: decoded.blacklisted,
};
}
async isBuyerWhitelisted(auctionAddress: string, buyerAddress: string) {
try {
const buyerPdaData = await this.getBuyerInfo(auctionAddress, buyerAddress);
return buyerPdaData.whitelisted;
} catch (err) {
return false;
}
}
async buyerParticipated(auctionAddress: string, buyerAddress: string) {
try {
const info = await this.getBuyerInfo(auctionAddress, buyerAddress);
return info.participated;
} catch (err) {
return false;
}
}
async buyerClaimed(auctionAddress: string, buyerAddress: string) {
try {
const info = await this.getBuyerInfo(auctionAddress, buyerAddress);
return info.claimed;
} catch (err) {
return false;
}
}
}