Skip to content

Commit

Permalink
feat: track ATA payer balances in warp monitors instead of key funder (
Browse files Browse the repository at this point in the history
…#4913)

### Description

- We need to track balances of "ATA payer" accounts, which are accounts
that have balances that are used to pay for rent for recipients that
have never received a particular token before. The balance is in the
native token as that's what the SVM uses to charge rent. Previously, we
were tracking these in the key funder in a very manual and annoying
process. Now, this is tracked automatically when a warp route monitor is
set up.
- Removed the old logic in the key funder
- Consolidated some code to ensure the same gauge is used to track
wallet balances throughout TS

What I'll do after this is merged:
- deploy an updated key funder to no longer track ATA payers
- deploy new warp route monitors with this

Example of the metric now included in the warp monitor:
```
# HELP hyperlane_wallet_balance Current balance of a wallet for a token
# TYPE hyperlane_wallet_balance gauge
hyperlane_wallet_balance{chain="eclipsemainnet",wallet_address="CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ",wallet_name="SOL/eclipsemainnet-solanamainnet/ata-payer",token_symbol="Native",token_name="Native"} 0.039941128

```

### Drive-by changes

- Some drive-bys to allow you to omit a warp route ID as a CLI arg to
some scripts to get an interactive prompt. Found this useful as it's
annoying to remember and type out the warp route ID structure

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

<!--
Are these changes backward compatible? Are there any infrastructure
implications, e.g. changes that would prohibit deploying older commits
using this infra tooling?

Yes/No
-->

### Testing

<!--
What kind of testing have these changes undergone?

None/Manual/Unit Tests
-->
  • Loading branch information
tkporter authored Nov 28, 2024
1 parent 24784af commit ff9e8a7
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 205 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-roses-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': minor
---

Added a getter to derive ATA payer accounts on Sealevel warp routes
2 changes: 1 addition & 1 deletion typescript/infra/config/environments/mainnet3/funding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig<
> = {
docker: {
repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo',
tag: 'd834d81-20241125-135642',
tag: 'aac6787-20241128-103715',
},
// We're currently using the same deployer/key funder key as mainnet2.
// To minimize nonce clobbering we offset the key funder cron
Expand Down
36 changes: 36 additions & 0 deletions typescript/infra/scripts/agent-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { checkbox, select } from '@inquirer/prompts';
import path, { join } from 'path';
import yargs, { Argv } from 'yargs';

Expand All @@ -23,6 +24,7 @@ import {

import { Contexts } from '../config/contexts.js';
import { agents } from '../config/environments/agents.js';
import { WarpRouteIds } from '../config/environments/mainnet3/warp/warpIds.js';
import { validatorBaseConfigsFn } from '../config/environments/utils.js';
import {
getChain,
Expand Down Expand Up @@ -279,6 +281,40 @@ export function withTxHashes<T>(args: Argv<T>) {
.alias('t', 'txHashes');
}

// Interactively gets a single warp route ID
export async function getWarpRouteIdInteractive() {
const choices = Object.values(WarpRouteIds).map((id) => ({
value: id,
}));
return select({
message: 'Select Warp Route ID',
choices,
pageSize: 30,
});
}

// Interactively gets multiple warp route IDs
export async function getWarpRouteIdsInteractive() {
const choices = Object.values(WarpRouteIds).map((id) => ({
value: id,
}));

let selection: WarpRouteIds[] = [];

while (!selection.length) {
selection = await checkbox({
message: 'Select Warp Route IDs',
choices,
pageSize: 30,
});
if (!selection.length) {
console.log('Please select at least one Warp Route ID');
}
}

return selection;
}

// not requiring to build coreConfig to get agentConfig
export async function getAgentConfigsBasedOnArgs(argv?: {
environment: DeployEnvironment;
Expand Down
3 changes: 2 additions & 1 deletion typescript/infra/scripts/check/check-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { logViolationDetails } from '../../src/utils/violation.js';
import {
Modules,
getArgs as getRootArgs,
getWarpRouteIdInteractive,
withAsDeployer,
withChains,
withContext,
Expand Down Expand Up @@ -192,7 +193,7 @@ export async function getGovernor(
governor = new ProxiedRouterGovernor(checker);
} else if (module === Modules.WARP) {
if (!warpRouteId) {
throw new Error('Warp route id required for warp module');
warpRouteId = await getWarpRouteIdInteractive();
}
const config = await getWarpConfig(multiProvider, envConfig, warpRouteId);
const warpAddresses = getWarpAddresses(warpRouteId);
Expand Down
165 changes: 10 additions & 155 deletions typescript/infra/scripts/funding/fund-keys-from-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ import {
KeyFunderConfig,
} from '../../src/config/funding.js';
import { FundableRole, Role } from '../../src/roles.js';
import { submitMetrics } from '../../src/utils/metrics.js';
import {
getWalletBalanceGauge,
submitMetrics,
} from '../../src/utils/metrics.js';
import {
assertContext,
assertFundableRole,
Expand Down Expand Up @@ -65,30 +68,19 @@ const L2ToL1: ChainMap<ChainName> = {
base: 'ethereum',
};

// Manually adding these labels as we are using a push gateway,
// and ordinarily these labels would be added via K8s annotations
const constMetricLabels = {
// this needs to get set in main because of async reasons
hyperlane_deployment: '',
hyperlane_context: 'hyperlane',
};

const metricsRegister = new Registry();

const walletBalanceGauge = new Gauge({
// Mirror the rust/main/ethers-prometheus `wallet_balance` gauge metric.
name: 'hyperlane_wallet_balance',
help: 'Current balance of eth and other tokens in the `tokens` map for the wallet addresses in the `wallets` set',
registers: [metricsRegister],
labelNames: [
'chain',
'wallet_address',
'wallet_name',
'token_address',
'token_symbol',
'token_name',
...(Object.keys(constMetricLabels) as (keyof typeof constMetricLabels)[]),
],
});
metricsRegister.registerMetric(walletBalanceGauge);
const walletBalanceGauge = getWalletBalanceGauge(
metricsRegister,
Object.keys(constMetricLabels),
);

// Min delta is 50% of the desired balance
const MIN_DELTA_NUMERATOR = ethers.BigNumber.from(5);
Expand All @@ -98,88 +90,6 @@ const MIN_DELTA_DENOMINATOR = ethers.BigNumber.from(10);
const RC_FUNDING_DISCOUNT_NUMERATOR = ethers.BigNumber.from(2);
const RC_FUNDING_DISCOUNT_DENOMINATOR = ethers.BigNumber.from(10);

interface SealevelAccount {
pubkey: PublicKey;
walletName: string;
}

const sealevelAccountsToTrack: ChainMap<SealevelAccount[]> = {
solanamainnet: [
{
// WIF warp route ATA payer
pubkey: new PublicKey('R5oMfxcbjx4ZYK1B2Aic1weqwt2tQsRzFEGe5WJfAxh'),
walletName: 'WIF/eclipsemainnet-solanamainnet/ata-payer',
},
{
// USDC warp route ATA payer
pubkey: new PublicKey('A1XtL9mAzkNEpBPinrCpDRrPqVAFjgaxDk4ATFVoQVyc'),
walletName: 'USDC/eclipsemainnet-ethereum-solanamainnet/ata-payer',
},
{
// USDT warp route ATA payer
pubkey: new PublicKey('9i3kYQqMtkm4sw1w5SQ8ebKMmh4LPVYKZRMPaZeRfn37'),
walletName: 'USDT/eclipsemainnet-ethereum-solanamainnet/ata-payer',
},
{
// ORCA warp route ATA payer
pubkey: new PublicKey('HqAVwQA6rh1TGdyUHi2XqmCtBSyG3DZjjsCLRXWqyNuU'),
walletName: 'ORCA/eclipsemainnet-solanamainnet/ata-payer',
},
],
eclipsemainnet: [
{
// WIF warp route ATA payer
pubkey: new PublicKey('HCQAfDd5ytAEidzR9g7CipjEGv2ZrSSZq1UY34oDFv8h'),
walletName: 'WIF/eclipsemainnet-solanamainnet/ata-payer',
},
{
// USDC warp route ATA payer
pubkey: new PublicKey('7arS1h8nwVVmmTVWSsu9rQ4WjLBN8iAi4DvHi8gWjBNC'),
walletName: 'USDC/eclipsemainnet-ethereum-solanamainnet/ata-payer',
},
{
// tETH warp route ATA payer
pubkey: new PublicKey('Hyy4jryRxgZm5pvuSx29fXxJ9J55SuDtXiCo89kmNuz5'),
walletName: 'tETH/eclipsemainnet-ethereum/ata-payer',
},
{
// SOL warp route ATA payer
pubkey: new PublicKey('CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ'),
walletName: 'SOL/eclipsemainnet-solanamainnet/ata-payer',
},
{
// stTIA warp route ATA payer
pubkey: new PublicKey('Bg3bAM3gEhdam5mbPqkiMi3mLZkoAieakMRdMHo6mbcn'),
walletName: 'stTIA/eclipsemainnet-stride/ata-payer',
},
{
// TIA warp route ATA payer
pubkey: new PublicKey('AZs4Rw6H6YwJBKoHBCfChCitHnHvQcVGgrJwGh4bKmAf'),
walletName: 'TIA/eclipsemainnet-stride/ata-payer',
},
{
// USDT warp route ATA payer
pubkey: new PublicKey('78s5TD48q89EZqHNC2bfsswQXn6n3sn1ecGgqXgJe4hL'),
walletName: 'USDT/eclipsemainnet-ethereum-solanamainnet/ata-payer',
},
{
// ORCA warp route ATA payer
pubkey: new PublicKey('3ZyZHoDRzfYg4ug6Tx4Zywe6M5Vt19vPZFx9Ag8qqnXu'),
walletName: 'ORCA/eclipsemainnet-solanamainnet/ata-payer',
},
{
// WBTC warp route ATA payer
pubkey: new PublicKey('BH9VfgYaCWbwuupzsTfSy67yR4dwuCbXmFRrm6aAH2NQ'),
walletName: 'WBTC/eclipsemainnet-ethereum/ata-payer',
},
// weETHs warp route ATA payer
{
pubkey: new PublicKey('F4Y6kHrq9qVnmkQhQibxh8nCU2quw5y25z7u8jSHMvtq'),
walletName: 'weETHs/eclipsemainnet-ethereum/ata-payer',
},
],
};

// Funds key addresses for multiple contexts from the deployer key of the context
// specified via the `--context` flag.
// The --contexts-and-roles flag is used to specify the contexts and the key roles
Expand Down Expand Up @@ -556,13 +466,6 @@ class ContextFunder {
false,
);

if (
this.environment === 'mainnet3' &&
this.context === Contexts.Hyperlane
) {
await this.updateSolanaWalletBalanceGauge();
}

return failureOccurred;
}

Expand Down Expand Up @@ -909,54 +812,6 @@ class ContextFunder {
),
);
}

private async updateSolanaWalletBalanceGauge() {
for (const chain of Object.keys(sealevelAccountsToTrack) as ChainName[]) {
await this.updateSealevelWalletBalanceAccounts(
chain,
sealevelAccountsToTrack[chain],
);
}
}

private async updateSealevelWalletBalanceAccounts(
chain: ChainName,
accounts: SealevelAccount[],
) {
const rpcUrls = await getSecretRpcEndpoints(this.environment, chain);
const provider = new Connection(rpcUrls[0], 'confirmed');

for (const { pubkey, walletName } of accounts) {
logger.info(
{
chain,
pubkey: pubkey.toString(),
walletName,
},
'Fetching sealevel wallet balance',
);
const balance = await provider.getBalance(pubkey);
logger.info(
{
balance,
chain,
pubkey: pubkey.toString(),
walletName,
},
'Retrieved sealevel chain wallet balance',
);
walletBalanceGauge
.labels({
chain,
wallet_address: pubkey.toString(),
wallet_name: walletName,
token_symbol: 'Native',
token_name: 'Native',
...constMetricLabels,
})
.set(balance / 1e9);
}
}
}

async function getAddressInfo(
Expand Down
19 changes: 5 additions & 14 deletions typescript/infra/scripts/helloworld/kathy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import { owners } from '../../config/environments/testnet4/owners.js';
import { CloudAgentKey } from '../../src/agents/keys.js';
import { DeployEnvironment } from '../../src/config/environment.js';
import { Role } from '../../src/roles.js';
import { startMetricsServer } from '../../src/utils/metrics.js';
import {
getWalletBalanceGauge,
startMetricsServer,
} from '../../src/utils/metrics.js';
import { assertChain, diagonalize } from '../../src/utils/utils.js';
import { getArgs, withContext } from '../agent-utils.js';
import { getEnvironmentConfig } from '../core-utils.js';
Expand Down Expand Up @@ -70,19 +73,7 @@ const messageReceiptSeconds = new Counter({
registers: [metricsRegister],
labelNames: ['origin', 'remote'],
});
const walletBalance = new Gauge({
name: 'hyperlane_wallet_balance',
help: 'Current balance of eth and other tokens in the `tokens` map for the wallet addresses in the `wallets` set',
registers: [metricsRegister],
labelNames: [
'chain',
'wallet_address',
'wallet_name',
'token_address',
'token_symbol',
'token_name',
],
});
const walletBalance = getWalletBalanceGauge(metricsRegister);

/** The maximum number of messages we will allow to get queued up if we are sending too slowly. */
const MAX_MESSAGES_ALLOWED_TO_SEND = 5;
Expand Down
23 changes: 1 addition & 22 deletions typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { checkbox } from '@inquirer/prompts';
import yargs from 'yargs';

import { Contexts } from '../../config/contexts.js';
import { WarpRouteIds } from '../../config/environments/mainnet3/warp/warpIds.js';
Expand All @@ -9,6 +8,7 @@ import {
assertCorrectKubeContext,
getAgentConfig,
getArgs,
getWarpRouteIdsInteractive,
withWarpRouteId,
} from '../agent-utils.js';
import { getEnvironmentConfig } from '../core-utils.js';
Expand Down Expand Up @@ -41,27 +41,6 @@ async function main() {
}
}

async function getWarpRouteIdsInteractive() {
const choices = Object.values(WarpRouteIds).map((id) => ({
value: id,
}));

let selection: WarpRouteIds[] = [];

while (!selection.length) {
selection = await checkbox({
message: 'Select Warp Route IDs to deploy',
choices,
pageSize: 30,
});
if (!selection.length) {
console.log('Please select at least one Warp Route ID');
}
}

return selection;
}

main()
.then(() => console.log('Deploy successful!'))
.catch(console.error);
Loading

0 comments on commit ff9e8a7

Please sign in to comment.