@unloc-xyz/unloc-sdk
Version:
420 lines • 18.3 kB
JavaScript
// @ts-ignore
import * as anchor from '@project-serum/anchor';
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
const { BN, web3, Program, AnchorProvider } = anchor;
const { PublicKey, SystemProgram, Transaction } = web3;
const utf8 = anchor.utils.bytes.utf8;
const defaultAccounts = {
tokenProgram: TOKEN_PROGRAM_ID,
clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
systemProgram: SystemProgram.programId,
};
import { IDL as idl } from '../types/unloc_staking';
import { createAssociatedTokenAccountIfNotExist2, sendTransaction } from '../utils';
import { STAKING_PID } from '../global-config';
let StakingProgram = null;
export function initStakingProgram(connection, wallet, programId = STAKING_PID) {
const provider = new anchor.AnchorProvider(connection, wallet, {
commitment: 'confirmed',
});
// Generate the program client from IDL.
const program = new anchor.Program(idl, programId, provider);
StakingProgram = program;
}
export async function getStakingStateAddress() {
const [stateSigner, stateBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('state')], StakingProgram.programId);
return stateSigner;
}
async function getStakingExtraRewardAddress() {
const [stateSigner, stateBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('extra')], StakingProgram.programId);
return stateSigner;
}
async function getStakingPoolAddressFromMint(mint) {
const [stateSigner, stateBump] = await anchor.web3.PublicKey.findProgramAddress([new PublicKey(mint).toBuffer()], StakingProgram.programId);
return stateSigner;
}
const ACC_PRECISION = new BN(100 * 1000 * 1000 * 1000);
const FULL_100 = new BN(100 * 1000 * 1000 * 1000);
export function estimateRewards(stateData, extraConfigData, poolData, userData) {
const currentTimeStamp = Math.ceil(new Date().getTime() / 1000);
const duration = new BN(Math.max(currentTimeStamp - poolData.lastRewardTime, 0));
const reward_per_share = stateData.tokenPerSecond.mul(duration).mul(ACC_PRECISION).div(poolData.amount);
const acc_reward_per_share = poolData.accRewardPerShare.add(reward_per_share);
let extraPercentage = new BN(0);
extraConfigData.configs.forEach((item) => {
if (item.duration.toString() === userData.lockDuration.toString()) {
extraPercentage = item.extraPercentage;
return;
}
});
const pending_amount = userData.amount.mul(acc_reward_per_share).div(ACC_PRECISION).sub(userData.rewardDebt);
const extra_amount = userData.extraReward.add(pending_amount.mul(extraPercentage).div(FULL_100));
const total_reward = userData.rewardAmount.add(pending_amount).add(extra_amount);
return total_reward.toString();
}
export function estimateStakingRewardsPerSec(stateData, extraConfigData, poolData, userData) {
const currentTimeStamp = Math.ceil(new Date().getTime() / 1000);
const duration = new BN(Math.max(currentTimeStamp - poolData.lastRewardTime, 0));
const reward_per_share = stateData.tokenPerSecond.mul(duration).mul(ACC_PRECISION).div(poolData.amount);
const acc_reward_per_share = poolData.accRewardPerShare.add(reward_per_share);
let extraPercentage = new BN(0);
extraConfigData.configs.forEach((item) => {
if (item.duration.toString() === userData.lockDuration.toString()) {
extraPercentage = item.extraPercentage;
return;
}
});
const pending_amount = userData.amount.mul(acc_reward_per_share).div(ACC_PRECISION).sub(userData.rewardDebt);
const extra_amount = userData.extraReward.add(pending_amount.mul(extraPercentage).div(FULL_100));
const total_reward = userData.rewardAmount.add(pending_amount).add(extra_amount);
return ((total_reward / 1000000000) / ((currentTimeStamp - poolData.lastRewardTime.toString()) * 1000));
}
export async function createStakingState(connection, wallet, tokenPerSecond, earlyUnlocFee, profileLevels, rewardMint) {
const transaction = new Transaction();
const [stateSigner, stateBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('state')], StakingProgram.programId);
const stateRewardVault = await createAssociatedTokenAccountIfNotExist2(null, stateSigner, wallet.publicKey, rewardMint, transaction);
const tx = await StakingProgram.rpc.createState(stateBump, new BN(tokenPerSecond), new BN(earlyUnlocFee), profileLevels, {
accounts: {
state: stateSigner,
rewardMint: rewardMint,
rewardVault: stateRewardVault,
feeVault: stateRewardVault,
authority: wallet.publicKey,
...defaultAccounts
},
preInstructions: transaction.instructions
});
return tx;
}
export async function getStakingState() {
const stateSigner = await getStakingStateAddress();
return await StakingProgram.account.stateAccount.fetch(stateSigner);
}
export async function getStakingExtraRewardConfigs() {
const [extraRewardSigner, extraRewardBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('extra')], StakingProgram.programId);
const extraRewardConfigs = await StakingProgram.account.extraRewardsAccount.fetchNullable(extraRewardSigner);
// assert.ok(extraRewardConfigs.configs.length === 3)
// assert.ok(new BN(1).eq(extraRewardConfigs.configs[1].duration))
// assert.ok(getNumber(50).eq(extraRewardConfigs.configs[1].extraPercentage))
return extraRewardConfigs;
}
export async function createStakingExtraReward(connection, wallet) {
const transaction = new Transaction();
const signers = [];
const [extraRewardSigner, extraRewardBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('extra')], StakingProgram.programId);
console.log(extraRewardSigner.toString(), extraRewardBump);
const tx = await StakingProgram.instruction.createExtraRewardConfigs(extraRewardBump, [
{ duration: new BN(43200 * 60), extraPercentage: new BN(0) },
{ duration: new BN(129600 * 60), extraPercentage: new BN(10 * 1000 * 1000 * 1000) },
{ duration: new BN(259200 * 60), extraPercentage: new BN(30 * 1000 * 1000 * 1000) },
{ duration: new BN(525600 * 60), extraPercentage: new BN(100 * 1000 * 1000 * 1000) }
], {
accounts: {
extraRewardAccount: extraRewardSigner,
authority: wallet.publicKey,
...defaultAccounts
},
});
transaction.add(tx);
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function setStakingExtraReward(connection, wallet, extraRewards) {
const transaction = new Transaction();
const signers = [];
const [extraRewardSigner, extraRewardBump] = await anchor.web3.PublicKey.findProgramAddress([utf8.encode('extra')], StakingProgram.programId);
transaction.add(StakingProgram.instruction.setExtraRewardConfigs(extraRewards, {
accounts: {
extraRewardAccount: extraRewardSigner,
authority: wallet.publicKey,
...defaultAccounts
},
}));
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function fundToStakingProgram(connection, wallet, poolSigner, masterRewardVault, amount) {
const transaction = new Transaction();
const signers = [];
const stateSigner = await getStakingStateAddress();
const state = await getStakingState();
// await rewardMint.mintTo(stateRewardVault, wallet, [provider.wallet], getNumber(10000).toString())
const tx = StakingProgram.instruction.fundRewardToken(new BN(amount), {
accounts: {
state: stateSigner,
rewardVault: state.rewardVault,
userVault: masterRewardVault,
authority: wallet.publicKey,
...defaultAccounts
}
});
transaction.add(tx);
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function getAllStakingPools() {
const pools = await StakingProgram.account.farmPoolAccount.all();
return pools;
}
export async function getFirstStakingPool() {
const pools = await StakingProgram.account.farmPoolAccount.all();
return pools[0];
}
export async function createStakingPool(connection, wallet, rewardMint, pools) {
const transaction = new Transaction();
const signers = [];
const [poolSigner, poolBump] = await anchor.web3.PublicKey.findProgramAddress([new PublicKey(rewardMint).toBuffer()], StakingProgram.programId);
const stateSigner = await getStakingStateAddress();
const poolVault = await createAssociatedTokenAccountIfNotExist2(null, poolSigner, wallet.publicKey, rewardMint, transaction);
// let pools = await StakingProgram.account.farmPoolAccount.all()
transaction.add(StakingProgram.instruction.createPool(poolBump, new BN('0'), new BN('0'), {
accounts: {
pool: poolSigner,
state: stateSigner,
mint: rewardMint,
vault: poolVault,
authority: wallet.publicKey,
...defaultAccounts
},
remainingAccounts: pools.map(p => ({
pubkey: p.publicKey,
isWritable: true,
isSigner: false
}))
}));
// let stateInfo = await program.account.stateAccount.fetch(stateSigner)
// let poolInfo = await program.account.farmPoolAccount.fetch(poolSigner)
// assert.ok(poolInfo.point.eq(stateInfo.totalPoint))
// assert.ok(poolInfo.point.eq(new BN('0')))
// assert.ok(poolInfo.amountMultipler.eq(new BN(0)))
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function closeStakingPool(connection, wallet, poolSigner, pools) {
const transaction = new Transaction();
const signers = [];
const stateSigner = await getStakingStateAddress();
StakingProgram.instruction.closePool({
accounts: {
pool: new PublicKey(poolSigner),
state: stateSigner,
authority: wallet,
...defaultAccounts
},
remainingAccounts: pools.map(p => ({
pubkey: p.publicKey,
isWritable: true,
isSigner: false
}))
});
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function changeStakingPoolAmountMultipler(connection, wallet, poolSigner) {
const transaction = new Transaction();
const signers = [];
const stateSigner = await getStakingStateAddress();
const tx = StakingProgram.instruction.changePoolAmountMultipler(new BN(1), {
accounts: {
pool: new PublicKey(poolSigner),
state: stateSigner,
authority: wallet.publicKey,
...defaultAccounts
}
});
transaction.add(tx);
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function changeStakingTokenPerSecond(connection, wallet, pools, tokenPerSecond) {
const transaction = new Transaction();
const signers = [];
const stateSigner = await getStakingStateAddress();
const tx = StakingProgram.instruction.changeTokensPerSecond(new BN(tokenPerSecond), {
accounts: {
state: stateSigner,
authority: wallet.publicKey,
...defaultAccounts
},
remainingAccounts: pools.map(p => ({
pubkey: p.publicKey,
isWritable: true,
isSigner: false
}))
});
transaction.add(tx);
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function changeStakingPoolPoint(connection, wallet, poolSigner) {
const transaction = new Transaction();
const signers = [];
const stateSigner = await getStakingStateAddress();
let pools = await getAllStakingPools();
const ix = StakingProgram.instruction.changePoolPoint(new BN(1000), {
accounts: {
pool: new PublicKey(poolSigner),
state: stateSigner,
authority: wallet.publicKey,
...defaultAccounts
},
remainingAccounts: pools.map((p) => ({
pubkey: p.publicKey,
isWritable: true,
isSigner: false
}))
});
transaction.add(ix);
return await sendTransaction(connection, wallet, transaction, signers);
}
export async function getStakingPoolUserAccount(wallet, poolSigner) {
const [poolUserAccount, bump1] = await PublicKey.findProgramAddress([
poolSigner.toBuffer(), wallet.publicKey.toBuffer()
], StakingProgram.programId);
return await StakingProgram.account.farmPoolUserAccount.fetchNullable(poolUserAccount);
}
const ONE_YEAR_SECOND = 365 * 24 * 3600;
export const TIERS_XCRP = [0, 200, 2000];
export function calculateTiers(amount, lockDuration) {
const rate = Number((lockDuration / ONE_YEAR_SECOND).toFixed(3));
const xCRP = rate * amount;
let i = 0;
for (; i < TIERS_XCRP.length; i++) {
if (TIERS_XCRP[i] > xCRP) {
break;
}
}
i--;
return {
xCRP: Number(xCRP.toFixed(2)),
tiers: i,
};
}
export async function stake(connection, wallet, poolSigner, rewardMint, poolVault, rewardUserVault, amount, lock = 0, signers = []) {
const transaction = new Transaction();
const stateSigner = await getStakingStateAddress();
const state = await getStakingState();
const extraRewardSigner = await getStakingExtraRewardAddress();
// const poolSigner = await getPoolAddressFromMint(rewardMint)
const [userAccount, bump1] = await PublicKey.findProgramAddress([
new PublicKey(poolSigner).toBuffer(), wallet.publicKey.toBuffer()
], StakingProgram.programId);
const userAccountData = await StakingProgram.account.farmPoolUserAccount.fetchNullable(userAccount);
if (!userAccountData) {
console.log("You are the new user to stake");
transaction.add(StakingProgram.instruction.createUser(bump1, {
accounts: {
user: userAccount,
state: stateSigner,
pool: poolSigner,
authority: wallet.publicKey,
...defaultAccounts
}
}));
}
try {
const txHash = await StakingProgram.rpc.stake(new BN(amount), new BN(lock), {
accounts: {
mint: rewardMint,
extraRewardAccount: extraRewardSigner,
poolVault: poolVault,
userVault: rewardUserVault,
feeVault: state.feeVault,
user: userAccount,
state: stateSigner,
pool: poolSigner,
authority: wallet.publicKey,
...defaultAccounts
},
preInstructions: transaction.instructions,
signers
});
return txHash;
}
catch (e) {
console.log(e);
}
return '';
}
export async function createStakingUser(connection, wallet, poolSigner, signers = []) {
const stateSigner = await getStakingStateAddress();
const [userAccount, bump1] = await PublicKey.findProgramAddress([
new PublicKey(poolSigner).toBuffer(), wallet.publicKey.toBuffer()
], StakingProgram.programId);
const userAccountData = await StakingProgram.account.farmPoolUserAccount.fetchNullable(userAccount);
if (!userAccountData) {
console.log("You are the new user to stake");
try {
const tx = await StakingProgram.rpc.createUser(bump1, {
accounts: {
user: userAccount,
state: stateSigner,
pool: poolSigner,
authority: wallet.publicKey,
...defaultAccounts
},
signers
});
return tx;
}
catch (e) {
console.log(e);
return null;
}
}
return null;
}
export async function unstake(connection, wallet, poolSigner, rewardMint, poolVault, rewardUserVault, amount, signers = []) {
const stateSigner = await getStakingStateAddress();
const programState = await getStakingState();
const extraRewardSigner = await getStakingExtraRewardAddress();
// const poolSigner = await getPoolAddressFromMint(rewardMint)
const [poolUserAccount, bump1] = await PublicKey.findProgramAddress([
new PublicKey(poolSigner).toBuffer(), wallet.publicKey.toBuffer()
], StakingProgram.programId);
try {
const txHash = await StakingProgram.rpc.unstake(new BN(amount), {
accounts: {
mint: rewardMint,
extraRewardAccount: extraRewardSigner,
poolVault: poolVault,
userVault: rewardUserVault,
feeVault: programState.feeVault,
user: poolUserAccount,
state: stateSigner,
pool: poolSigner,
authority: wallet.publicKey,
...defaultAccounts
},
signers
});
return txHash;
}
catch (e) {
console.log(e);
return null;
}
}
export async function harvest(connection, wallet, poolSigner, rewardMint, rewardUserVault, stateRewardVault, signers = []) {
const stateSigner = await getStakingStateAddress();
const extraRewardSigner = await getStakingExtraRewardAddress();
// const poolSigner = await getPoolAddressFromMint(rewardMint)
const [poolUserAccount, bump1] = await PublicKey.findProgramAddress([
new PublicKey(poolSigner).toBuffer(), wallet.publicKey.toBuffer()
], StakingProgram.programId);
try {
const txHash = await StakingProgram.rpc.harvest({
accounts: {
mint: rewardMint,
extraRewardAccount: extraRewardSigner,
rewardVault: stateRewardVault,
userVault: rewardUserVault,
user: poolUserAccount,
state: stateSigner,
pool: poolSigner,
authority: wallet.publicKey,
...defaultAccounts
},
signers
});
return txHash;
}
catch (e) {
console.log(e);
return null;
}
}
//# sourceMappingURL=index.js.map