Skip to content

Commit 4f10d60

Browse files
committed
Implement deployer balance checking
Progress on balance refunding
1 parent 0b258e7 commit 4f10d60

File tree

6 files changed

+113
-30
lines changed

6 files changed

+113
-30
lines changed

src/features/chains/metadata.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,19 @@ export async function assembleChainMetadata(
5757
(_, m): m is ChainMetadata => m.protocol === ProtocolType.Ethereum,
5858
);
5959

60-
const chainMetadata = mergeChainMetadataMap(evmRegistryChainMetadata, filesystemMetadata);
60+
let chainMetadata = mergeChainMetadataMap(evmRegistryChainMetadata, filesystemMetadata);
6161

6262
// Filter to only chains for which there are core deployment artifacts in the registry
6363
// May want to revisit this later but it would require a way for users to provide these addresses
64-
const chainsWithMailboxes = objFilter(
64+
chainMetadata = objFilter(
6565
chainMetadata,
6666
(c, m): m is ChainMetadata => !!chainAddresses[c]?.mailbox,
6767
);
6868

69+
// Filter to only chains with native token information as this is used in a few places
70+
chainMetadata = objFilter(chainMetadata, (_, m): m is ChainMetadata => !!m.nativeToken);
71+
6972
const chainMetadataWithOverrides = mergeChainMetadataMap(chainMetadata, storeMetadataOverrides);
7073

71-
return { chainMetadata: chainsWithMailboxes, chainMetadataWithOverrides };
74+
return { chainMetadata, chainMetadataWithOverrides };
7275
}

src/features/deployerWallet/fund.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
2-
EvmNativeTokenAdapter,
32
getChainIdNumber,
43
MultiProtocolProvider,
54
ProviderType,
5+
Token,
66
WarpTxCategory,
77
WarpTypedTransaction,
88
} from '@hyperlane-xyz/sdk';
@@ -80,15 +80,17 @@ async function executeTransfer({
8080
}) {
8181
logger.debug('Preparing to fund deployer', deployerAddress, chainName);
8282

83-
const protocol = multiProvider.getProtocol(chainName);
84-
const sendTransaction = transactionFns[protocol].sendTransaction;
85-
const activeChainName = activeChains.chains[protocol].chainName;
83+
const chainMetadata = multiProvider.getChainMetadata(chainName);
84+
const sendTransaction = transactionFns[chainMetadata.protocol].sendTransaction;
85+
const activeChainName = activeChains.chains[chainMetadata.protocol].chainName;
8686
const sender = getAccountAddressForChain(multiProvider, chainName, activeAccounts.accounts);
8787
if (!sender) throw new Error(`No active account found for chain ${chainName}`);
8888

8989
const amount = await getFundingAmount(chainName, gasUnits, multiProvider);
90-
await assertSenderBalance(sender, chainName, amount, multiProvider);
91-
const tx = await getFundingTx(deployerAddress, chainName, amount, multiProvider);
90+
91+
const token = Token.FromChainMetadataNativeToken(chainMetadata);
92+
await assertSenderBalance(sender, amount, token, multiProvider);
93+
const tx = await getFundingTx(deployerAddress, amount, token, multiProvider);
9294

9395
try {
9496
const { hash, confirm } = await sendTransaction({
@@ -112,6 +114,7 @@ async function executeTransfer({
112114
}
113115
}
114116

117+
// TODO multi-protocol support
115118
async function getFundingAmount(
116119
chainName: ChainName,
117120
gasUnits: bigint,
@@ -124,26 +127,27 @@ async function getFundingAmount(
124127

125128
async function assertSenderBalance(
126129
sender: Address,
127-
chainName: ChainName,
128130
amount: bigint,
131+
token: Token,
129132
multiProvider: MultiProtocolProvider,
130133
) {
131-
const adapter = new EvmNativeTokenAdapter(chainName, multiProvider, {});
132-
const balance = await adapter.getBalance(sender);
133-
assert(balance >= amount, 'Insufficient balance for deployment');
134+
const balance = await token.getBalance(multiProvider, sender);
135+
assert(balance.amount >= amount, 'Insufficient balance for deployment');
134136
}
135137

136138
// TODO edit Widgets lib to default to TypedTransaction instead of WarpTypedTransaction?
139+
// TODO multi-protocol support
137140
async function getFundingTx(
138141
recipient: Address,
139-
chainName: ChainName,
140142
amount: bigint,
143+
token: Token,
141144
multiProvider: MultiProtocolProvider,
142145
): Promise<WarpTypedTransaction> {
143-
const adapter = new EvmNativeTokenAdapter(chainName, multiProvider, {});
144-
const tx = await adapter.populateTransferTx({ recipient, weiAmountOrId: amount });
146+
const tx = token
147+
.getAdapter(multiProvider)
148+
.populateTransferTx({ recipient, weiAmountOrId: amount });
145149
// Add chainId to help reduce likely of wallet signing on wrong chain
146-
const chainId = getChainIdNumber(multiProvider.getChainMetadata(chainName));
150+
const chainId = getChainIdNumber(multiProvider.getChainMetadata(token.chainName));
147151
// TODO remove data when widgets lib is updated
148152
const txParams = { ...tx, chainId, data: '0x' };
149153
return {

src/features/deployerWallet/hooks.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ async function getOrCreateTempDeployerWallets(
7474
return wallets;
7575
}
7676

77+
// TODO multi-protocol support
7778
async function createTempDeployerWallet(protocol: ProtocolType): Promise<[TypedWallet, string]> {
7879
logger.info('Creating temp deployer wallet for:', protocol);
7980
let wallet: TypedWallet;
@@ -95,6 +96,7 @@ async function createTempDeployerWallet(protocol: ProtocolType): Promise<[TypedW
9596
return [wallet, encryptedKey];
9697
}
9798

99+
// TODO multi-protocol support
98100
async function getTempDeployerWallet(
99101
protocol: ProtocolType,
100102
encryptedKey: string,
@@ -113,10 +115,12 @@ async function getTempDeployerWallet(
113115
}
114116
}
115117

118+
// TODO multi-protocol support
116119
export function getDeployerAddressForProtocol(
117120
wallets: TempDeployerWallets,
118-
protocol: ProtocolType,
121+
protocol?: ProtocolType,
119122
) {
123+
if (!protocol) return undefined;
120124
const typedWallet = wallets[protocol];
121125
if (!typedWallet) return undefined;
122126
if (typedWallet.type === ProviderType.EthersV5) {

src/features/deployerWallet/refund.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,94 @@
1-
import { sleep } from '@hyperlane-xyz/utils';
1+
import { MultiProtocolProvider, Token } from '@hyperlane-xyz/sdk';
2+
import { ProtocolType } from '@hyperlane-xyz/utils';
23
import { useMutation } from '@tanstack/react-query';
3-
import { logger } from 'ethers';
44
import { useToastError } from '../../components/toast/useToastError';
5-
import { useTempDeployerWallets } from './hooks';
5+
import { logger } from '../../utils/logger';
6+
import { useMultiProvider } from '../chains/hooks';
7+
import { useDeploymentChains } from '../deployment/hooks';
8+
import { getDeployerAddressForProtocol, useTempDeployerWallets } from './hooks';
69
import { TempDeployerWallets } from './types';
710

811
export function useRefundDeployerAccounts({ onSuccess }: { onSuccess?: () => void }) {
12+
const multiProvider = useMultiProvider();
13+
const chains = useDeploymentChains();
914
const { wallets } = useTempDeployerWallets([]);
1015

1116
const { error, mutate } = useMutation({
12-
mutationKey: ['refundDeployerAccounts', wallets],
13-
mutationFn: () => refundDeployerAccounts(wallets),
17+
mutationKey: ['refundDeployerAccounts', chains, wallets],
18+
mutationFn: () => refundDeployerAccounts(chains, wallets, multiProvider),
1419
retry: 3,
1520
onSuccess,
1621
});
1722

18-
useToastError(error, 'Error refunding deployer balances. Please try again later.');
23+
useToastError(
24+
error,
25+
'Error refunding deployer balances. The key has been stored. Please try again later.',
26+
);
1927

2028
return mutate;
2129
}
2230

23-
async function refundDeployerAccounts(_wallets: TempDeployerWallets) {
24-
//TODO
31+
async function refundDeployerAccounts(
32+
chains: ChainName[],
33+
wallets: TempDeployerWallets,
34+
multiProvider: MultiProtocolProvider,
35+
) {
2536
logger.info('Refunding deployer accounts');
26-
await sleep(10_000);
37+
const nonZeroBalances = await getDeployerBalances(chains, wallets, multiProvider);
38+
const txReceipts = await transferBalances(nonZeroBalances, wallets, multiProvider);
2739
logger.info('Done refunding deployer accounts');
2840
return true;
2941
}
42+
43+
interface Balance {
44+
chainName: ChainName;
45+
protocol: ProtocolType;
46+
address: Address;
47+
amount: bigint;
48+
}
49+
50+
async function getDeployerBalances(
51+
chains: ChainName[],
52+
wallets: TempDeployerWallets,
53+
multiProvider: MultiProtocolProvider,
54+
) {
55+
const balances: Array<PromiseSettledResult<Balance | undefined>> = await Promise.allSettled(
56+
chains.map(async (chainName) => {
57+
const chainMetadata = multiProvider.tryGetChainMetadata(chainName);
58+
const address = getDeployerAddressForProtocol(wallets, chainMetadata?.protocol);
59+
if (!chainMetadata || !address) return undefined;
60+
const token = Token.FromChainMetadataNativeToken(chainMetadata);
61+
logger.debug('Checking balance', chainName, address);
62+
const balance = await token.getBalance(multiProvider, address);
63+
logger.debug('Balance retrieved', chainName, address, balance.amount);
64+
return { chainName, protocol: chainMetadata.protocol, address, amount: balance.amount };
65+
}),
66+
);
67+
const nonZeroBalances = balances
68+
.filter((b) => b.status === 'fulfilled')
69+
.map((b) => b.value)
70+
.filter((b): b is Balance => !!b && b.amount > 0n);
71+
logger.debug(
72+
'Non-zero balances found for chains:',
73+
nonZeroBalances.map((b) => b.chainName),
74+
);
75+
return nonZeroBalances;
76+
}
77+
78+
async function transferBalances(
79+
balances: Balance[],
80+
wallets: TempDeployerWallets,
81+
multiProvider: MultiProtocolProvider,
82+
) {
83+
const txReceipts: Array<PromiseSettledResult<string>> = await Promise.allSettled(
84+
balances.map(async (balance) => {
85+
const { chainName, protocol, address: deployerAddress, amount } = balance;
86+
const chainMetadata = multiProvider.getChainMetadata(chainName);
87+
const token = Token.FromChainMetadataNativeToken(chainMetadata);
88+
logger.debug('Preparing transfer', chainName, amount);
89+
// TODO generalize and call getFundingTx from fund.ts
90+
}),
91+
);
92+
93+
// TODO process txReceipts
94+
}

src/features/deployment/hooks.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useMemo } from 'react';
12
import { useStore } from '../store';
23
import { DeploymentType } from './types';
34

@@ -31,3 +32,11 @@ export function useDeploymentHistory() {
3132
currentIndex: state.deployments.length - 1,
3233
};
3334
}
35+
36+
export function useDeploymentChains() {
37+
const { deployments } = useDeploymentHistory();
38+
return useMemo<ChainName[]>(
39+
() => Array.from(new Set(deployments.map((d) => d.config.chains).flat())),
40+
[deployments],
41+
);
42+
}

src/global.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
declare type Address = string;
1+
declare type Address = UtilsAddress;
22
declare type ChainName = string;
3-
declare type ChainId = number | string;
4-
declare type DomainId = number;
53

64
declare module '*.yaml' {
75
const data: any;

0 commit comments

Comments
 (0)