@bayswap/sdk
Version:
SDK for BaySwap smart contract
206 lines • 7.96 kB
JavaScript
import { Coin, getMoveObject, getObjectId, SUI_CLOCK_OBJECT_ID, TransactionBlock, } from '@mysten/sui.js';
import { REWARD_PER_SHARE_MUL } from '../constants';
import { enrichPrefixZero, parsingStakePoolTypes, standardizeStakeLPType, } from '../utils';
export class StakeModule {
constructor(provider, registry, txBuiderConfig) {
this._provider = provider;
this._registry = registry;
this._txBuilderConfig = txBuiderConfig;
}
async parseStorageToSetIDs() {
const storage = await this._provider.getObject({
id: this._registry.storageID,
options: {
showContent: true,
},
});
const obj = getMoveObject(storage);
this._poolsParentID = obj?.fields.pools?.fields?.id.id;
this._positionsParentID = obj?.fields.position_manager?.fields?.id.id;
}
async getBatchStakePoolInfo(poolID) {
const pools = await this._provider.multiGetObjects({
ids: poolID,
options: { showContent: true },
});
return pools.map((pool) => this.parsePool(pool));
}
async getAllPoolIDs() {
const res = [];
if (!this._poolsParentID) {
await this.parseStorageToSetIDs();
}
if (this._poolsParentID) {
const poolListData = await this._provider.getDynamicFields({
parentId: this._poolsParentID,
});
for (const pool of poolListData.data) {
res.push(pool.objectId);
}
}
return res;
}
// if this stake pool not found, throw error
// if user didn't stake before, return undefined
async getUserPosition(stakeCoinType, rewardCoinType, owner) {
const standardizeStakeCoinType = standardizeStakeLPType(stakeCoinType);
if (owner.length == 42 && owner.startsWith('0x0')) {
owner = '0x' + owner.slice(3);
}
if (!this._positionsParentID) {
await this.parseStorageToSetIDs();
}
let pmID = '';
if (this._positionsParentID) {
const positionManagers = await this._provider.getDynamicFields({
parentId: this._positionsParentID,
});
for (const pm of positionManagers.data) {
if (pm.objectType.includes(standardizeStakeCoinType) &&
pm.objectType.includes(rewardCoinType)) {
pmID = pm.objectId;
break;
}
}
}
if (!pmID) {
throw new Error(`stake pool corresponding ${standardizeStakeCoinType} ${rewardCoinType} not found`);
}
const resp = await this._provider.getObject({
id: pmID,
options: { showContent: true },
});
const allPositionsID = this.parseRespToGetAllPositionsID(resp);
try {
const userPos = await this._provider.getDynamicFieldObject({
parentId: allPositionsID,
name: {
type: 'address',
value: owner,
},
});
const obj = getMoveObject(userPos);
if (!(obj?.fields && obj?.type)) {
return undefined;
}
return {
owner: owner,
positionType: obj.type,
lastAccRewardPerShare: BigInt(obj.fields?.last_acc_reward_per_share),
stakedAmount: BigInt(obj.fields?.staked_amount),
};
}
catch (e) {
return undefined;
}
}
calculatePendingRewards(pool, positionLastAccRewardPerShare, stakedAmount, now // timestamp in milliseconds
) {
if (pool.stake == BigInt(0)) {
return BigInt(0);
}
const duration = now - pool.lastUpdateTime;
const rewardSinceLastUpdate = (duration * pool.rewardRemaining) / (pool.endTime - pool.lastUpdateTime);
const accRewardPerShare = pool.accRewardPerShare +
(rewardSinceLastUpdate * REWARD_PER_SHARE_MUL) / pool.stake;
return ((stakedAmount * (accRewardPerShare - positionLastAccRewardPerShare)) /
REWARD_PER_SHARE_MUL);
}
buildCreateStakePoolUnsignedTx(t, p) {
const tx = new TransactionBlock();
if (p.rewardCoinIds.length > 1) {
tx.mergeCoins(tx.object(p.rewardCoinIds[0]), p.rewardCoinIds.slice(1).map((id) => tx.object(id)));
}
const [used] = tx.splitCoins(tx.object(p.rewardCoinIds[0]), [
tx.pure(p.rewardAmount),
]);
tx.moveCall({
target: `${this._registry.packageID}::stake_entry::create_pool`,
typeArguments: [t.stakeCoinType, t.rewardCoinType],
arguments: [
tx.object(SUI_CLOCK_OBJECT_ID),
tx.object(this._registry.storageID),
tx.pure(p.duration.toString()),
used,
],
});
return tx;
}
buildStakeRawTx(t, p) {
const tx = new TransactionBlock();
if (p.stakeCoinIds.length > 1) {
tx.mergeCoins(tx.object(p.stakeCoinIds[0]), p.stakeCoinIds.slice(1).map((id) => tx.object(id)));
}
const [used] = tx.splitCoins(tx.object(p.stakeCoinIds[0]), [
tx.pure(p.stakeAmount),
]);
tx.moveCall({
target: `${this._registry.packageID}::stake_entry::stake`,
typeArguments: [t.stakeCoinType, t.rewardCoinType],
arguments: [
tx.object(SUI_CLOCK_OBJECT_ID),
tx.object(this._registry.storageID),
used,
],
});
return tx;
}
buildClaimRawTx(t) {
return this.buildMultipleClaimsRawTx([t]);
}
buildMultipleClaimsRawTx(tz) {
const tx = new TransactionBlock();
for (const t of tz) {
tx.moveCall({
target: `${this._registry.packageID}::stake_entry::claim`,
typeArguments: [t.stakeCoinType, t.rewardCoinType],
arguments: [
tx.object(SUI_CLOCK_OBJECT_ID),
tx.object(this._registry.storageID),
],
});
}
return tx;
}
buildUnstakeRawTx(t, p) {
const tx = new TransactionBlock();
tx.moveCall({
target: `${this._registry.packageID}::stake_entry::unstake`,
typeArguments: [t.stakeCoinType, t.rewardCoinType],
arguments: [
tx.object(SUI_CLOCK_OBJECT_ID),
tx.object(this._registry.storageID),
tx.pure(p.amount.toString()),
],
});
return tx;
}
parsePool(resp) {
const poolId = getObjectId(resp);
const obj = getMoveObject(resp);
if (!(obj?.fields && obj?.type)) {
throw new Error(`pool ${poolId} is not found`);
}
const fields = obj.fields;
const { poolType } = parsingStakePoolTypes(obj.type);
return {
packageID: this._registry.packageID,
poolID: poolId,
poolType: poolType,
stakeCoinType: Coin.getCoinType(fields.stake?.type) || '',
rewardCoinType: enrichPrefixZero(Coin.getCoinType(fields.reward?.type) || ''),
startTime: BigInt(fields.start_time),
endTime: BigInt(fields.end_time),
rewardRemaining: BigInt(fields.reward_remaining),
stake: BigInt(fields.stake.fields.balance),
reward: BigInt(fields.reward.fields.balance),
lastUpdateTime: BigInt(fields.last_update_time),
accRewardPerShare: BigInt(fields.acc_reward_per_share),
};
}
parseRespToGetAllPositionsID(resp) {
const obj = getMoveObject(resp);
return obj?.fields?.all_positions?.fields?.id?.id;
}
}
//# sourceMappingURL=stake.js.map