UNPKG

@unloc-xyz/unloc-sdk

Version:
420 lines 18.3 kB
// @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