Skip to content

Commit

Permalink
Feat: Pull balances /w block-height | fetch tokenPrice for native tok…
Browse files Browse the repository at this point in the history
…en (#119)

* Pull balances|Add opt block height /w fetching native balance

* Add commitment in solana getBlockHeight

* Refactor appending token balances in usd

* Fix minor issue | fix type issue

* Bump with beta version

* Make sure to compute tokenBalance in bigint

* Refactor | Add usd balance for native tokens

* Update code for all the chains

* Fix type issue

* Use number instead of bigint to avoid conversion issues

* Fix cache invalidation of token prices

* Accept blockHeightByChain as option in pullBalances method

* Sync client wallet manager with wallet-manager changes

* Fix Injecting native tokens logic

* Lift priceFeedOptions as walletOptions

* Optimize price feed instance instantiation

* Bump version

* Add comment for wallet balance config

* Add chainName in balances schema

* Bump version

* minor update

* publish beta version for integration

* Revert mandatory blockHeight

* Expose blockHeightPerChain method|remove chainName from balance

* Bump package to latest version w.r.t main branch

* Resolve PR review comment

* Add coingeckoIds for cosm-wasm chains

* Bump version
  • Loading branch information
abhidtu2014 authored Dec 4, 2023
1 parent 435527f commit b386051
Show file tree
Hide file tree
Showing 25 changed files with 999 additions and 339 deletions.
63 changes: 49 additions & 14 deletions examples/wallet-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,42 @@ const allChainWallets: WalletManagerFullConfig['config'] = {
tokens: ["WBTC"]
}
],
priceFeedConfig: {
walletBalanceConfig: {
enabled: true,
scheduled: {
enabled: true
},
enabled: false,
}
},
priceFeedConfig: {
supportedTokens: [{
chainId: 2,
chainName: "ethereum",
tokenContract: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
coingeckoId: "usd-coin",
symbol: "USDC"
},
{
chainId: 2,
chainName: "ethereum",
tokenContract: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
coingeckoId: "wrapped-bitcoin",
symbol: "WBTC"
}],
enabled: true
}
},
solana: {
wallets: [
{ address: "6VnfVsLdLwNuuCmooLTziQ99PFXZ5vc3yyqyb9tMDhhw", tokens: ['usdc'] },
],
walletBalanceConfig: {
enabled: true,
scheduled: {
enabled: false,
}
},
priceFeedConfig: {
supportedTokens: []
}
},
sui: {
rebalance: {
Expand All @@ -62,14 +75,20 @@ const allChainWallets: WalletManagerFullConfig['config'] = {
tokens: ['USDC', 'USDT']
},
],
walletBalanceConfig: {
enabled: true,
scheduled: {
enabled: false,
}
},
priceFeedConfig: {
supportedTokens: [{
chainId: 21,
chainName: "sui",
tokenContract: "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf",
coingeckoId: "usd-coin",
symbol: "USDC"
}],
enabled: false,
}
},
klatyn: {
Expand All @@ -88,7 +107,16 @@ const allChainWallets: WalletManagerFullConfig['config'] = {
address: "0x8d0d970225597085A59ADCcd7032113226C0419d",
tokens: []
}
]
],
walletBalanceConfig: {
enabled: true,
scheduled: {
enabled: false,
}
},
priceFeedConfig: {
supportedTokens: []
}
}
}

Expand All @@ -103,15 +131,22 @@ export const manager = buildWalletManager({
enabled: true,
serve: true,
port: 9091,
},
priceFeedOptions: {
enabled: true,
scheduled: {
enabled: false,
}
}
}
});


// Note: Below code needs wallet's private key to be set in config abopve for aquiring wallet
// manager.withWallet('ethereum', async (wallet) => {
// console.log('Address', wallet.address);
// console.log('Block height', wallet.walletToolbox.getBlockHeight());
// console.log('Native balances', await wallet.walletToolbox.pullNativeBalance(wallet.address));
// console.log('Token balances', await wallet.walletToolbox.pullTokenBalances(wallet.address, ['0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599']));
// })
(async () => {
try {
console.time('balances')
const balances = await manager.pullBalancesAtBlockHeight();
console.timeLog('balances', JSON.stringify(balances))
} catch (err) {
console.error('Failed to pullBalancesAtBlockHeight', err);
}
})();
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@xlabs-xyz/wallet-monitor",
"version": "0.2.25",
"version": "0.2.26",
"description": "A set of utilities to monitor blockchain wallets and react to them",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
5 changes: 3 additions & 2 deletions src/balances/evm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ export async function pullEvmTokenBalance(
export async function pullEvmNativeBalance(
provider: ethers.providers.JsonRpcProvider,
address: string,
blockHeight?: number,
): Promise<Balance>{
const weiAmount = await provider.getBalance(address);
const weiAmount = await provider.getBalance(address, blockHeight);

return {
isNative: true,
rawBalance: weiAmount.toString(),
Expand Down
6 changes: 4 additions & 2 deletions src/balances/solana.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import bs58 from 'bs58';
import { Connection, PublicKey, Keypair } from "@solana/web3.js";
import { Balance } from "./index";
import { SOLANA_DEFAULT_COMMITMENT } from '../wallets/solana/solana.config';

export async function pullSolanaNativeBalance(
connection: Connection,
address: string
address: string,
): Promise<Balance> {
const lamports = await connection.getBalance(new PublicKey(address))
// solana web3.js doesn't support passing exact slot(block number) only minSlot, while fetching balance
const lamports = await connection.getBalance(new PublicKey(address), SOLANA_DEFAULT_COMMITMENT)

return {
isNative: true,
Expand Down
12 changes: 11 additions & 1 deletion src/balances/sui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface SuiTransactionDetails {
export async function pullSuiNativeBalance(conn: Connection, address: string): Promise<WalletBalance> {
const provider = new JsonRpcProvider(conn);

// mysten SDK doesn't support passing checkpoint (block number) to getBalance
// https://github.com/MystenLabs/sui/issues/14137
const rawBalance = await provider.getBalance({ owner: address });

return {
Expand Down Expand Up @@ -54,7 +56,15 @@ export async function pullSuiTokenData(
export async function pullSuiTokenBalances(
conn: Connection,
address: string
): Promise<any> {
): Promise<{
coinType: string;
coinObjectCount: number;
totalBalance: string;
lockedBalance: {
number?: number | undefined;
epochId?: number | undefined;
};
}[]> {
const provider = new JsonRpcProvider(conn);

return provider.getAllBalances({ owner: address });
Expand Down
57 changes: 37 additions & 20 deletions src/chain-wallet-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@ import { CosmosProvider, CosmosWallet } from "./wallets/cosmos";
import { EVMProvider, EVMWallet } from "./wallets/evm";
import { SolanaProvider, SolanaWallet } from "./wallets/solana";
import { SuiProvider, SuiWallet } from "./wallets/sui";
import {
PriceFeed,
WalletPriceFeedConfig,
WalletRebalancingConfig,
} from "./wallet-manager";
import { ScheduledPriceFeed } from "./price-assistant/scheduled-price-feed";
import { OnDemandPriceFeed } from "./price-assistant/ondemand-price-feed";
import { PriceFeed, WalletBalanceConfig, WalletPriceFeedConfig, WalletRebalancingConfig } from "./wallet-manager";

const DEFAULT_POLL_INTERVAL = 60 * 1000;
const DEFAULT_REBALANCE_INTERVAL = 60 * 1000;
Expand All @@ -42,6 +36,7 @@ export type ChainWalletManagerOptions = {
maxGasPrice?: number;
gasLimit?: number;
};
walletBalanceConfig: WalletBalanceConfig;
priceFeedConfig: WalletPriceFeedConfig;
balancePollInterval?: number;
walletOptions?: WalletOptions;
Expand Down Expand Up @@ -85,7 +80,11 @@ export class ChainWalletManager {
public walletToolbox: Wallet;
protected priceFeed?: PriceFeed;

constructor(options: any, private wallets: WalletConfig[]) {
constructor(
options: any,
private wallets: WalletConfig[],
priceFeedInstance?: PriceFeed,
) {
this.validateOptions(options);
this.options = this.parseOptions(options);

Expand All @@ -94,15 +93,7 @@ export class ChainWalletManager {
}

this.logger = createLogger(this.options.logger);
const { priceFeedConfig } = this.options;

if (priceFeedConfig?.enabled) {
if (priceFeedConfig?.scheduled?.enabled) {
this.priceFeed = new ScheduledPriceFeed(priceFeedConfig, this.logger);
} else {
this.priceFeed = new OnDemandPriceFeed(priceFeedConfig, this.logger);
}
}
this.priceFeed = priceFeedInstance

this.walletToolbox = createWalletToolbox(
options.network,
Expand Down Expand Up @@ -274,9 +265,20 @@ export class ChainWalletManager {
);
this.priceFeed?.start();
}
this.interval = setInterval(async () => {
await this.refreshBalances();
}, this.options.balancePollInterval);

if (this.options.walletBalanceConfig?.enabled) {
if (this.options.walletBalanceConfig?.scheduled?.enabled) {
this.interval = setInterval(async () => {
await this.refreshBalances();
}, this.options.walletBalanceConfig?.scheduled?.interval ?? this.options.balancePollInterval);
} else {
// no op: Don't poll balances, fetch on demand instead
}
} else {
this.interval = setInterval(async () => {
await this.refreshBalances();
}, this.options.balancePollInterval);
}

if (this.options.rebalance.enabled) {
this.logger.info(
Expand Down Expand Up @@ -419,4 +421,19 @@ export class ChainWalletManager {
this.availableWalletsByChainName[chainName],
);
}

public getBlockHeight () {
return this.walletToolbox.getBlockHeight();
}
/** Pull balances on demand */
public async pullBalances () {
const balances = await this.walletToolbox.pullBalances();
return this.mapBalances(balances);
}

/** Pull balances on demand with block height */
public async pullBalancesAtBlockHeight(blockHeight: number) {
const balances = await this.walletToolbox.pullBalancesAtBlockHeight(blockHeight);
return this.mapBalances(balances);
}
}
Loading

0 comments on commit b386051

Please sign in to comment.