feat(warpMonitor): Add support for sei-FASTUSD warp monitor (#4702)

### Description
- Support sei-ethereum-FastUSD warp monitor
- Support xERC20 balance metrics

### Testing

Manual
pull/4709/head
Mohammed Hussan 1 month ago committed by GitHub
parent 9c0c4bbe21
commit a54210583e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml
  2. 21
      typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml
  3. 199
      typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts

@ -9,7 +9,7 @@ data:
name: Renzo Restaked ETH
symbol: ezETH
hypAddress: '0xC59336D8edDa9722B4f1Ec104007191Ec16f7087'
tokenAddress: '0x2416092f143378750bb29b79eD961ab195CcEea5'
tokenAddress: '0xbf5495Efe5DB9ce00f80364C8B423567e58d2110'
decimals: 18
bsc:
protocolType: ethereum

@ -0,0 +1,21 @@
description: Hyperlane Warp Route artifacts
timestamp: '2024-10-17T14:00:00.000Z'
deployer: Abacus Works (Hyperlane)
data:
config:
ethereum:
protocolType: ethereum
type: collateral
hypAddress: '0x9AD81058c6C3Bf552C9014CB30E824717A0ee21b'
tokenAddress: '0x15700B564Ca08D9439C58cA5053166E8317aa138'
name: fastUSD
symbol: fastUSD
decimals: 18
sei:
protocolType: ethereum
type: xERC20
hypAddress: '0xeA895A7Ff45d8d3857A04c1E38A362f3bd9a076f'
tokenAddress: '0x37a4dD9CED2b19Cfe8FAC251cd727b5787E45269'
name: fastUSD
symbol: fastUSD
decimals: 18

@ -55,10 +55,11 @@ const xERC20LimitsGauge = new Gauge({
name: 'hyperlane_xerc20_limits',
help: 'Current minting and burning limits of xERC20 tokens',
registers: [metricsRegister],
labelNames: ['chain_name', 'limit_type'],
labelNames: ['chain_name', 'limit_type', 'token_name'],
});
interface xERC20Limit {
tokenName: string;
mint: number;
burn: number;
mintMax: number;
@ -102,18 +103,7 @@ async function main(): Promise<boolean> {
const registry = await envConfig.getRegistry();
const chainMetadata = await registry.getMetadata();
// TODO: eventually support token balance checks for xERC20 token type also
if (
Object.values(tokenConfig).some(
(token) =>
token.type === TokenType.XERC20 ||
token.type === TokenType.XERC20Lockbox,
)
) {
await checkXERC20Limits(checkFrequency, tokenConfig, chainMetadata);
} else {
await checkTokenBalances(checkFrequency, tokenConfig, chainMetadata);
}
await checkWarpRouteMetrics(checkFrequency, tokenConfig, chainMetadata);
return true;
}
@ -136,7 +126,7 @@ async function checkBalance(
ethers.utils.formatUnits(nativeBalance, token.decimals),
);
}
case ProtocolType.Sealevel:
case ProtocolType.Sealevel: {
const adapter = new SealevelHypNativeAdapter(
chain,
multiProtocolProvider,
@ -155,6 +145,7 @@ async function checkBalance(
return parseFloat(
ethers.utils.formatUnits(balance, token.decimals),
);
}
case ProtocolType.Cosmos: {
if (!token.ibcDenom)
throw new Error('IBC denom missing for native token');
@ -245,7 +236,7 @@ async function checkBalance(
ethers.utils.formatUnits(syntheticBalance, token.decimals),
);
}
case ProtocolType.Sealevel:
case ProtocolType.Sealevel: {
if (!token.tokenAddress)
throw new Error('Token address missing for synthetic token');
const adapter = new SealevelHypSyntheticAdapter(
@ -265,12 +256,67 @@ async function checkBalance(
return parseFloat(
ethers.utils.formatUnits(syntheticBalance, token.decimals),
);
}
case ProtocolType.Cosmos:
// TODO - cosmos synthetic
return 0;
}
break;
}
case TokenType.XERC20: {
switch (token.protocolType) {
case ProtocolType.Ethereum: {
const provider = multiProtocolProvider.getEthersV5Provider(chain);
const hypXERC20 = HypXERC20__factory.connect(
token.hypAddress,
provider,
);
const xerc20Address = await hypXERC20.wrappedToken();
const xerc20 = IXERC20__factory.connect(xerc20Address, provider);
const syntheticBalance = await xerc20.totalSupply();
return parseFloat(
ethers.utils.formatUnits(syntheticBalance, token.decimals),
);
}
default:
throw new Error(
`Unsupported protocol type ${token.protocolType} for token type ${token.type}`,
);
}
}
case TokenType.XERC20Lockbox: {
switch (token.protocolType) {
case ProtocolType.Ethereum: {
if (!token.tokenAddress)
throw new Error(
'Token address missing for xERC20Lockbox token',
);
const provider = multiProtocolProvider.getEthersV5Provider(chain);
const hypXERC20Lockbox = HypXERC20Lockbox__factory.connect(
token.hypAddress,
provider,
);
const xerc20LockboxAddress = await hypXERC20Lockbox.lockbox();
const tokenContract = ERC20__factory.connect(
token.tokenAddress,
provider,
);
const collateralBalance = await tokenContract.balanceOf(
xerc20LockboxAddress,
);
return parseFloat(
ethers.utils.formatUnits(collateralBalance, token.decimals),
);
}
default:
throw new Error(
`Unsupported protocol type ${token.protocolType} for token type ${token.type}`,
);
}
}
}
return 0;
},
@ -301,46 +347,51 @@ export function updateTokenBalanceMetrics(
});
}
export function updateXERC20LimitsMetrics(xERC20Limits: ChainMap<xERC20Limit>) {
objMap(xERC20Limits, (chain: ChainName, limit: xERC20Limit) => {
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'mint',
})
.set(limit.mint);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'burn',
})
.set(limit.burn);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'mintMax',
})
.set(limit.mintMax);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'burnMax',
})
.set(limit.burnMax);
logger.info('xERC20 limits updated for chain', {
chain,
mint: limit.mint,
burn: limit.burn,
mintMax: limit.mintMax,
burnMax: limit.burnMax,
});
export function updateXERC20LimitsMetrics(
xERC20Limits: ChainMap<xERC20Limit | undefined>,
) {
objMap(xERC20Limits, (chain: ChainName, limits: xERC20Limit | undefined) => {
if (limits) {
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'mint',
token_name: limits.tokenName,
})
.set(limits.mint);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'burn',
token_name: limits.tokenName,
})
.set(limits.burn);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'mintMax',
token_name: limits.tokenName,
})
.set(limits.mintMax);
xERC20LimitsGauge
.labels({
chain_name: chain,
limit_type: 'burnMax',
token_name: limits.tokenName,
})
.set(limits.burnMax);
logger.info('xERC20 limits updated for chain', {
chain,
limits,
});
}
});
}
async function getXERC20Limits(
tokenConfig: WarpRouteConfig,
chainMetadata: ChainMap<ChainMetadata>,
): Promise<ChainMap<xERC20Limit>> {
): Promise<ChainMap<xERC20Limit | undefined>> {
const multiProtocolProvider = new MultiProtocolProvider(chainMetadata);
const output = objMap(
@ -358,7 +409,12 @@ async function getXERC20Limits(
);
const xerc20Address = await lockbox.xERC20();
const xerc20 = IXERC20__factory.connect(xerc20Address, provider);
return getXERC20Limit(routerAddress, xerc20, token.decimals);
return getXERC20Limit(
routerAddress,
xerc20,
token.decimals,
token.name,
);
}
case TokenType.XERC20: {
const provider = multiProtocolProvider.getEthersV5Provider(chain);
@ -369,10 +425,19 @@ async function getXERC20Limits(
);
const xerc20Address = await hypXERC20.wrappedToken();
const xerc20 = IXERC20__factory.connect(xerc20Address, provider);
return getXERC20Limit(routerAddress, xerc20, token.decimals);
return getXERC20Limit(
routerAddress,
xerc20,
token.decimals,
token.name,
);
}
default:
throw new Error(`Unsupported token type ${token.type}`);
logger.info(
`Unsupported token type ${token.type} for xERC20 limits check on protocol type ${token.protocolType}`,
);
return undefined;
}
}
default:
@ -388,12 +453,14 @@ const getXERC20Limit = async (
routerAddress: string,
xerc20: IXERC20,
decimals: number,
tokenName: string,
): Promise<xERC20Limit> => {
const mintCurrent = await xerc20.mintingCurrentLimitOf(routerAddress);
const mintMax = await xerc20.mintingMaxLimitOf(routerAddress);
const burnCurrent = await xerc20.burningCurrentLimitOf(routerAddress);
const burnMax = await xerc20.burningMaxLimitOf(routerAddress);
return {
tokenName,
mint: parseFloat(ethers.utils.formatUnits(mintCurrent, decimals)),
mintMax: parseFloat(ethers.utils.formatUnits(mintMax, decimals)),
burn: parseFloat(ethers.utils.formatUnits(burnCurrent, decimals)),
@ -401,37 +468,27 @@ const getXERC20Limit = async (
};
};
async function checkXERC20Limits(
async function checkWarpRouteMetrics(
checkFrequency: number,
tokenConfig: WarpRouteConfig,
chainMetadata: ChainMap<ChainMetadata>,
) {
setInterval(async () => {
try {
const xERC20Limits = await getXERC20Limits(tokenConfig, chainMetadata);
logger.info('xERC20 Limits:', xERC20Limits);
updateXERC20LimitsMetrics(xERC20Limits);
const multiProtocolProvider = new MultiProtocolProvider(chainMetadata);
const balances = await checkBalance(tokenConfig, multiProtocolProvider);
logger.info('Token Balances:', balances);
updateTokenBalanceMetrics(tokenConfig, balances);
} catch (e) {
logger.error('Error checking balances', e);
}
}, checkFrequency);
}
async function checkTokenBalances(
checkFrequency: number,
tokenConfig: WarpRouteConfig,
chainMetadata: ChainMap<ChainMetadata>,
) {
logger.info('Starting Warp Route balance monitor');
const multiProtocolProvider = new MultiProtocolProvider(chainMetadata);
setInterval(async () => {
try {
logger.debug('Checking balances');
const balances = await checkBalance(tokenConfig, multiProtocolProvider);
updateTokenBalanceMetrics(tokenConfig, balances);
const xERC20Limits = await getXERC20Limits(tokenConfig, chainMetadata);
logger.info('xERC20 Limits:', xERC20Limits);
updateXERC20LimitsMetrics(xERC20Limits);
} catch (e) {
logger.error('Error checking balances', e);
logger.error('Error checking xERC20 limits', e);
}
}, checkFrequency);
}

Loading…
Cancel
Save