Skip to content

Commit 3f44ac7

Browse files
committed
refactor: streamline downloadable config transformation and improve code consistency
1 parent 93a9a6d commit 3f44ac7

File tree

2 files changed

+114
-125
lines changed

2 files changed

+114
-125
lines changed

governance/xc_admin/packages/xc_admin_common/src/programs/core/core_functions.ts

Lines changed: 91 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -91,44 +91,40 @@ const sortObjectByKeys = <T extends Record<string, unknown>>(
9191
): Array<[string, unknown]> =>
9292
Object.entries(obj).sort(([a], [b]) => a.localeCompare(b));
9393

94+
/**
95+
* Helper function to transform object values
96+
*/
97+
const mapValues = <T, U>(
98+
obj: Record<string, T>,
99+
fn: (value: T) => U,
100+
): Record<string, U> =>
101+
Object.fromEntries(
102+
Object.entries(obj).map(([key, value]) => [key, fn(value)]),
103+
);
104+
94105
/**
95106
* Sort configuration data for consistent output
96107
*/
97108
function sortData(data: DownloadableConfig): DownloadableConfig {
98-
const sortedData: DownloadableConfig = {};
99-
const keys = Object.keys(data).sort();
100-
for (const key of keys) {
101-
const productData = data[key];
102-
const sortedKeyValues = sortObjectByKeys(productData.metadata);
103-
104-
const sortedInnerData: DownloadableProduct = {
105-
address: productData.address,
106-
metadata: Object.fromEntries(sortedKeyValues) as Omit<
107-
Product,
108-
"price_account"
109-
>,
110-
priceAccounts: [],
111-
};
112-
113-
// Sort price accounts by address
114-
sortedInnerData.priceAccounts = [...productData.priceAccounts]
115-
.sort((a, b) => a.address.localeCompare(b.address))
116-
.map((priceAccount) => {
117-
const sortedPriceAccount: DownloadablePriceAccount = {
118-
address: priceAccount.address,
119-
expo: priceAccount.expo,
120-
minPub: priceAccount.minPub,
121-
maxLatency: priceAccount.maxLatency,
122-
publishers: [...priceAccount.publishers].sort((a, b) =>
123-
a.localeCompare(b),
124-
),
125-
};
126-
return sortedPriceAccount;
127-
});
128-
129-
sortedData[key] = sortedInnerData;
130-
}
131-
return sortedData;
109+
return mapValues(data, (productData: DownloadableProduct) => ({
110+
address: productData.address,
111+
metadata: Object.fromEntries(
112+
sortObjectByKeys(productData.metadata),
113+
) as Omit<Product, "price_account">,
114+
priceAccounts: [...productData.priceAccounts]
115+
.sort((a: DownloadablePriceAccount, b: DownloadablePriceAccount) =>
116+
a.address.localeCompare(b.address),
117+
)
118+
.map((priceAccount: DownloadablePriceAccount) => ({
119+
address: priceAccount.address,
120+
expo: priceAccount.expo,
121+
minPub: priceAccount.minPub,
122+
maxLatency: priceAccount.maxLatency,
123+
publishers: [...priceAccount.publishers].sort((a: string, b: string) =>
124+
a.localeCompare(b),
125+
),
126+
})),
127+
}));
132128
}
133129

134130
/**
@@ -149,73 +145,72 @@ export function getConfig(
149145
);
150146

151147
// First pass: Extract price accounts
152-
const priceRawConfigs = Object.fromEntries(
153-
accounts
154-
.filter(
155-
(account) =>
156-
parsedBaseDataMap.get(account.pubkey.toBase58())?.type ===
157-
AccountType.Price,
158-
)
159-
.map((account) => {
160-
const parsed = parsePriceData(account.account.data);
161-
return [
162-
account.pubkey.toBase58(),
163-
{
164-
next: parsed.nextPriceAccountKey,
165-
address: account.pubkey,
166-
publishers: parsed.priceComponents
167-
.filter((x) => x.publisher !== null && x.publisher !== undefined)
168-
.map((x) => x.publisher),
169-
expo: parsed.exponent,
170-
minPub: parsed.minPublishers,
171-
maxLatency: parsed.maxLatency,
172-
},
173-
];
174-
}),
148+
const priceAccounts = accounts.filter(
149+
(account) =>
150+
parsedBaseDataMap.get(account.pubkey.toBase58())?.type ===
151+
AccountType.Price,
152+
);
153+
154+
const priceRawConfigs = mapValues(
155+
Object.fromEntries(
156+
priceAccounts.map((account) => [account.pubkey.toBase58(), account]),
157+
),
158+
(account) => {
159+
const parsed = parsePriceData(account.account.data);
160+
return {
161+
next: parsed.nextPriceAccountKey,
162+
address: account.pubkey,
163+
publishers: parsed.priceComponents
164+
.filter((x) => x.publisher !== null && x.publisher !== undefined)
165+
.map((x) => x.publisher),
166+
expo: parsed.exponent,
167+
minPub: parsed.minPublishers,
168+
maxLatency: parsed.maxLatency,
169+
};
170+
},
175171
);
176172

177173
// Second pass: Extract product accounts and link to price accounts
178-
const productRawConfigs = Object.fromEntries(
179-
accounts
180-
.filter(
181-
(account) =>
182-
parsedBaseDataMap.get(account.pubkey.toBase58())?.type ===
183-
AccountType.Product,
184-
)
185-
.map((account) => {
186-
const parsed = parseProductData(account.account.data);
187-
const priceAccounts: PriceRawConfig[] = [];
188-
189-
// Follow the linked list of price accounts
190-
if (parsed.priceAccountKey) {
191-
let priceAccountKey: string | undefined =
192-
parsed.priceAccountKey.toBase58();
193-
const processedPriceKeys = new Set<string>();
194-
195-
while (
196-
priceAccountKey &&
197-
!processedPriceKeys.has(priceAccountKey) &&
198-
priceRawConfigs[priceAccountKey]
199-
) {
200-
processedPriceKeys.add(priceAccountKey);
201-
const priceConfig: PriceRawConfig =
202-
priceRawConfigs[priceAccountKey];
203-
priceAccounts.push(priceConfig);
204-
priceAccountKey = priceConfig.next
205-
? priceConfig.next.toBase58()
206-
: undefined;
207-
}
174+
const productAccounts = accounts.filter(
175+
(account) =>
176+
parsedBaseDataMap.get(account.pubkey.toBase58())?.type ===
177+
AccountType.Product,
178+
);
179+
180+
const productRawConfigs = mapValues(
181+
Object.fromEntries(
182+
productAccounts.map((account) => [account.pubkey.toBase58(), account]),
183+
),
184+
(account) => {
185+
const parsed = parseProductData(account.account.data);
186+
const priceAccounts: PriceRawConfig[] = [];
187+
188+
// Follow the linked list of price accounts
189+
if (parsed.priceAccountKey) {
190+
let priceAccountKey: string | undefined =
191+
parsed.priceAccountKey.toBase58();
192+
const processedPriceKeys = new Set<string>();
193+
194+
while (
195+
priceAccountKey &&
196+
!processedPriceKeys.has(priceAccountKey) &&
197+
priceRawConfigs[priceAccountKey]
198+
) {
199+
processedPriceKeys.add(priceAccountKey);
200+
const priceConfig: PriceRawConfig = priceRawConfigs[priceAccountKey];
201+
priceAccounts.push(priceConfig);
202+
priceAccountKey = priceConfig.next
203+
? priceConfig.next.toBase58()
204+
: undefined;
208205
}
206+
}
209207

210-
return [
211-
account.pubkey.toBase58(),
212-
{
213-
priceAccounts,
214-
metadata: parsed.product,
215-
address: account.pubkey,
216-
},
217-
];
218-
}),
208+
return {
209+
priceAccounts,
210+
metadata: parsed.product,
211+
address: account.pubkey,
212+
};
213+
},
219214
);
220215

221216
// Third pass: Extract mapping accounts and permission data

governance/xc_admin/packages/xc_admin_common/src/programs/lazer/lazer_functions.ts

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -118,36 +118,30 @@ export function getConfig(
118118
* @returns Configuration formatted for download
119119
*/
120120
export function getDownloadableConfig(config: LazerConfig): DownloadableConfig {
121-
// Transform LazerConfig to DownloadableConfig
122-
const downloadableConfig: DownloadableConfig = {};
123-
124-
// Convert each feed to a format compatible with DownloadableProduct
125-
config.feeds.forEach((feed) => {
126-
downloadableConfig[feed.id] = {
127-
address: "", // We'll need to determine how to represent this for Lazer
128-
metadata: {
129-
symbol: feed.id,
130-
// Convert feed metadata to match expected Product metadata format
131-
// This is a placeholder and will need to be adjusted based on actual metadata
132-
asset_type: feed.metadata.asset_type?.toString() ?? "",
133-
country: feed.metadata.country?.toString() ?? "",
134-
quote_currency: feed.metadata.quote_currency?.toString() ?? "",
135-
tenor: feed.metadata.tenor?.toString() ?? "",
136-
// Add other required fields
137-
},
138-
priceAccounts: [
139-
{
140-
address: "", // Will need to be determined based on Lazer's structure
141-
publishers: [], // Will need to be populated from Lazer data
142-
expo: 0, // Default value, update based on actual data
143-
minPub: 0, // Default value, update based on actual data
144-
maxLatency: 0, // Default value, update based on actual data
121+
return Object.fromEntries(
122+
config.feeds.map((feed) => [
123+
feed.id,
124+
{
125+
address: "",
126+
metadata: {
127+
symbol: feed.id,
128+
asset_type: feed.metadata.asset_type?.toString() ?? "",
129+
country: feed.metadata.country?.toString() ?? "",
130+
quote_currency: feed.metadata.quote_currency?.toString() ?? "",
131+
tenor: feed.metadata.tenor?.toString() ?? "",
145132
},
146-
],
147-
};
148-
});
149-
150-
return downloadableConfig;
133+
priceAccounts: [
134+
{
135+
address: "",
136+
publishers: [],
137+
expo: 0,
138+
minPub: 0,
139+
maxLatency: 0,
140+
},
141+
],
142+
},
143+
]),
144+
);
151145
}
152146

153147
/**

0 commit comments

Comments
 (0)