Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(InventoryClient): Support 1:many HubPool mappings #1465

Merged
merged 98 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
110bcb1
refactor(RelayerConfig): Simplify InventoryConfig parsing
pxrl Apr 29, 2024
1dacc1e
Merge branch 'master' into pxrl/inventoryParsing
pxrl Apr 30, 2024
703f028
Additional simplification
pxrl Apr 30, 2024
b2fa39a
Drop
pxrl Apr 30, 2024
dd5e453
Lint & chill
pxrl Apr 30, 2024
9efd8fe
improve(RelayerConfig): Permit symbol-based token config
pxrl Apr 30, 2024
20da413
feat(InventoryClient): Support 1:many HubPool mappings
pxrl Apr 30, 2024
951a94d
Merge remote-tracking branch 'origin/master' into pxrl/usdcInventory
pxrl May 1, 2024
c57caef
Merge remote-tracking branch 'origin/master' into pxrl/usdcInventory
pxrl May 1, 2024
e3b6665
Update repayment shortfall check
pxrl May 1, 2024
ec623d6
Update
pxrl May 1, 2024
a48be1f
Update allocations & l2 token requirements
pxrl May 1, 2024
a65330a
Merge branch 'master' into pxrl/usdcInventory
pxrl May 1, 2024
56abc3c
WIP
pxrl May 1, 2024
cb5518b
wip...
pxrl May 1, 2024
ce2f5a1
Merge remote-tracking branch 'origin/master' into pxrl/usdcInventory
pxrl May 2, 2024
2e2f03b
Revert "wip..."
pxrl May 2, 2024
564e12e
Cleanup
pxrl May 2, 2024
d1d0f79
Typo
pxrl May 2, 2024
5159d12
Update getBalanceOnChain
pxrl May 2, 2024
8bef2e3
Migrate balance checker
pxrl May 2, 2024
2efc0f9
Fix
pxrl May 2, 2024
b064dee
Catch undefined destination tokens
pxrl May 2, 2024
f435d81
Fix test
pxrl May 3, 2024
33c3fac
lint
pxrl May 3, 2024
aefabe8
Fix test
pxrl May 3, 2024
73b1990
Merge branch 'master' into pxrl/usdcInventory
pxrl May 3, 2024
8c8abb6
Add comments
pxrl May 3, 2024
47fec54
Fix test
pxrl May 3, 2024
5a30873
Fix test
pxrl May 3, 2024
d26d1ac
Revert return
pxrl May 3, 2024
4f872bf
Rename type
pxrl May 3, 2024
26d8dfd
Initial tests
pxrl May 6, 2024
9f0def9
Fix tests
pxrl May 6, 2024
6fb41ea
Overhaul test
pxrl May 6, 2024
53811c5
Merge remote-tracking branch 'origin/master' into pxrl/usdcInventory
pxrl May 6, 2024
8d44904
Fix minor merge error
pxrl May 6, 2024
bd18e01
Tidy up
pxrl May 6, 2024
5b88f19
Fix balance check
pxrl May 6, 2024
0c40936
Sub in chain alias
pxrl May 6, 2024
5e80d91
Add l2Token
pxrl May 6, 2024
bca53b8
refactor(test): InventoryClient simplifications
pxrl May 6, 2024
520a284
Merge remote-tracking branch 'origin/pxrl/inventoryClientTest' into p…
pxrl May 6, 2024
1b89118
lint
pxrl May 6, 2024
97c5a3a
Merge remote-tracking branch 'origin/pxrl/inventoryClientTest' into p…
pxrl May 6, 2024
cb9db39
Simplify
pxrl May 6, 2024
90510b3
Merge remote-tracking branch 'origin/master' into pxrl/usdcInventory
pxrl May 6, 2024
eb11de6
refactor(CrossChainTransferClient): Support unique L2 tokens
pxrl May 1, 2024
85cab0a
lint
pxrl May 1, 2024
4da8064
Add InventoryClient shims
pxrl May 1, 2024
bb9343a
fix: resolve linea (#1472)
james-a-morris May 2, 2024
a1ccd0a
Merge branch 'master' into pxrl/usdcInventory
pxrl May 7, 2024
c73791d
Merge branch 'master' into pxrl/usdcInventory
pxrl May 7, 2024
baf3cbd
refactor(test): Cleanup InventoryClient refund chain test
pxrl May 7, 2024
f9c3bf1
lint
pxrl May 7, 2024
3f08e94
Merge branch 'master' into pxrl/inventoryClientTest
pxrl May 7, 2024
9aa7db0
Merge branch 'master' into pxrl/crossChainTransfer
james-a-morris May 7, 2024
9e05d1e
Merge branch 'pxrl/inventoryClientTest' into pxrl/usdcInventory
pxrl May 7, 2024
fc15beb
Initial repayment chain test
pxrl May 7, 2024
d04f442
Stragglers
pxrl May 7, 2024
e2535ec
Merge branch 'pxrl/inventoryClientTest' into pxrl/usdcInventory
pxrl May 7, 2024
06ede93
lint
pxrl May 7, 2024
e3da99e
Add rebalancing test
pxrl May 7, 2024
ea78310
Merge branch 'master' into pxrl/inventoryClientTest
pxrl May 7, 2024
bbd8fb9
Additional
pxrl May 7, 2024
6cb87f3
Tweak
pxrl May 7, 2024
ece343c
Merge branch 'pxrl/inventoryClientTest' into pxrl/usdcInventory
pxrl May 7, 2024
20ad610
Doc
pxrl May 7, 2024
2e1ce4c
Update method name & document
pxrl May 7, 2024
b1f2902
Merge branch 'master' into pxrl/crossChainTransfer
pxrl May 7, 2024
9eba919
Merge branch 'master' into pxrl/usdcInventory
pxrl May 8, 2024
4bdc030
Make l2Token required
pxrl May 8, 2024
4886cf2
Relocate instantiation
pxrl May 8, 2024
17c4d15
Merge branch 'master' into pxrl/crossChainTransfer
pxrl May 8, 2024
c17b614
lint
pxrl May 8, 2024
b9f0b4b
Identify todo
pxrl May 8, 2024
7fcbb42
improve(accounting): account for l2 routes on non-op-stack bridges (…
james-a-morris May 8, 2024
2b20472
Merge branch 'master' into pxrl/crossChainTransfer
james-a-morris May 8, 2024
4cce142
Merge branch 'master' into pxrl/crossChainTransfer
james-a-morris May 8, 2024
3fe67d4
Merge branch 'master' into pxrl/usdcInventory
pxrl May 8, 2024
a0836fe
Merge branch 'master' into pxrl/crossChainTransfer
pxrl May 8, 2024
eeb8641
chore: bump package
james-a-morris May 8, 2024
7d7c19f
Merge branch 'master' into pxrl/crossChainTransfer
pxrl May 8, 2024
6db6e60
Add missing l2Token arguments
pxrl May 9, 2024
4007994
Merge branch 'pxrl/crossChainTransfer' into pxrl/usdcInventory
pxrl May 9, 2024
d68728e
Update getBalanceOnChain
pxrl May 9, 2024
f518474
Fix test
pxrl May 9, 2024
d56505e
nit: lint
james-a-morris May 9, 2024
3d03d77
nit: optimize call
james-a-morris May 9, 2024
36e2287
nit: re-run test
james-a-morris May 9, 2024
16f04e6
Merge branch 'pxrl/crossChainTransfer' into pxrl/usdcInventory
pxrl May 9, 2024
0d759cb
lint
pxrl May 9, 2024
24cc6f6
nit: re-run test
james-a-morris May 9, 2024
7911dc1
fix(test): Avoid external RPC call
pxrl May 9, 2024
4f66a64
Merge branch 'pxrl/fixTest' into pxrl/crossChainTransfer
pxrl May 9, 2024
3c22411
Merge branch 'pxrl/crossChainTransfer' into pxrl/usdcInventory
pxrl May 9, 2024
901b217
Merge branch 'master' into pxrl/usdcInventory
pxrl May 9, 2024
69abda2
Merge branch 'master' into pxrl/usdcInventory
pxrl May 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 115 additions & 44 deletions src/clients/InventoryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
} from "../utils";
import { HubPoolClient, TokenClient, BundleDataClient } from ".";
import { AdapterManager, CrossChainTransferClient } from "./bridges";
import { InventoryConfig, V3Deposit } from "../interfaces";
import { V3Deposit } from "../interfaces";
import { InventoryConfig, isAliasConfig, TokenInventoryConfig } from "../interfaces/InventoryManagement";
import lodash from "lodash";
import { CONTRACT_ADDRESSES, SLOW_WITHDRAWAL_CHAINS } from "../common";
import { CombinedRefunds } from "../dataworker/DataworkerUtils";
Expand Down Expand Up @@ -71,6 +72,21 @@ export class InventoryClient {
this.formatWei = createFormatFunction(2, 4, false, 18);
}

getTokenConfig(hubPoolToken: string, chainId: number, spokePoolToken?: string): TokenInventoryConfig | undefined {
const tokenConfig = this.inventoryConfig.tokenConfig[hubPoolToken];
assert(isDefined(tokenConfig), `getTokenConfig: No token config found for ${hubPoolToken}.`);

if (isAliasConfig(tokenConfig)) {
assert(
isDefined(spokePoolToken),
`Cannot resolve ambiguous ${getNetworkName(chainId)} token config for ${hubPoolToken}`
);
return tokenConfig[spokePoolToken]?.[chainId];
} else {
return tokenConfig[chainId];
}
}

// Get the total balance across all chains, considering any outstanding cross chain transfers as a virtual balance on that chain.
getCumulativeBalance(l1Token: string): BigNumber {
return this.getEnabledChains()
Expand All @@ -86,17 +102,31 @@ export class InventoryClient {
return bnZero;
}

const balances = this.getDestinationTokensForL1Token(l1Token, chainId).map(
(token) => this.tokenClient.getBalance(Number(chainId), token) || bnZero
pxrl marked this conversation as resolved.
Show resolved Hide resolved
);

// If the chain does not have this token (EG BOBA on Optimism) then 0.
const balance =
this.tokenClient.getBalance(chainId, this.getDestinationTokenForL1Token(l1Token, chainId)) || bnZero;
const balance = balances.reduce((acc, curr) => acc.add(curr), bnZero);

// Consider any L1->L2 transfers that are currently pending in the canonical bridge.
return balance.add(
this.crossChainTransferClient.getOutstandingCrossChainTransferAmount(this.relayer, chainId, l1Token)
);
}

// Get the fraction of funds allocated on each chain.
// @note: l1Token is current needed because not all l2 tokens (USDC.e, USDbC, ...) map back to an L1 token.
getBalanceOnChain(chainId: number, l1Token: string, l2Token?: string): BigNumber {
l2Token ??= this.hubPoolClient.getL2TokenForL1TokenAtBlock(l1Token, chainId);
const balance = this.tokenClient.getBalance(chainId, l2Token);

// @todo: This will resolve all outstanding transfers for the L1 token, but it should be filtered for _only_
// transfers that apply to the specific L2 token. This needs to be fixed in the crossChainTransferClient
pxrl marked this conversation as resolved.
Show resolved Hide resolved
return balance.add(
this.crossChainTransferClient.getOutstandingCrossChainTransferAmount(this.relayer, chainId, l1Token)
);
}

getChainDistribution(l1Token: string): { [chainId: number]: TokenDistribution } {
const cumulativeBalance = this.getCumulativeBalance(l1Token);
const distribution: { [chainId: number]: TokenDistribution } = {};
Expand All @@ -105,13 +135,18 @@ export class InventoryClient {
// If token doesn't have entry on chain, skip creating an entry for it since we'll likely run into an error
// later trying to grab the chain equivalent of the L1 token via the HubPoolClient.
if (chainId === this.hubPoolClient.chainId || this._l1TokenEnabledForChain(l1Token, chainId)) {
const l2Token = this.getDestinationTokenForL1Token(l1Token, chainId);
if (cumulativeBalance.gt(bnZero)) {
distribution[chainId] ??= {};
distribution[chainId][l2Token] = this.getBalanceOnChainForL1Token(chainId, l1Token)
.mul(this.scalar)
.div(cumulativeBalance);
distribution[chainId] ??= {};
pxrl marked this conversation as resolved.
Show resolved Hide resolved

if (cumulativeBalance.eq(bnZero)) {
return;
}

const l2Tokens = this.getDestinationTokensForL1Token(l1Token, chainId);
l2Tokens.forEach((l2Token) => {
// THe effective balance is the current balance + inbound bridge transfers.
pxrl marked this conversation as resolved.
Show resolved Hide resolved
const effectiveBalance = this.getBalanceOnChain(chainId, l1Token, l2Token);
distribution[chainId][l2Token] = effectiveBalance.mul(this.scalar).div(cumulativeBalance);
});
}
});
return distribution;
Expand All @@ -125,28 +160,43 @@ export class InventoryClient {
}

// Get the balance of a given token on a given chain, including shortfalls and any pending cross chain transfers.
getCurrentAllocationPct(l1Token: string, chainId: number): BigNumber {
getCurrentAllocationPct(l1Token: string, chainId: number, l2Token?: string): BigNumber {
l2Token ??= this.hubPoolClient.getL2TokenForL1TokenAtBlock(l1Token, chainId);
pxrl marked this conversation as resolved.
Show resolved Hide resolved

// If there is nothing over all chains, return early.
const cumulativeBalance = this.getCumulativeBalance(l1Token);
if (cumulativeBalance.eq(bnZero)) {
return bnZero;
}

const shortfall = this.getTokenShortFall(l1Token, chainId);
const shortfall = this.tokenClient.getShortfallTotalRequirement(chainId, l2Token);
const currentBalance = this.getBalanceOnChainForL1Token(chainId, l1Token).sub(shortfall);

// Multiply by scalar to avoid rounding errors.
return currentBalance.mul(this.scalar).div(cumulativeBalance);
}

// Find how short a given chain is for a desired L1Token.
getTokenShortFall(l1Token: string, chainId: number): BigNumber {
return this.tokenClient.getShortfallTotalRequirement(chainId, this.getDestinationTokenForL1Token(l1Token, chainId));
return this.getDestinationTokensForL1Token(l1Token, chainId)
.map((token) => this.tokenClient.getShortfallTotalRequirement(chainId, token))
.reduce((acc, curr) => acc.add(curr), bnZero);
}
pxrl marked this conversation as resolved.
Show resolved Hide resolved

getDestinationTokenForL1Token(l1Token: string, chainId: number | string): string {
getRepaymentTokenForL1Token(l1Token: string, chainId: number | string): string {
return this.hubPoolClient.getL2TokenForL1TokenAtBlock(l1Token, Number(chainId));
}

getDestinationTokensForL1Token(l1Token: string, chainId: number | string): string[] {
pxrl marked this conversation as resolved.
Show resolved Hide resolved
const tokenConfig = this.inventoryConfig.tokenConfig[l1Token];

if (isAliasConfig(tokenConfig)) {
return Object.keys(tokenConfig).filter((k) => isDefined(tokenConfig[k][chainId]));
}

return [this.getRepaymentTokenForL1Token(l1Token, chainId)];
}

getEnabledChains(): number[] {
return this.chainIdList;
}
Expand Down Expand Up @@ -209,7 +259,7 @@ export class InventoryClient {
// Increase virtual balance by pending relayer refunds from the latest valid bundle and the
// upcoming bundle. We can assume that all refunds from the second latest valid bundle have already
// been executed.
let startTimer;
let startTimer: number;
if (!isDefined(this.bundleRefundsPromise)) {
startTimer = performance.now();
// @dev Save this as a promise so that other parallel calls to this function don't make the same call.
Expand All @@ -219,9 +269,9 @@ export class InventoryClient {
const totalRefundsPerChain = this.getEnabledChains().reduce(
(refunds: { [chainId: string]: BigNumber }, chainId) => {
if (!this.hubPoolClient.l2TokenEnabledForL1Token(l1Token, chainId)) {
refunds[chainId] = toBN(0);
refunds[chainId] = bnZero;
} else {
const destinationToken = this.getDestinationTokenForL1Token(l1Token, chainId);
const destinationToken = this.getRepaymentTokenForL1Token(l1Token, chainId);
refunds[chainId] = this.bundleDataClient.getTotalRefund(
refundsToConsider,
this.relayer,
Expand Down Expand Up @@ -329,8 +379,8 @@ export class InventoryClient {
` (${inputToken} != ${outputToken})`
);
}

l1Token ??= this.hubPoolClient.getL1TokenForL2TokenAtBlock(inputToken, originChainId);
const tokenConfig = this.inventoryConfig?.tokenConfig?.[l1Token];

// Consider any refunds from executed and to-be executed bundles. If bundle data client doesn't return in
// time, return an object with zero refunds for all chains.
Expand Down Expand Up @@ -386,8 +436,8 @@ export class InventoryClient {
for (const _chain of chainsToEvaluate) {
assert(this._l1TokenEnabledForChain(l1Token, _chain), `Token ${l1Token} not enabled for chain ${_chain}`);
// Destination chain:
const chainShortfall = this.getTokenShortFall(l1Token, _chain);
const chainVirtualBalance = this.getBalanceOnChainForL1Token(_chain, l1Token);
const chainShortfall = this.tokenClient.getShortfallTotalRequirement(_chain, outputToken);
const chainVirtualBalance = this.getBalanceOnChain(_chain, l1Token, outputToken);
const chainVirtualBalanceWithShortfall = chainVirtualBalance.sub(chainShortfall);
let cumulativeVirtualBalanceWithShortfall = cumulativeVirtualBalance.sub(chainShortfall);
// @dev No need to factor in outputAmount when computing origin chain balance since funds only leave relayer
Expand Down Expand Up @@ -416,8 +466,10 @@ export class InventoryClient {
.div(cumulativeVirtualBalanceWithShortfallPostRelay);

// Consider configured buffer for target to allow relayer to support slight overages.
const thresholdPct = toBN(this.inventoryConfig.tokenConfig[l1Token][_chain].targetPct)
.mul(tokenConfig[_chain].targetOverageBuffer ?? toBNWei("1"))
const tokenConfig = this.getTokenConfig(l1Token, destinationChainId, outputToken);
assert(isDefined(tokenConfig), `No tokenConfig for ${l1Token} on ${_chain}.`);
const thresholdPct = toBN(tokenConfig.targetPct)
.mul(tokenConfig.targetOverageBuffer ?? toBNWei("1"))
.div(fixedPointAdjustment);
this.log(
`Evaluated taking repayment on ${
Expand Down Expand Up @@ -487,12 +539,13 @@ export class InventoryClient {
} else {
runningBalanceForToken = leaf.runningBalances[l1TokenIndex];
}

// Approximate latest running balance as last known proposed running balance...
// - minus total deposit amount on chain since the latest end block proposed
// - plus total refund amount on chain since the latest end block proposed
const upcomingDeposits = this.bundleDataClient.getUpcomingDepositAmount(
chainId,
this.getDestinationTokenForL1Token(l1Token, chainId),
this.getRepaymentTokenForL1Token(l1Token, chainId),
blockRange[1]
);
// Grab refunds that are not included in any bundle proposed on-chain. These are refunds that have not
Expand All @@ -502,7 +555,7 @@ export class InventoryClient {
// If a chain didn't exist in the last bundle or a spoke pool client isn't defined, then
// one of the refund entries for a chain can be undefined.
const upcomingRefundForChain = Object.values(
upcomingRefunds?.[chainId]?.[this.getDestinationTokenForL1Token(l1Token, chainId)] ?? {}
upcomingRefunds?.[chainId]?.[this.getRepaymentTokenForL1Token(l1Token, chainId)] ?? {}
).reduce((acc, curr) => acc.add(curr), bnZero);

// Updated running balance is last known running balance minus deposits plus upcoming refunds.
Expand Down Expand Up @@ -607,29 +660,34 @@ export class InventoryClient {
}

getPossibleRebalances(): Rebalance[] {
const chainIds = this.getEnabledChains();
const rebalancesRequired: Rebalance[] = [];

// First, compute the rebalances that we would do assuming we have sufficient tokens on L1.
for (const l1Token of this.getL1Tokens()) {
const cumulativeBalance = this.getCumulativeBalance(l1Token);
if (cumulativeBalance.eq(bnZero)) {
continue;
return;
}

for (const chainId of this.getEnabledL2Chains()) {
// Skip if there's no configuration for l1Token on chainId. This is the case for BOBA and BADGER
// as they're not present on all L2s.
chainIds.forEach((chainId) => {
// Skip if there's no configuration for l1Token on chainId.
if (!this._l1TokenEnabledForChain(l1Token, chainId)) {
continue;
return;
}

const currentAllocPct = this.getCurrentAllocationPct(l1Token, chainId);
const { thresholdPct, targetPct } = this.inventoryConfig.tokenConfig[l1Token][chainId];
if (currentAllocPct.lt(thresholdPct)) {
const l2Tokens = this.getDestinationTokensForL1Token(l1Token, chainId);
l2Tokens.forEach((l2Token) => {
const currentAllocPct = this.getCurrentAllocationPct(l1Token, chainId, l2Token);
const tokenConfig = this.getTokenConfig(l1Token, chainId, l2Token);
const { thresholdPct, targetPct } = tokenConfig;

if (currentAllocPct.gte(thresholdPct)) {
return;
}

const deltaPct = targetPct.sub(currentAllocPct);
const amount = deltaPct.mul(cumulativeBalance).div(this.scalar);
const balance = this.tokenClient.getBalance(this.hubPoolClient.chainId, l1Token);
const l2Token = this.getDestinationTokenForL1Token(l1Token, chainId);
rebalancesRequired.push({
chainId,
l1Token,
Expand All @@ -641,9 +699,10 @@ export class InventoryClient {
cumulativeBalance,
amount,
});
}
}
});
});
}

return rebalancesRequired;
}

Expand All @@ -670,7 +729,6 @@ export class InventoryClient {
}

// Next, evaluate if we have enough tokens on L1 to actually do these rebalances.

for (const rebalance of rebalancesRequired) {
const { balance, amount, l1Token, chainId } = rebalance;

Expand Down Expand Up @@ -797,6 +855,10 @@ export class InventoryClient {
}

async unwrapWeth(): Promise<void> {
if (!this.isInventoryManagementEnabled()) {
return;
}

// Note: these types are just used inside this method, so they are declared in-line.
type ChainInfo = {
chainId: number;
Expand All @@ -813,16 +875,14 @@ export class InventoryClient {
const executedTransactions: ExecutedUnwrap[] = [];

try {
if (!this.isInventoryManagementEnabled()) {
return;
}
const l1Weth = TOKEN_SYMBOLS_MAP.WETH.addresses[this.hubPoolClient.chainId];
const chains = await Promise.all(
this.getEnabledChains()
.map((chainId) => {
const unwrapWethThreshold =
this.inventoryConfig.tokenConfig?.[l1Weth]?.[chainId.toString()]?.unwrapWethThreshold;
const unwrapWethTarget = this.inventoryConfig.tokenConfig?.[l1Weth]?.[chainId.toString()]?.unwrapWethTarget;
const tokenConfig = this.getTokenConfig(l1Weth, chainId);
assert(isDefined(tokenConfig));

const { unwrapWethThreshold, unwrapWethTarget } = tokenConfig;

// Ignore chains where ETH isn't the native gas token. Returning null will result in these being filtered.
if (chainId === CHAIN_IDs.POLYGON || unwrapWethThreshold === undefined || unwrapWethTarget === undefined) {
Expand Down Expand Up @@ -1029,7 +1089,18 @@ export class InventoryClient {
}

_l1TokenEnabledForChain(l1Token: string, chainId: number): boolean {
return this.inventoryConfig.tokenConfig?.[l1Token]?.[String(chainId)] !== undefined;
const tokenConfig = this.inventoryConfig?.tokenConfig?.[l1Token];
if (!isDefined(tokenConfig)) {
return false;
}

// If tokenConfig directly references chainId, token is enabled.
if (!isAliasConfig(tokenConfig) && isDefined(tokenConfig[chainId])) {
return true;
}

// If any of the mapped symbols reference chainId, token is enabled.
return Object.keys(tokenConfig).some((symbol) => isDefined(tokenConfig[symbol][chainId]));
}

/**
Expand Down
43 changes: 28 additions & 15 deletions src/interfaces/InventoryManagement.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { BigNumber } from "ethers";
import { BigNumber, utils as ethersUtils } from "ethers";
import { TOKEN_SYMBOLS_MAP } from "../utils";

export type TokenInventoryConfig = {
targetOverageBuffer: BigNumber; // Max multiplier for targetPct, to give flexibility in repayment chain selection.
targetPct: BigNumber; // The desired amount of the given token on the L2 chainId.
thresholdPct: BigNumber; // Threshold, below which, we will execute a rebalance.
unwrapWethThreshold?: BigNumber; // Threshold for ETH to trigger WETH unwrapping to maintain ETH balance.
unwrapWethTarget?: BigNumber; // Amount of WETH to unwrap to refill ETH. Unused if unwrapWethThreshold is undefined.
};

export type ChainTokenConfig = {
[chainId: string]: TokenInventoryConfig;
};

// AliasConfig permits a single HubPool token to map onto multiple tokens on a remote chain.
export type ChainTokenInventory = {
[symbol: string]: ChainTokenConfig;
};

export interface InventoryConfig {
tokenConfig: {
[l1Token: string]: {
[chainId: string]: {
targetOverageBuffer: BigNumber; // The relayer will be allowed to hold this multiple times the targetPct
// of the full token balance on this chain.
targetPct: BigNumber; // The desired amount of the given token on the L2 chainId.
thresholdPct: BigNumber; // Threshold, below which, we will execute a rebalance.
unwrapWethThreshold?: BigNumber; // Threshold for ETH on this chain to trigger WETH unwrapping to maintain
// ETH balance
unwrapWethTarget?: BigNumber; // Amount of WETH to unwrap to refill ETH balance. Unused if unwrapWethThreshold
// is undefined.
};
};
};
// tokenConfig can map to a single token allocation, or a set of allocations that all map to the same HubPool token.
tokenConfig: { [l1Token: string]: ChainTokenConfig } | { [l1Token: string]: ChainTokenInventory };

nicholaspai marked this conversation as resolved.
Show resolved Hide resolved
// If ETH balance on chain is above threshold, wrap the excess over the target to WETH.
wrapEtherTargetPerChain: {
[chainId: number]: BigNumber;
Expand All @@ -25,3 +32,9 @@ export interface InventoryConfig {
};
wrapEtherThreshold: BigNumber;
}

export function isAliasConfig(config: ChainTokenConfig | ChainTokenInventory): config is ChainTokenInventory {
return (
pxrl marked this conversation as resolved.
Show resolved Hide resolved
Object.keys(config).every((k) => ethersUtils.isAddress(k)) || Object.keys(config).every((k) => TOKEN_SYMBOLS_MAP[k])
);
}
Loading
Loading