Skip to content

Commit

Permalink
feat: remove multicalls from silo/updater
Browse files Browse the repository at this point in the history
  • Loading branch information
Space-Bean committed Oct 21, 2024
1 parent c388eeb commit 4f85ac6
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 155 deletions.
10 changes: 0 additions & 10 deletions projects/sdk/src/lib/BeanstalkSDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,6 @@ export class BeanstalkSDK {
this.DEBUG = config.DEBUG ?? false;

this.source = DataSource.LEDGER; // FIXME

console.log("[sdk/handleConfig]", {
config,
provider: this.provider,
signer: this.signer,
providerOrSigner: this.providerOrSigner,
});
}

deriveSource<T extends { source?: DataSource }>(config?: T): DataSource {
Expand All @@ -177,8 +170,6 @@ export class BeanstalkSDK {
const provider = config.signer ? (config.signer.provider as Provider) : config.provider;
const networkish = provider?._network || provider?.network || ChainResolver.defaultChainId;

console.log("[sdk/getProviderFromUrl]", url, networkish);

if (url.startsWith("ws")) {
return new ethers.providers.WebSocketProvider(url, networkish);
}
Expand All @@ -197,7 +188,6 @@ export class BeanstalkSDK {
}

private deriveChainId() {
console.log("[sdk/deriveChainId]", this.provider);
const { _network, network } = this.provider || {};
const providerChainId = _network?.chainId || network?.chainId || ChainResolver.defaultChainId;

Expand Down
25 changes: 1 addition & 24 deletions projects/ui/src/constants/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,4 @@ export const TESTNET_RPC_ADDRESSES: { [chainId: number]: string } = {
'https://rpc.vnet.tenderly.co/devnet/silo-v3/3ed19e82-a81c-45e5-9b16-5e385aa74587',
[SupportedChainId.ANVIL1]: 'https://anvil1.bean.money:443',
[SupportedChainId.LOCALHOST_ETH]: 'http://localhost:9545',
};

// BS3TODO: update me when these are ready
export const BEANSTALK_SUBGRAPH_ADDRESSES: { [chainId: number]: string } = {
[SupportedChainId.ETH_MAINNET]:
'https://graph.node.bean.money/subgraphs/name/beanstalk',
// [SupportedChainId.MAINNET]: 'https://api.thegraph.com/subgraphs/name/cujowolf/beanstalk',
[SupportedChainId.LOCALHOST]:
'https://api.thegraph.com/subgraphs/name/cujowolf/beanstalk-dev-replanted',
[SupportedChainId.TESTNET]:
'http://graph.playgrounds.academy/subgraphs/name/beanstalk',
};

// BS3TODO: update me when these are ready
/// The BEAN subgraph is slow to index because it tracks many events.
/// To speed up development time, Bean metrics are provided from a separate subgraph.
export const BEAN_SUBGRAPH_ADDRESSES: { [chainId: number]: string } = {
[SupportedChainId.ETH_MAINNET]:
'https://api.thegraph.com/subgraphs/name/cujowolf/bean',
[SupportedChainId.LOCALHOST]:
'https://api.thegraph.com/subgraphs/name/cujowolf/bean',
[SupportedChainId.TESTNET]:
'https://api.thegraph.com/subgraphs/name/cujowolf/bean',
};
};
256 changes: 136 additions & 120 deletions projects/ui/src/state/beanstalk/silo/updater.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { tokenIshEqual, transform } from '~/util';
import { tokenIshEqual, transform, getTokenIndex } from '~/util';
import useSdk from '~/hooks/sdk';
import { Token } from '@beanstalk/sdk';
import { ContractFunctionParameters } from 'viem';
import { multicall } from '@wagmi/core';
import { config } from '~/util/wagmi/config';
import {
BeanstalkSDK,
Token,
Clipboard,
AdvancedPipeStruct,
} from '@beanstalk/sdk';
import BNJS from 'bignumber.js';
import {
ABISnippets,
BEAN_TO_SEEDS,
BEAN_TO_STALK,
ONE_BN,
Expand All @@ -21,12 +22,6 @@ import useL2OnlyEffect from '~/hooks/chain/useL2OnlyEffect';
import { resetBeanstalkSilo, updateBeanstalkSilo } from './actions';
import { BeanstalkSiloBalance } from '.';

// limit to maximum of 20 calls per multicall. (5 * 4 = 20)
const MAX_BUCKETS = 5;

// 4 queries per token
const QUERIES_PER_TK = 4;

export const useFetchBeanstalkSilo = () => {
const dispatch = useDispatch();
const sdk = useSdk();
Expand All @@ -39,33 +34,24 @@ export const useFetchBeanstalkSilo = () => {
if (!beanstalk) return;

const BEAN = sdk.tokens.BEAN;
const STALK = sdk.tokens.STALK;

const wl = await sdk.contracts.beanstalk.getWhitelistedTokens();
const whitelist = wl
.map((t) => sdk.tokens.findByAddress(t))
.filter(Boolean) as Token[];

const wlContractCalls = buildWhitelistMultiCall(beanstalk, whitelist);
const siloCalls = buildBeanstalkSiloMultiCall(beanstalk.address);

const [stemTips, siloResults, wlResults] = await Promise.all([
const [stemTips, siloResults, wlResults, bdvs] = await Promise.all([
sdk.silo.getStemTips(),
multicall(config, { contracts: siloCalls }),
Promise.all(
wlContractCalls.map((calls) =>
multicall(config, { contracts: calls })
)
),
getSiloResults(sdk),
getWhitelistResults(sdk, whitelist),
getBDVs(sdk, whitelist),
]);

const parsedSiloData = siloResults.map((r) => parseCallResult(r));
const stalkTotal = transform(parsedSiloData[0], 'bnjs', STALK);
const rootsTotal = transform(parsedSiloData[1], 'bnjs');
const earnedBeansTotal = transform(parsedSiloData[2], 'bnjs', BEAN);
const stalkTotal = siloResults.totalStalk;
const rootsTotal = siloResults.totalRoots;
const earnedBeansTotal = siloResults.totalEarnedBeans;
const bdvTotal = ZERO_BN;

const chunked = chunkArray(wlResults.flat(), QUERIES_PER_TK);
const whitelistedAssetTotals: ({
address: string;
deposited: BNJS;
Expand All @@ -75,9 +61,8 @@ export const useFetchBeanstalkSilo = () => {
stemTip: ethers.BigNumber;
} | null)[] = [];

chunked.forEach((chunk, i) => {
Object.values(wlResults).forEach((datas, i) => {
const token = whitelist[i];
const data = chunk.map((d) => parseCallResult(d));

const stemTip = stemTips.get(token.address);
if (!stemTip) {
Expand All @@ -86,12 +71,10 @@ export const useFetchBeanstalkSilo = () => {

whitelistedAssetTotals.push({
address: token.address,
deposited: transform(data[0], 'bnjs', token),
depositedBdv: tokenIshEqual(token, sdk.tokens.BEAN)
? ONE_BN
: transform(data[1], 'bnjs', token),
totalGerminating: transform(data[2], 'bnjs', token),
bdvPerToken: transform(data[3], 'bnjs', BEAN),
deposited: datas.deposited,
depositedBdv: datas.depositedBdv,
totalGerminating: datas.germinating,
bdvPerToken: bdvs[i],
stemTip: stemTips.get(token.address) || ethers.BigNumber.from(0),
});
});
Expand All @@ -107,7 +90,7 @@ export const useFetchBeanstalkSilo = () => {
// because 1 bean = 1 stalk, 2 seeds
const activeStalkTotal = stalkTotal;
const earnedStalkTotal = earnedBeansTotal.times(BEAN_TO_STALK);
const earnedSeedTotal = earnedBeansTotal.times(BEAN_TO_SEEDS);
const earnedSeedTotal = earnedBeansTotal.times(BEAN_TO_SEEDS); // FIX ME

/// Aggregate balances
const balances = whitelistedAssetTotals.reduce((agg, curr) => {
Expand Down Expand Up @@ -195,96 +178,129 @@ const BeanstalkSiloUpdater = () => {

export default BeanstalkSiloUpdater;

// -- Helper Types
async function getSiloResults(sdk: BeanstalkSDK) {
const beanstalk = sdk.contracts.beanstalk;
const iBeanstalk = beanstalk.interface;

type CallParams = ContractFunctionParameters<typeof ABISnippets.siloGetters>;

type CallResult = Awaited<
ReturnType<typeof multicall<typeof config, CallParams[]>>
>[number];

// -- Helpers
const common = {
target: beanstalk.address,
clipboard: Clipboard.encode([]),
};

function parseCallResult(
result: CallResult,
defaultValue: bigint = -1n
): bigint {
if (result.error) return defaultValue;
return result.result;
const fnNames = ['totalStalk', 'totalRoots', 'totalEarnedBeans'] as const;

const calls: AdvancedPipeStruct[] = fnNames.map((fnName) => ({
...common,
callData: iBeanstalk.encodeFunctionData(fnName as any),
}));

const result = await beanstalk.callStatic.advancedPipe(calls, '0');

const _totalStalk = iBeanstalk.decodeFunctionResult(
'totalStalk',
result[0]
)[0];
const _totalRoots = iBeanstalk.decodeFunctionResult(
'totalRoots',
result[1]
)[0];
const _totalEarnedBeans = iBeanstalk.decodeFunctionResult(
'totalEarnedBeans',
result[2]
)[0];

return {
calls,
totalStalk: transform(_totalStalk, 'bnjs', sdk.tokens.STALK),
totalRoots: transform(_totalRoots, 'bnjs'),
totalEarnedBeans: transform(_totalEarnedBeans, 'bnjs', sdk.tokens.BEAN),
};
}

function buildWhitelistMultiCall(
beanstalk: ReturnType<typeof useSdk>['contracts']['beanstalk'],
// ensure silo whitelist is order is consistent w/ the multiCall results
whitelist: Token[]
): CallParams[][] {
const beanstalkAddress = beanstalk.address as `0x{string}`;
const contractCalls: CallParams[][] = [];

const shared = {
address: beanstalkAddress,
abi: ABISnippets.siloGetters,
};
interface WhitelistPipeResult {
deposited: BNJS;
depositedBdv: BNJS;
germinating: BNJS;
}

let callBucket: CallParams[] = [];
whitelist.forEach((token, i) => {
const tokenAddress = token.address as `0x{string}`;
const calls: CallParams[] = [
{
...shared,
functionName: 'getTotalDeposited',
args: [tokenAddress],
},
{
...shared,
functionName: 'getTotalDepositedBdv',
args: [tokenAddress],
},
{
...shared,
functionName: 'getGerminatingTotalDeposited',
args: [tokenAddress],
},
{
...shared,
functionName: 'bdv',
args: [tokenAddress, BigInt(token.fromHuman(1).blockchainString)],
},
];
callBucket.push(...calls);

if (i % MAX_BUCKETS === MAX_BUCKETS - 1) {
contractCalls.push(callBucket);
callBucket = [];
}
});
async function getWhitelistResults(sdk: BeanstalkSDK, whitelist: Token[]) {
const beanstalk = sdk.contracts.beanstalk;
const iBeanstalk = beanstalk.interface;

if (callBucket.length) contractCalls.push(callBucket);
const common = {
target: beanstalk.address,
clipboard: Clipboard.encode([]),
};

return contractCalls;
const allCalls: AdvancedPipeStruct[] = whitelist
.map((token) => {
const tokenAddress = token.address;
const calls = [
iBeanstalk.encodeFunctionData('getTotalDeposited', [tokenAddress]),
iBeanstalk.encodeFunctionData('getTotalDepositedBdv', [tokenAddress]),
iBeanstalk.encodeFunctionData('getGerminatingTotalDeposited', [
tokenAddress,
]),
];

return calls.map((c) => ({
...common,
callData: c,
}));
})
.flat();

const results = await beanstalk.callStatic.advancedPipe(allCalls, '0');

const chunkedByToken = chunkArray(results, 3);

return whitelist.reduce<TokenMap<WhitelistPipeResult>>((prev, token, i) => {
const tokenChunk = chunkedByToken[i];

const deposited = iBeanstalk.decodeFunctionResult(
'getTotalDeposited',
tokenChunk[0]
)[0];
const depositedBdv = iBeanstalk.decodeFunctionResult(
'getTotalDepositedBdv',
tokenChunk[1]
)[0];
const germinating = iBeanstalk.decodeFunctionResult(
'getGerminatingTotalDeposited',
tokenChunk[2]
)[0];

prev[getTokenIndex(token)] = {
deposited: transform(deposited, 'bnjs', token),
depositedBdv: transform(depositedBdv, 'bnjs', sdk.tokens.BEAN),
germinating: transform(germinating, 'bnjs', token),
};

return prev;
}, {});
}

function buildBeanstalkSiloMultiCall(beanstalkAddress: string): CallParams[] {
const shared = {
address: beanstalkAddress as `0x{string}`,
abi: ABISnippets.siloGetters,
};

return [
{
...shared,
functionName: 'totalStalk',
args: [],
},
{
...shared,
functionName: 'totalRoots',
args: [],
},
{
...shared,
functionName: 'totalEarnedBeans',
args: [],
},
];
async function getBDVs(sdk: BeanstalkSDK, whitelist: Token[]) {
const beanstalk = sdk.contracts.beanstalk;

return Promise.all(
whitelist.map((token) =>
beanstalk
.bdv(token.address, token.fromHuman(1).blockchainString)
.then((r) => {
if (tokenIshEqual(token, sdk.tokens.BEAN)) {
return ONE_BN;
}
return transform(r, 'bnjs', sdk.tokens.BEAN) as BNJS;
})
.catch((e) => {
console.debug(
'[beanstalk/silo/updater] bdv failed for token',
token.address,
e
);
return ZERO_BN;
})
)
);
}
2 changes: 1 addition & 1 deletion projects/ui/src/util/wagmi/ethersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const SHOW_DEV = import.meta.env.VITE_SHOW_DEV_CHAINS;

const fallbackChain = {
chainId: SHOW_DEV ? ChainId.LOCALHOST : ChainId.ARBITRUM_MAINNET,
name: SHOW_DEV ? 'locahost:8545' : 'arbitrum',
name: SHOW_DEV ? 'locahost:8545' : 'Arbitrum One',
} as const;

export function clientToProvider(client: Client<Transport, Chain>) {
Expand Down

0 comments on commit 4f85ac6

Please sign in to comment.