solmash-whitelist-sdk
Version:
A sdk for interacting Solmash Whitelist program
532 lines (453 loc) • 15.1 kB
text/typescript
import assert from "assert";
import BigNumber from "bignumber.js";
import { BN, utils } from "@project-serum/anchor";
import {
createAssociatedTokenAccountInstruction,
getAssociatedTokenAddressSync,
} from "@solana/spl-token";
import {
Connection,
LAMPORTS_PER_SOL,
PublicKey,
Signer,
Transaction,
VersionedTransaction,
} from "@solana/web3.js";
import { SOLMASH_SEEDS } from "./constants";
import { ISolmashWhitelistInstructions } from "./definitions";
import { AuctionInfo } from "./definitions/auctionInfo";
import { BuyerInfo } from "./definitions/buyerInfo";
import { SolmashWhitelistPayload } from "./payload";
import { getDecimals } from "./utils";
export class SolmashWhitelistService {
private readonly _programId: PublicKey;
static deriveAuctionAddress(auctionName: string, programId: PublicKey): PublicKey {
const [auctionAddress] = PublicKey.findProgramAddressSync(
[
Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.auctionSeed)),
Buffer.from(utils.bytes.utf8.encode(auctionName)),
],
programId,
);
return auctionAddress;
}
static deriveAuctionVaultAddress(auctionAddress: PublicKey, programId: PublicKey): PublicKey {
const [auctionVaultAddress] = PublicKey.findProgramAddressSync(
[
Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.auctionVaultSeed)),
auctionAddress.toBuffer(),
],
programId,
);
return auctionVaultAddress;
}
static deriveBuyerPdaAddress(
auctionAddress: PublicKey,
buyer: PublicKey,
programId: PublicKey,
): PublicKey {
const [auctionVaultAddress] = PublicKey.findProgramAddressSync(
[
Buffer.from(utils.bytes.utf8.encode(SOLMASH_SEEDS.buyerSeed)),
buyer.toBuffer(),
auctionAddress.toBuffer(),
],
programId,
);
return auctionVaultAddress;
}
constructor(
readonly instructions: ISolmashWhitelistInstructions,
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();
this.instructions.program.idl.errors.forEach((error) => errorMap.set(error.code, error.msg));
return new SolmashWhitelistPayload(
this._connection,
this._signTransaction,
errorMap,
tx,
signers,
);
}
async initAuction(params: {
ownerAddress: string;
auctionTokenAddress: string;
bidTokenAddress: string;
enabled: boolean;
name: string;
preSaleEndTime: number;
preSaleStartTime: number;
ticketPriceInSobb: string;
ticketPriceInSol: string;
ticketsInPool: string;
tokenQuantityPerTicket: string;
}) {
const owner = new PublicKey(params.ownerAddress);
const auctionToken = new PublicKey(params.auctionTokenAddress);
const bidToken = new PublicKey(params.bidTokenAddress);
const auction = SolmashWhitelistService.deriveAuctionAddress(params.name, this._programId);
console.debug("auction address:", auction.toString());
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const ticketPriceInSol = BigNumber(params.ticketPriceInSol).times(LAMPORTS_PER_SOL).toFixed(0);
const bidTokenDecimals = await getDecimals(this._connection, bidToken);
const ticketPriceInSobb = BigNumber(params.ticketPriceInSobb)
.times(BigNumber(10).pow(bidTokenDecimals))
.toFixed(0);
const ix = await this.instructions.getInitAuctionInstruction(auction, auctionVault, owner, {
enabled: params.enabled,
name: params.name,
preSaleEndTime: new BN(params.preSaleEndTime),
preSaleStartTime: new BN(params.preSaleStartTime),
ticketPriceInSobb: new BN(ticketPriceInSobb),
ticketPriceInSol: new BN(ticketPriceInSol),
ticketsInPool: new BN(params.ticketsInPool),
tokenQuantityPerTicket: new BN(params.tokenQuantityPerTicket),
auctionToken,
bidToken,
});
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: owner,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async addToken(params: {
auctionAddress: string;
auctionTokenAddress: string;
ownerAddress: string;
}) {
const owner = new PublicKey(params.ownerAddress);
const auction = new PublicKey(params.auctionAddress);
const auctionToken = new PublicKey(params.auctionTokenAddress);
const auctionVault = SolmashWhitelistService.deriveAuctionVaultAddress(
auction,
this._programId,
);
const auctionVaultTokenAccount = getAssociatedTokenAddressSync(
auctionToken,
auctionVault,
true,
);
const ownerAuctionTokenAccount = getAssociatedTokenAddressSync(auctionToken, owner);
const ix = await this.instructions.getAddTokenInstruction(
auction,
auctionToken,
auctionVault,
auctionVaultTokenAccount,
owner,
ownerAuctionTokenAccount,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: owner,
blockhash,
lastValidBlockHeight,
}).add(ix);
return this._createPayload(tx, []);
}
async withdrawFunds(params: {
auctionAddress: string;
auctionTokenAddress: string;
bidTokenAddress: string;
creatorAddress: string;
}) {
const { auctionAddress, auctionTokenAddress, bidTokenAddress, creatorAddress } = params;
const auction = new PublicKey(auctionAddress);
const auctionToken = new PublicKey(auctionTokenAddress);
const bidToken = new PublicKey(bidTokenAddress);
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 creatorBidTokenAccountInfo = await this._connection.getAccountInfo(
creatorBidTokenAccount,
"confirmed",
);
const auctionVaultTokenAccountInfo = await this._connection.getAccountInfo(
auctionVaultTokenAccount,
"confirmed",
);
const ix = await this.instructions.getWithdrawFundsInstruction(
auction,
auctionBidTokenAccount,
auctionToken,
auctionVault,
auctionVaultTokenAccount,
bidToken,
creator,
createAuctionTokenAccount,
creatorBidTokenAccount,
);
const { blockhash, lastValidBlockHeight } = await this._connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: creator,
blockhash,
lastValidBlockHeight,
});
if (!auctionVaultTokenAccountInfo) {
tx.add(
createAssociatedTokenAccountInstruction(
creator,
auctionVaultTokenAccount,
auctionVault,
auctionToken,
),
);
}
if (!creatorBidTokenAccountInfo) {
console.info("Creator doesn't have bid token account");
tx.add(
createAssociatedTokenAccountInstruction(creator, creatorBidTokenAccount, creator, bidToken),
);
}
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;
auctionTokenAddress: string;
buyerAddress: string;
}) {
const { auctionAddress, auctionTokenAddress, buyerAddress } = params;
const auction = new PublicKey(auctionAddress);
const auctionToken = new PublicKey(auctionTokenAddress);
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 buyerAuctionTokenAccountInfo =
await this._connection.getAccountInfo(buyerAuctionTokenAccount);
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,
});
if (!buyerAuctionTokenAccountInfo) {
console.info("Claimer doesn't have auction token account");
tx.add(
createAssociatedTokenAccountInstruction(
buyer,
buyerAuctionTokenAccount,
buyer,
auctionToken,
),
);
}
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);
return {
accumulatedSobb: decoded.accumulatedSobb.toString(),
accumulatedSol: decoded.accumulatedSol.toString(),
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(),
ticketPriceInSobb: decoded.ticketPriceInSobb.toString(),
ticketPriceInSol: decoded.ticketPriceInSol.toString(),
ticketsInPool: decoded.ticketsInPool.toString(),
tokenQuantityPerTicket: decoded.tokenQuantityPerTicket.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 {
claimed: decoded.claimed,
participated: decoded.participated,
whitelisted: decoded.whitelisted,
blacklisted: decoded.blacklisted,
};
}
async isBuyerWhitelisted(auctionAddress: string, buyerAddress: string) {
const auction = new PublicKey(auctionAddress);
const buyer = new PublicKey(buyerAddress);
const buyerPda = SolmashWhitelistService.deriveBuyerPdaAddress(auction, buyer, this._programId);
const accountInfo = await this._connection.getAccountInfo(buyerPda, "confirmed");
return accountInfo !== null;
}
async buyerParticipated(auctionAddress: string, buyerAddress: string) {
const info = await this.getBuyerInfo(auctionAddress, buyerAddress);
return info.participated;
}
async buyerClaimed(auctionAddress: string, buyerAddress: string) {
const info = await this.getBuyerInfo(auctionAddress, buyerAddress);
return info.claimed;
}
}