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 all 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
257 changes: 174 additions & 83 deletions src/clients/InventoryClient.ts

Large diffs are not rendered by default.

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

export type TokenBalanceConfig = {
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]: TokenBalanceConfig;
};

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

/**
* Example configuration:
* - DAI on chains 10 & 42161.
* - Bridged USDC (USDC.e, USDbC) on chains 10, 137, 324, 8453, 42161 & 59144.
* - Native USDC on Polygon.
*
* All token allocations are "global", so Polygon will be allocated a total of 8% of all USDC:
* - 4% of global USDC as Native USDC, and
* - 4% as Bridged USDC.
*
* "tokenConfig": {
* "DAI": {
* "10": { "targetPct": 8, "thresholdPct": 4 },
* "42161": { "targetPct": 8, "thresholdPct": 4 },
* },
* "USDC": {
* "USDC.e": {
* "10": { "targetPct": 8, "thresholdPct": 4 },
* "137": { "targetPct": 4, "thresholdPct": 2 },
* "324": { "targetPct": 8, "thresholdPct": 4 },
* "42161": { "targetPct": 8, "thresholdPct": 4 },
* "59144": { "targetPct": 5, "thresholdPct": 2 }
* },
* "USDbC": {
* "8453": { "targetPct": 5, "thresholdPct": 2 }
* },
* "USDC": {
* "137": { "targetPct": 4, "thresholdPct": 2 }
* }
* }
* }
*/
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 +64,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])
);
}
90 changes: 58 additions & 32 deletions src/relayer/RelayerConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "../utils";
import { CommonConfig, ProcessEnv } from "../common";
import * as Constants from "../common/Constants";
import { InventoryConfig } from "../interfaces";
import { InventoryConfig, TokenBalanceConfig, isAliasConfig } from "../interfaces/InventoryManagement";

type DepositConfirmationConfig = {
usdThreshold: BigNumber;
Expand Down Expand Up @@ -153,47 +153,73 @@ export class RelayerConfig extends CommonConfig {
}
});

const parseTokenConfig = (
l1Token: string,
chainId: string,
rawTokenConfig: TokenBalanceConfig
): TokenBalanceConfig => {
const { targetPct, thresholdPct, unwrapWethThreshold, unwrapWethTarget, targetOverageBuffer } = rawTokenConfig;
const tokenConfig: TokenBalanceConfig = { targetPct, thresholdPct, targetOverageBuffer };

assert(
targetPct !== undefined && thresholdPct !== undefined,
`Bad config. Must specify targetPct, thresholdPct for ${l1Token} on ${chainId}`
);
assert(
toBN(thresholdPct).lte(toBN(targetPct)),
`Bad config. thresholdPct<=targetPct for ${l1Token} on ${chainId}`
);
tokenConfig.targetPct = toBNWei(targetPct).div(100);
tokenConfig.thresholdPct = toBNWei(thresholdPct).div(100);

// Default to 150% the targetPct. targetOverageBuffer does not have to be defined so that no existing configs
// are broken. This is a reasonable default because it allows the relayer to be a bit more flexible in
// holding more tokens than the targetPct, but perhaps a better default is 100%
tokenConfig.targetOverageBuffer = toBNWei(targetOverageBuffer ?? "1.5");

// For WETH, also consider any unwrap target/threshold.
if (l1Token === TOKEN_SYMBOLS_MAP.WETH.addresses[this.hubPoolChainId]) {
if (unwrapWethThreshold !== undefined) {
tokenConfig.unwrapWethThreshold = toBNWei(unwrapWethThreshold);
}
tokenConfig.unwrapWethTarget = toBNWei(unwrapWethTarget ?? 2);
}

return tokenConfig;
};

const rawTokenConfigs = inventoryConfig?.tokenConfig ?? {};
const tokenConfigs = (inventoryConfig.tokenConfig = {});
Object.keys(rawTokenConfigs).forEach((l1Token) => {
// If the l1Token is a symbol, resolve the correct address.
const effectiveL1Token = ethersUtils.isAddress(l1Token)
? l1Token
: TOKEN_SYMBOLS_MAP[l1Token]?.addresses[this.hubPoolChainId];
: TOKEN_SYMBOLS_MAP[l1Token].addresses[this.hubPoolChainId];
assert(effectiveL1Token !== undefined, `No token identified for ${l1Token}`);

Object.keys(rawTokenConfigs[l1Token]).forEach((chainId) => {
const { targetPct, thresholdPct, unwrapWethThreshold, unwrapWethTarget, targetOverageBuffer } =
rawTokenConfigs[l1Token][chainId];
tokenConfigs[effectiveL1Token] ??= {};
const hubTokenConfig = rawTokenConfigs[l1Token];

tokenConfigs[effectiveL1Token] ??= {};
tokenConfigs[effectiveL1Token][chainId] ??= { targetPct, thresholdPct, targetOverageBuffer };
const tokenConfig = tokenConfigs[effectiveL1Token][chainId];
if (isAliasConfig(hubTokenConfig)) {
Object.keys(hubTokenConfig).forEach((symbol) => {
Object.keys(hubTokenConfig[symbol]).forEach((chainId) => {
const rawTokenConfig = hubTokenConfig[symbol][chainId];
const effectiveSpokeToken = TOKEN_SYMBOLS_MAP[symbol].addresses[chainId];

assert(
targetPct !== undefined && thresholdPct !== undefined,
`Bad config. Must specify targetPct, thresholdPct for ${l1Token} on ${chainId}`
);
assert(
toBN(thresholdPct).lte(toBN(targetPct)),
`Bad config. thresholdPct<=targetPct for ${l1Token} on ${chainId}`
);
tokenConfig.targetPct = toBNWei(targetPct).div(100);
tokenConfig.thresholdPct = toBNWei(thresholdPct).div(100);

// Default to 150% the targetPct. targetOverageBuffer does not have to be defined so that no existing configs
// are broken. This is a reasonable default because it allows the relayer to be a bit more flexible in
// holding more tokens than the targetPct, but perhaps a better default is 100%
tokenConfig.targetOverageBuffer = toBNWei(targetOverageBuffer ?? "1.5");

// For WETH, also consider any unwrap target/threshold.
if (effectiveL1Token === TOKEN_SYMBOLS_MAP.WETH.addresses[this.hubPoolChainId]) {
if (unwrapWethThreshold !== undefined) {
tokenConfig.unwrapWethThreshold = toBNWei(unwrapWethThreshold);
}
tokenConfig.unwrapWethTarget = toBNWei(unwrapWethTarget ?? 2);
}
});
tokenConfigs[effectiveL1Token][effectiveSpokeToken] ??= {};
tokenConfigs[effectiveL1Token][effectiveSpokeToken][chainId] = parseTokenConfig(
l1Token,
chainId,
rawTokenConfig
);
});
});
} else {
Object.keys(hubTokenConfig).forEach((chainId) => {
const rawTokenConfig = hubTokenConfig[chainId];
tokenConfigs[effectiveL1Token][chainId] = parseTokenConfig(l1Token, chainId, rawTokenConfig);
});
}
});
}

Expand Down
Loading
Loading