-
Notifications
You must be signed in to change notification settings - Fork 156
Open
Description
The current architecture is tightly coupled to the ChainId dependency and its a poor architecture.
Fix Dependency Injection for the sdks
- SDK-Core Addresses and mappings are a mess ()
- There's code duplication in other sdks and no single source of truth (WETH addresses, and Quoter addresses are overridden and duplicated in smart-order-router, router-sdk, universal-router sdk)
- Adding a new chain requires redeployment and synchronization of multiple sdks.
- V2-SDK does not allow InitHash injection
At this point you're pretty tied into this poor architecture however you can do the following.
A: Inject all dependencies into all dependent classes, this a big headache since the pool and route dependencies will need to take in the factory and init hashes.
This would be an extreme refactor and cause a lot of existing third party code to break as a cause of the update
B: Static configuration Injection (Overall is a hack as the SDK should not be statically dependent however currently you don't support configurations per chain so it shouldn't be an issue.)
1. Add a class to statically initialize the chain configuration but fallback to the existing chains so people aren't forced to call the chain initializer
// ChainAddressConfig.ts
import { ChainAddresses } from "./addresses";
export type ChainAddressesMap = Record<number, ChainAddresses>;
const GLOBAL_CHAIN_ADDRESSES_KEY = '__GLOBAL_CHAIN_ADDRESSES_CONFIG__';
export class ChainAddressConfig {
private addressesMap: ChainAddressesMap = {};
private constructor() { }
private static get instance(): ChainAddressConfig {
if (!(globalThis as any)[GLOBAL_CHAIN_ADDRESSES_KEY]) {
(globalThis as any)[GLOBAL_CHAIN_ADDRESSES_KEY] = new ChainAddressConfig();
}
return (globalThis as any)[GLOBAL_CHAIN_ADDRESSES_KEY];
}
public static initialize(addressesMap: ChainAddressesMap, allowMerge = false): void {
if (!allowMerge && Object.keys(this.instance.addressesMap).length > 0) {
throw new Error('ChainAddressConfig already initialized. To merge, set allowMerge=true.');
}
this.instance.addressesMap = allowMerge
? { ...this.instance.addressesMap, ...addressesMap }
: addressesMap;
}
public static getAddresses(chainId: number): ChainAddresses | undefined {
return this.instance.addressesMap[chainId];
}
public static getAddress(
chainId: number,
key: Exclude<keyof ChainAddresses, 'weth'>
): string | undefined {
const addresses = this.getAddresses(chainId);
if (!addresses) {
return undefined;
}
return addresses[key];
}
public static getChainIds(): number[] {
return Object.keys(this.instance.addressesMap).map(Number);
}
}
- Create a proxy to override the existing chain maps to dynamically support the static configuration: (Note: I created a fallback proxy provider which can be used in the smart-order router since you again decided to override some properties and dynamically create address maps e.g quoterV2Addresses)
export function createAddressProxy(propertyName: keyof ChainAddresses): AddressMap {
return new Proxy({}, {
get(_, chainId: string) {
const numericChainId = Number(chainId);
return CHAIN_TO_ADDRESSES_MAP[numericChainId]?.[propertyName];
},
ownKeys() {
return Object.keys(CHAIN_TO_ADDRESSES_MAP).filter((chainId) => {
return CHAIN_TO_ADDRESSES_MAP[Number(chainId)]?.[propertyName];
});
},
getOwnPropertyDescriptor(_, chainId: string) {
const numericChainId = Number(chainId);
if (CHAIN_TO_ADDRESSES_MAP[numericChainId]?.[propertyName]) {
return { enumerable: true, configurable: true };
}
return undefined;
},
has(_, chainId: string) {
const numericChainId = Number(chainId);
return CHAIN_TO_ADDRESSES_MAP[numericChainId]?.[propertyName] !== undefined;
},
});
}
export function createDynamicAddressMapProxyWithFallback(
property: string,
fallbackProperty: keyof ChainAddresses,
propertyOverrides: Record<number, string>
): AddressMap {
return new Proxy({}, {
get(_, chainId: string) {
const numericChainId = Number(chainId);
const chainConfig = CHAIN_TO_ADDRESSES_MAP[numericChainId];
return (
(chainConfig as any)?.[property] ??
propertyOverrides[numericChainId] ??
chainConfig?.[fallbackProperty]
);
},
has(_, chainId: string) {
const numericChainId = Number(chainId);
const chainConfig = CHAIN_TO_ADDRESSES_MAP[numericChainId];
return (
(chainConfig as any)?.[property] !== undefined ||
propertyOverrides[numericChainId] !== undefined ||
chainConfig?.[fallbackProperty] !== undefined
);
},
ownKeys() {
const chainIds = new Set<number>();
// Include any chain with a matching primary or fallback property
for (const [idStr, config] of Object.entries(CHAIN_TO_ADDRESSES_MAP)) {
const id = Number(idStr);
if ((config as any)?.[property] || config?.[fallbackProperty]) {
chainIds.add(id);
}
}
// Include overrides if the property/fallback is not already present
for (const idStr of Object.keys(propertyOverrides)) {
const id = Number(idStr);
const config = CHAIN_TO_ADDRESSES_MAP[id];
if (!(config as any)?.[property] && !config?.[fallbackProperty]) {
chainIds.add(id);
}
}
return Array.from(chainIds).map(String);
},
getOwnPropertyDescriptor(_, chainId: string) {
const numericChainId = Number(chainId);
const config = CHAIN_TO_ADDRESSES_MAP[numericChainId];
const value = (
(config as any)?.[property] ??
propertyOverrides[numericChainId] ??
config?.[fallbackProperty]
);
if (value !== undefined) {
return { enumerable: true, configurable: true };
}
return undefined;
}
});
}
- Depricate old chain map and reference static chain configuration and build properties for the addtional address map
export const CHAIN_TO_ADDRESSES_MAP: Record<number, ChainAddresses> = new Proxy(
{} as Record<number, ChainAddresses>,
{
get(_, chainId: string) {
const numericChainId = Number(chainId);
const configuredAddresses = ChainAddressConfig.getAddresses(numericChainId);
if (configuredAddresses) return configuredAddresses;
return DEPRICATED_CHAIN_TO_ADDRESSES_MAP[numericChainId];
},
ownKeys() {
const dynamicKeys = ChainAddressConfig.getChainIds();
const deprecatedKeys = Object.keys(DEPRICATED_CHAIN_TO_ADDRESSES_MAP).map(Number);
return Array.from(new Set([...dynamicKeys, ...deprecatedKeys])).map(String);
},
getOwnPropertyDescriptor(_, chainId: string) {
const numericChainId = Number(chainId);
const exists =
ChainAddressConfig.getAddresses(numericChainId) !== undefined ||
DEPRICATED_CHAIN_TO_ADDRESSES_MAP[numericChainId] !== undefined;
if (exists) {
return { enumerable: true, configurable: true };
}
return undefined;
},
has(_, chainId: string) {
const numericChainId = Number(chainId);
return (
ChainAddressConfig.getAddresses(numericChainId) !== undefined ||
DEPRICATED_CHAIN_TO_ADDRESSES_MAP[numericChainId] !== undefined
);
},
}
);
export const V3_CORE_FACTORY_ADDRESSES = createAddressProxy('v3CoreFactoryAddress');
Metadata
Metadata
Assignees
Labels
No labels