namillum
Version:
Bubble Protocol SDK
215 lines (182 loc) • 8.76 kB
text/typescript
import { call, delay, fork, put, race, select, take, takeLatest } from 'redux-saga/effects';
import { logger } from '../util/logger';
import { getBlockchain } from './selectors';
import { CONFIG_LOADED } from '../store/blockchainSlice';
import { POLL_STAKING_DATA_START, POLL_STAKING_DATA_STOP, PRELOAD_INFO_READY, QUERY_AND_UPDATE_STAKING_STATS, UPDATE_FETCH_LANDING_STATS_STATUS, UPDATE_FETCH_SSN_STATS_STATUS, UPDATE_GZIL_ADDRESS, UPDATE_GZIL_TOTAL_SUPPLY, UPDATE_LANDING_STATS, UPDATE_MIN_BNUM_REQ, UPDATE_MIN_DELEG, UPDATE_SSN_DROPDOWN_LIST, UPDATE_SSN_LIST, UPDATE_TOTAL_STAKE_AMOUNT } from '../store/stakingSlice';
import { Constants, OperationStatus, SsnStatus } from '../util/enum';
import { LandingStats, NodeOptions, SsnStats } from '../util/interface';
import { toBech32Address } from '@zilliqa-js/crypto';
import { ZilSdk } from '../zilliqa-api';
import { convertZilToQa, isRespOk } from '../util/utils';
import BigNumber from 'bignumber.js';
import { getRefreshRate } from '../util/config-json-helper';
const MAX_GZIL_SUPPLY = Constants.MAX_GZIL_SUPPLY.toString();
const REWARD_ZIL_PER_DAY = Constants.REWARD_ZIL_PER_DAY.toString();
const REFRESH_INTERVAL = getRefreshRate();
/**
* fetch these data only once
*/
function* watchInitOnce() {
try {
yield put(UPDATE_FETCH_LANDING_STATS_STATUS(OperationStatus.PENDING));
console.log("fetching preload...");
const { impl } = yield select(getBlockchain);
const checksumImpl = impl.replace("0x", '')
let queries = [
[checksumImpl, 'mindelegstake', []],
[checksumImpl, 'totalstakeamount', []],
[checksumImpl, 'bnum_req', []],
[checksumImpl, 'gziladdr', []],
];
const response: Object = yield call(ZilSdk.getSmartContractSubStateBatch, queries);
const mindelegstake = (response as any)[0]["result"]['mindelegstake'];
const totalstakeamount = (response as any)[1]["result"]['totalstakeamount'];
const bnum_req = (response as any)[2]["result"]['bnum_req'];
const gziladdr = (response as any)[3]["result"]['gziladdr'];
logger("mindelegstake: %o", mindelegstake);
logger("totalstakeamount: %o", totalstakeamount);
logger("bnum req: ", bnum_req);
const { total_supply } = yield call(ZilSdk.getSmartContractSubState, gziladdr, 'total_supply');
logger("gziladdr: ", gziladdr);
logger("gzil minted: ", total_supply);
// populate landing stats data
const { last_withdraw_cycle_deleg } = yield call(ZilSdk.getSmartContractSubState, impl, 'last_withdraw_cycle_deleg');
if (isRespOk(last_withdraw_cycle_deleg)) {
const { comm_for_ssn } = yield call(ZilSdk.getSmartContractSubState, impl, 'comm_for_ssn');
const nodesNum = isRespOk(comm_for_ssn) ? Object.keys(comm_for_ssn).length.toString() : '0';
const delegNum = Object.keys(last_withdraw_cycle_deleg).length.toString();
let circulatingSupplyStake = '0';
const totalCoinSupply: string = yield call(ZilSdk.getTotalCoinSupply);
if (isRespOk(totalCoinSupply)) {
const totalCoinSupplyBN = new BigNumber(convertZilToQa(totalCoinSupply));
circulatingSupplyStake = (new BigNumber(totalstakeamount).dividedBy(totalCoinSupplyBN)).times(100).toFixed(5);
}
// compute remaining gzil percentage
const maxGzilSupply = new BigNumber(MAX_GZIL_SUPPLY).shiftedBy(15);
const remainGzil = maxGzilSupply.minus(new BigNumber(total_supply));
const remainingGzil = (remainGzil.dividedBy(maxGzilSupply)).times(100).toFixed(2);
// compute est. APY
const temp = new BigNumber(totalstakeamount);
const rewardZilPerDay = new BigNumber(convertZilToQa(REWARD_ZIL_PER_DAY));
const estAPY = rewardZilPerDay.dividedBy(temp).times(36500).toFixed(2);
const landingStats: LandingStats = {
circulatingSupplyStake: circulatingSupplyStake,
nodesNum: nodesNum,
delegNum: delegNum,
gzil: total_supply,
remainingGzil: remainingGzil,
totalDeposits: totalstakeamount,
estRealtimeAPY: estAPY,
}
logger("landing stats: ", landingStats);
yield put(UPDATE_LANDING_STATS({ landing_stats: landingStats }));
}
yield put(UPDATE_MIN_BNUM_REQ({ min_bnum_req: bnum_req }));
yield put(UPDATE_MIN_DELEG({ min_deleg_stake: mindelegstake }));
yield put(UPDATE_TOTAL_STAKE_AMOUNT({ total_stake_amount: totalstakeamount }));
yield put(UPDATE_GZIL_ADDRESS({ gzil_address: gziladdr }));
yield put(UPDATE_GZIL_TOTAL_SUPPLY({ gzil_total_supply: total_supply }));
} catch (e) {
console.warn("fetch home data failed");
console.warn(e);
} finally {
yield put(UPDATE_FETCH_LANDING_STATS_STATUS(OperationStatus.COMPLETE));
yield put(PRELOAD_INFO_READY()); // inform other saga that preloaded info is in store
}
}
function* pollStakingData() {
try {
yield put(UPDATE_FETCH_SSN_STATS_STATUS(OperationStatus.PENDING));
logger("fetching ssn data...");
const { impl } = yield select(getBlockchain);
const checksumImpl = impl.replace("0x", '')
let queries = [
[checksumImpl, 'ssnlist', []],
[checksumImpl, 'ssn_deleg_amt', []],
];
const response: Object = yield call(ZilSdk.getSmartContractSubStateBatch, queries);
const ssnlist = (response as any)[0]["result"]["ssnlist"];
const ssn_deleg_amt = (response as any)[1]["result"]["ssn_deleg_amt"];
logger("ssnlist: ", ssnlist);
logger("ssn_deleg_amt: ", ssn_deleg_amt);
let dropdown_list: NodeOptions[] = [];
let ssn_list: SsnStats[] = [];
for (const ssnAddress in ssnlist) {
const ssnArgs = ssnlist[ssnAddress]['arguments'];
const status = (ssnArgs[0]['constructor'] === 'True') ? SsnStatus.ACTIVE : SsnStatus.INACTIVE;
let delegNum = '0';
if (ssn_deleg_amt.hasOwnProperty(ssnAddress)) {
delegNum = Object.keys(ssn_deleg_amt[ssnAddress]).length.toString();
}
// for ssn table
const ssnStats: SsnStats = {
address: toBech32Address(ssnAddress),
name: ssnArgs[3],
apiUrl: ssnArgs[5],
stakeAmt: ssnArgs[1],
bufferedDeposits: ssnArgs[6],
commRate: ssnArgs[7],
commReward: ssnArgs[8],
delegNum: delegNum,
status: status,
}
// for use as dropdown options in the modals
const dropdownOptions: NodeOptions = {
address: toBech32Address(ssnAddress),
name: ssnArgs[3],
stakeAmt: ssnArgs[1],
delegNum: delegNum,
commRate: ssnArgs[7],
}
ssn_list.push(ssnStats);
dropdown_list.push(dropdownOptions);
}
yield put(UPDATE_SSN_DROPDOWN_LIST({ dropdown_list: dropdown_list }));
yield put(UPDATE_SSN_LIST({ ssn_list: ssn_list }));
} catch (e) {
console.warn("fetch failed");
yield put(UPDATE_SSN_DROPDOWN_LIST({ dropdown_list: [] }));
yield put(UPDATE_SSN_LIST({ ssn_list: [] }));
} finally {
yield put(UPDATE_FETCH_SSN_STATS_STATUS(OperationStatus.COMPLETE));
}
}
/**
* reload user data when zilpay change network
* stop the current poll and resume later
*/
function* queryAndUpdateStats() {
yield put(POLL_STAKING_DATA_STOP());
yield call(pollStakingData);
// delay before start to poll again
yield delay(REFRESH_INTERVAL);
yield put(POLL_STAKING_DATA_START());
}
function* pollStakingSaga() {
while (true) {
try {
yield call(pollStakingData);
} catch (e) {
console.warn("poll staking data failed");
console.warn(e);
} finally {
yield delay(REFRESH_INTERVAL);
}
}
}
function* watchPollStakingData() {
while (true) {
yield take(POLL_STAKING_DATA_START);
yield race([
call(pollStakingSaga),
take(POLL_STAKING_DATA_STOP),
])
}
}
function* stakingSaga() {
yield take(CONFIG_LOADED) // wait for app to load details from config
yield fork(watchInitOnce)
yield fork(watchPollStakingData)
yield takeLatest(QUERY_AND_UPDATE_STAKING_STATS, queryAndUpdateStats)
}
export default stakingSaga;