Skip to content

Commit

Permalink
Bugfix/pool staking (pancakeswap#1431)
Browse files Browse the repository at this point in the history
* feat: Add options to multicall

* feat: Archived node web3

* refactor: Add option to multicall options

* fix: Use archive provider for voting helpers

* refactor: Return BigNumber for voting helpers
  • Loading branch information
hachiojidev authored Jun 8, 2021
1 parent 1d22ef4 commit 4f99ef0
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 36 deletions.
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ REACT_APP_NODE_3 = "https://bsc-dataseed.binance.org"

REACT_APP_GRAPH_API_PROFILE = "https://api.thegraph.com/subgraphs/name/pancakeswap/profile"
REACT_APP_GRAPH_API_PREDICTION = "https://api.thegraph.com/subgraphs/name/pancakeswap/prediction"
REACT_APP_ARCHIVED_NODE = "https://little-long-pine.bsc.quiknode.pro/"
1 change: 1 addition & 0 deletions .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ REACT_APP_NODE_3 = "https://bsc-dataseed.binance.org"

REACT_APP_GRAPH_API_PROFILE = "https://api.thegraph.com/subgraphs/name/pancakeswap/profile"
REACT_APP_GRAPH_API_PREDICTION = "https://api.thegraph.com/subgraphs/name/pancakeswap/prediction"
REACT_APP_ARCHIVED_NODE = "https://little-long-pine.bsc.quiknode.pro/"
1 change: 1 addition & 0 deletions src/config/constants/endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const GRAPH_API_PROFILE = process.env.REACT_APP_GRAPH_API_PROFILE
export const GRAPH_API_PREDICTION = process.env.REACT_APP_GRAPH_API_PREDICTION
export const ARCHIVED_NODE = process.env.REACT_APP_ARCHIVED_NODE
58 changes: 34 additions & 24 deletions src/utils/callHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { getAddress, getCakeAddress } from 'utils/addressHelpers'
import tokens from 'config/constants/tokens'
import pools from 'config/constants/pools'
import sousChefABI from 'config/abi/sousChef.json'
import multicall from './multicall'
import { getWeb3NoAccount } from './web3'
import { multicallv2 } from './multicall'
import { getWeb3WithArchivedNodeProvider } from './web3'
import { getBalanceAmount } from './formatBalance'
import { BIG_TEN, BIG_ZERO } from './bigNumber'

Expand Down Expand Up @@ -144,8 +144,9 @@ const CAKE_BNB_TOKEN = new Token(chainId, getAddress(cakeBnbFarm.lpAddresses), 1
*/
export const getUserStakeInCakeBnbLp = async (account: string, block?: number) => {
try {
const masterContract = getMasterchefContract()
const cakeBnbContract = getLpContract(getAddress(cakeBnbFarm.lpAddresses))
const archivedWeb3 = getWeb3WithArchivedNodeProvider()
const masterContract = getMasterchefContract(archivedWeb3)
const cakeBnbContract = getLpContract(getAddress(cakeBnbFarm.lpAddresses), archivedWeb3)
const totalSupplyLP = await cakeBnbContract.methods.totalSupply().call(undefined, block)
const reservesLP = await cakeBnbContract.methods.getReserves().call(undefined, block)
const cakeBnbBalance = await masterContract.methods.userInfo(cakeBnbPid, account).call(undefined, block)
Expand All @@ -161,10 +162,10 @@ export const getUserStakeInCakeBnbLp = async (account: string, block?: number) =
false,
)

return cakeLPBalance.toSignificant(18)
return new BigNumber(cakeLPBalance.toSignificant(18))
} catch (error) {
console.error(`CAKE-BNB LP error: ${error}`)
return 0
return BIG_ZERO
}
}

Expand All @@ -173,38 +174,49 @@ export const getUserStakeInCakeBnbLp = async (account: string, block?: number) =
*/
export const getUserStakeInCakePool = async (account: string, block?: number) => {
try {
const masterContract = getMasterchefContract()
const archivedWeb3 = getWeb3WithArchivedNodeProvider()
const masterContract = getMasterchefContract(archivedWeb3)
const response = await masterContract.methods.userInfo(0, account).call(undefined, block)

return getBalanceAmount(new BigNumber(response.amount)).toNumber()
return getBalanceAmount(new BigNumber(response.amount))
} catch (error) {
console.error('Error getting stake in CAKE pool', error)
return 0
return BIG_ZERO
}
}

/**
* Returns total staked value of active pools
*/
export const getUserStakeInPools = async (account: string) => {
export const getUserStakeInPools = async (account: string, block?: number) => {
try {
const web3 = getWeb3NoAccount()
const multicallOptions = {
web3: getWeb3WithArchivedNodeProvider(),
blockNumber: block,
requireSuccess: false,
}
const eligiblePools = pools
.filter((pool) => pool.sousId !== 0)
.filter((pool) => pool.isFinished === false || pool.isFinished === undefined)

// Get the ending block is eligible pools
const calls = eligiblePools.map((eligiblePool) => ({
const endBlockCalls = eligiblePools.map((eligiblePool) => ({
address: getAddress(eligiblePool.contractAddress),
name: 'bonusEndBlock',
}))
const currentBlock = await web3.eth.getBlockNumber()
const ends = await multicall(sousChefABI, calls)
const startBlockCalls = eligiblePools.map((eligiblePool) => ({
address: getAddress(eligiblePool.contractAddress),
name: 'startBlock',
}))
const endBlocks = await multicallv2(sousChefABI, endBlockCalls, multicallOptions)
const startBlocks = await multicallv2(sousChefABI, startBlockCalls, multicallOptions)

// Filter out pools that have ended
const activePools = eligiblePools.filter((eligiblePool, index) => {
const endBlock = new BigNumber(ends[index])
return endBlock.gt(currentBlock)
const endBlock = new BigNumber(endBlocks[index])
const startBlock = new BigNumber(startBlocks[index])

return startBlock.lte(block) && endBlock.gte(block)
})

// Get the user info of each pool
Expand All @@ -213,15 +225,13 @@ export const getUserStakeInPools = async (account: string) => {
name: 'userInfo',
params: [account],
}))
const userInfos = await multicall(sousChefABI, userInfoCalls)
const userInfos = await multicallv2(sousChefABI, userInfoCalls, multicallOptions)

return userInfos
.reduce((accum: BigNumber, userInfo) => {
return accum.plus(new BigNumber(userInfo.amount._hex))
}, new BigNumber(0))
.toNumber()
return userInfos.reduce((accum: BigNumber, userInfo) => {
return accum.plus(new BigNumber(userInfo.amount._hex))
}, new BigNumber(0))
} catch (error) {
console.error('Coult not fetch staked value in pools', error)
return 0
console.error('Error fetching staked values:', error)
return BIG_ZERO
}
}
35 changes: 24 additions & 11 deletions src/utils/multicall.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Web3 from 'web3'
import { AbiItem } from 'web3-utils'
import { Interface } from '@ethersproject/abi'
import { getWeb3NoAccount } from 'utils/web3'
Expand All @@ -10,16 +11,26 @@ interface Call {
params?: any[] // Function params
}

const multicall = async (abi: any[], calls: Call[]) => {
const web3 = getWeb3NoAccount()
const multi = new web3.eth.Contract(MultiCallAbi as unknown as AbiItem, getMulticallAddress())
const itf = new Interface(abi)
interface MulticallOptions {
web3?: Web3
blockNumber?: number
requireSuccess?: boolean
}

const calldata = calls.map((call) => [call.address.toLowerCase(), itf.encodeFunctionData(call.name, call.params)])
const { returnData } = await multi.methods.aggregate(calldata).call()
const res = returnData.map((call, i) => itf.decodeFunctionResult(calls[i].name, call))
const multicall = async (abi: any[], calls: Call[], options: MulticallOptions = {}) => {
try {
const web3 = options.web3 || getWeb3NoAccount()
const multi = new web3.eth.Contract(MultiCallAbi as unknown as AbiItem, getMulticallAddress())
const itf = new Interface(abi)

return res
const calldata = calls.map((call) => [call.address.toLowerCase(), itf.encodeFunctionData(call.name, call.params)])
const { returnData } = await multi.methods.aggregate(calldata).call(undefined, options.blockNumber)
const res = returnData.map((call, i) => itf.decodeFunctionResult(calls[i].name, call))

return res
} catch (error) {
throw new Error(error)
}
}

/**
Expand All @@ -28,13 +39,15 @@ const multicall = async (abi: any[], calls: Call[]) => {
* 1. If "requireSuccess" is false multicall will not bail out if one of the calls fails
* 2. The return inclues a boolean whether the call was successful e.g. [wasSuccessfull, callResult]
*/
export const multicallv2 = async (abi: any[], calls: Call[], requireSuccess = true) => {
const web3 = getWeb3NoAccount()
export const multicallv2 = async (abi: any[], calls: Call[], options: MulticallOptions = {}) => {
const web3 = options.web3 || getWeb3NoAccount()
const multi = new web3.eth.Contract(MultiCallAbi as unknown as AbiItem, getMulticallAddress())
const itf = new Interface(abi)

const calldata = calls.map((call) => [call.address.toLowerCase(), itf.encodeFunctionData(call.name, call.params)])
const returnData = await multi.methods.tryAggregate(requireSuccess, calldata).call()
const returnData = await multi.methods
.tryAggregate(options.requireSuccess === undefined ? true : options.requireSuccess, calldata)
.call(undefined, options.blockNumber)
const res = returnData.map((call, i) => {
const [result, data] = call
return {
Expand Down
8 changes: 7 additions & 1 deletion src/utils/web3.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Web3 from 'web3'
import { HttpProviderOptions } from 'web3-core-helpers'
import { ARCHIVED_NODE } from 'config/constants/endpoints'
import getRpcUrl from 'utils/getRpcUrl'

const RPC_URL = getRpcUrl()
Expand All @@ -10,5 +11,10 @@ const getWeb3NoAccount = () => {
return web3NoAccount
}

export { getWeb3NoAccount }
const getWeb3WithArchivedNodeProvider = () => {
const archivedHttpProvider = new Web3.providers.HttpProvider(ARCHIVED_NODE, { timeout: 10000 } as HttpProviderOptions)
return new Web3(archivedHttpProvider)
}

export { getWeb3NoAccount, getWeb3WithArchivedNodeProvider }
export default web3NoAccount

0 comments on commit 4f99ef0

Please sign in to comment.