Skip to content

Commit 15daae2

Browse files
committed
refactor: manifest object
feat: add "contractNames" in release type feat: export global contract names perf: simplify print scripts refactor: generate version groups dynamically refactor: rename release exports refactor: flatten query getters refactor: remove "isValidVersion" refactor: throw error using winston logger
1 parent 1bb2094 commit 15daae2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1340
-1216
lines changed

biome.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"enabled": true,
2121
"rules": {
2222
"correctness": {
23-
"noUnusedImports": "error",
23+
"noUnusedImports": "off",
2424
"noUnusedVariables": "error"
2525
},
2626
"recommended": true

scripts/check-broadcast.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import path from "node:path";
22
import type { Sablier } from "@src/types";
33
import { globby } from "globby";
44
import type { Options as GlobbyOptions } from "globby";
5-
import { log } from "./logger";
5+
import { logInfo } from "./logger";
66

77
const ROOT_DIR = path.join(__dirname, "..");
88

@@ -37,7 +37,7 @@ async function checkPath(
3737

3838
if (!pathExists) {
3939
const relativePath = path.relative(ROOT_DIR, pathToCheck);
40-
log("info", release, `No broadcasts for ${chain.name} at ${relativePath}`);
40+
logInfo({ msg: `No broadcasts for ${chain.name} at ${relativePath}`, release });
4141
return null;
4242
}
4343

scripts/logger.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,24 @@ const logger = winston.createLogger({
7171
transports,
7272
});
7373

74-
export function log(level: "info" | "error", release: Sablier.Release, message: string): void {
75-
logger[level](`${release.protocol}:${release.version}\t${message}`);
74+
export function logInfo(params: { msg: string; release?: Sablier.Release }): void {
75+
const { msg, release } = params;
76+
if (release) {
77+
logger.info(`${formatRelease(release)}\t${msg}`);
78+
} else {
79+
logger.info(msg);
80+
}
81+
}
82+
83+
export function logAndThrow(params: { msg: string; release?: Sablier.Release }): never {
84+
const { msg, release } = params;
85+
const errorMsg = release ? `${formatRelease(release)}\t${msg}` : msg;
86+
logger.error(errorMsg);
87+
throw new Error(errorMsg);
88+
}
89+
90+
function formatRelease(release: Sablier.Release): string {
91+
return `${release.protocol}:${release.version}`;
7692
}
7793

7894
export default logger;

scripts/print-aliases.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,41 @@
11
import { releases } from "@src/releases";
22
import _ from "lodash";
33
import logger from "./logger";
4-
54
interface AliasRow {
65
alias: string;
76
contractName: string;
8-
release: string;
7+
releaseName: string;
98
}
109

1110
async function main() {
1211
const rows: AliasRow[] = [];
1312

1413
for (const release of releases) {
15-
const aliases = release.aliases;
1614
const releaseName = `${release.protocol} ${release.version}`;
17-
18-
_.forOwn(aliases, (alias, key) => {
19-
let contractName: string;
20-
21-
// Lockup v1.x has core/periphery structure, others are flat
22-
if (release.kind === "lockupV1") {
23-
// Type cast the manifest to ManifestLockupV1
24-
const manifest = release.manifest;
25-
contractName = _.get(manifest.core, key) || _.get(manifest.periphery, key) || key;
26-
} else {
27-
// For the array-based manifest, the key should be the contract name itself
28-
contractName = key;
15+
if (!release.aliases) {
16+
logger.verbose(`Skipping ${releaseName} because it has no aliases`);
17+
continue;
18+
}
19+
20+
_.forOwn(release.aliases, (alias, contractName) => {
21+
// Exclude the MerkleFactory from being printed twice in the table
22+
if (release.protocol === "lockup" && alias.startsWith("MSF")) {
23+
return;
2924
}
30-
3125
rows.push({
3226
alias,
3327
contractName,
34-
release: releaseName,
28+
releaseName,
3529
});
3630
});
3731
}
3832

3933
if (rows.length === 0) {
40-
console.log("❌ No aliases found");
34+
logger.info("❌ No aliases found");
4135
return;
4236
}
4337

44-
console.log(`✅ Found ${rows.length} total aliases\n`);
38+
logger.info(`✅ Found ${rows.length} total aliases\n`);
4539

4640
rows.sort((a, b) => a.alias.localeCompare(b.alias));
4741

@@ -54,9 +48,9 @@ async function main() {
5448
console.log(sep);
5549

5650
for (const row of rows) {
57-
const vals = [row.alias, row.contractName, row.release];
58-
const line = vals.map((v, i) => v.padEnd(colWidths[i])).join(" | ");
59-
console.log(line);
51+
const cellValues = [row.alias, row.contractName, row.releaseName];
52+
const content = cellValues.map((v, i) => v.padEnd(colWidths[i])).join(" | ");
53+
console.log(content);
6054
}
6155
}
6256

@@ -65,5 +59,5 @@ main().catch((error) => {
6559
if (error.stack) {
6660
logger.error(error.stack);
6761
}
68-
process.exit(1);
62+
throw error;
6963
});

scripts/print-missing-broadcasts.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* for all chains, only the ones listed in the releases.
55
*
66
* Usage:
7-
* bun run scripts/missing-broadcasts.ts airdrops
8-
* bun run scripts/missing-broadcasts.ts flow
9-
* bun run scripts/missing-broadcasts.ts lockup
7+
* bun run scripts/print-missing-broadcasts.ts airdrops
8+
* bun run scripts/print-missing-broadcasts.ts flow
9+
* bun run scripts/print-missing-broadcasts.ts lockup
1010
*/
1111
import { getChain } from "@src/chains";
1212
import { releasesByProtocol } from "@src/releases";
@@ -15,7 +15,6 @@ import _ from "lodash";
1515
import { checkBroadcast, checkZKBroadcast } from "./check-broadcast";
1616
import logger from "./logger";
1717

18-
// Emojis for better visual output
1918
const EMOJIS = {
2019
check: "✅",
2120
cross: "❌",
@@ -26,25 +25,18 @@ const EMOJIS = {
2625
warning: "⚠️",
2726
} as const;
2827

29-
type BroadcastType = Sablier.Protocol;
30-
31-
function parseArgs(): BroadcastType {
32-
const protocol = process.argv[2] as BroadcastType;
33-
const validProtocols: BroadcastType[] = ["airdrops", "flow", "legacy", "lockup"];
28+
function parseArgs(): Sablier.Protocol {
29+
const protocol = process.argv[2] as Sablier.Protocol;
30+
const validProtocols: Sablier.Protocol[] = ["airdrops", "flow", "legacy", "lockup"];
3431
if (!protocol || !validProtocols.includes(protocol)) {
35-
logger.error("Error: Please provide one of these protocols: airdrops, flow, legacy, lockup");
36-
process.exit(1);
32+
const msg = `Error: Please provide one of these protocols: ${validProtocols.join(", ")}`;
33+
logger.error(msg);
34+
throw new Error(msg);
3735
}
3836

3937
return protocol;
4038
}
4139

42-
// Get broadcast type from command line
43-
const protocol = parseArgs();
44-
45-
/**
46-
* Prints a section header with a nice separator
47-
*/
4840
function printSectionHeader(text: string): void {
4941
const separator = "═".repeat(50);
5042
console.log(`\n${separator}`);
@@ -54,11 +46,11 @@ function printSectionHeader(text: string): void {
5446

5547
async function main(): Promise<void> {
5648
const missing: Record<string, Sablier.Chain[]> = {};
57-
const releasesToCheck = releasesByProtocol[protocol];
49+
const protocol = parseArgs();
5850

59-
console.log(`\n${EMOJIS.folder} Checking ${protocol} broadcasts...\n`);
51+
logger.info(`\n${EMOJIS.folder} Checking ${protocol} broadcasts...\n`);
6052

61-
for (const release of releasesToCheck) {
53+
for (const release of releasesByProtocol[protocol]) {
6254
for (const deployment of release.deployments) {
6355
const chain = getChain(deployment.chainId);
6456

@@ -146,11 +138,10 @@ async function main(): Promise<void> {
146138
console.log(`${EMOJIS.testnet} Missing testnet broadcasts: ${testnetCount}\n`);
147139
}
148140

149-
// Run the script
150141
main().catch((error) => {
151142
logger.error(`Error checking missing broadcasts: ${error.message}`);
152143
if (error.stack) {
153144
logger.error(error.stack);
154145
}
155-
process.exit(1);
146+
throw error;
156147
});

src/contracts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as names } from "./names";

src/contracts/names.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import airdropsV1_1 from "@src/releases/airdrops/v1.1/manifest";
2+
import airdropsV1_2 from "@src/releases/airdrops/v1.2/manifest";
3+
import airdropsV1_3 from "@src/releases/airdrops/v1.3/manifest";
4+
import flowV1_0 from "@src/releases/flow/v1.0/manifest";
5+
import flowV1_1 from "@src/releases/flow/v1.1/manifest";
6+
import legacyV1_0 from "@src/releases/legacy/v1.0/manifest";
7+
import legacyV1_1 from "@src/releases/legacy/v1.1/manifest";
8+
import lockupV1_0 from "@src/releases/lockup/v1.0/manifest";
9+
import lockupV1_1 from "@src/releases/lockup/v1.1/manifest";
10+
import lockupV1_2 from "@src/releases/lockup/v1.2/manifest";
11+
import lockupV2_0 from "@src/releases/lockup/v2.0/manifest";
12+
import type { Sablier } from "@src/types";
13+
import _ from "lodash";
14+
15+
/**
16+
* At runtime: for v1.x, merge `core` + `periphery`; for v2.0, use the top-level record directly.
17+
*/
18+
function flattenNames(manifest: Sablier.Manifest): Record<string, string> {
19+
if ("core" in manifest && "periphery" in manifest) {
20+
const lockupManifest = manifest as Sablier.Manifest.LockupV1;
21+
return { ...lockupManifest.core, ...lockupManifest.periphery };
22+
}
23+
// v2.0 has no core/periphery nesting
24+
return { ...manifest } as Record<string, string>;
25+
}
26+
27+
/**
28+
* Works at compile-time!
29+
*/
30+
type LeafKeys<T> = T extends { core: Record<string, unknown>; periphery: Record<string, unknown> }
31+
? keyof T["core"] | keyof T["periphery"]
32+
: keyof T;
33+
34+
type A1_1 = LeafKeys<typeof airdropsV1_1>;
35+
type A1_2 = LeafKeys<typeof airdropsV1_2>;
36+
type A1_3 = LeafKeys<typeof airdropsV1_3>;
37+
38+
type F1_0 = LeafKeys<typeof flowV1_0>;
39+
type F1_1 = LeafKeys<typeof flowV1_1>;
40+
41+
type LEGACY_1_0 = LeafKeys<typeof legacyV1_0>;
42+
type LEGACY_1_1 = LeafKeys<typeof legacyV1_1>;
43+
44+
type L1_0 = LeafKeys<typeof lockupV1_0>;
45+
type L1_1 = LeafKeys<typeof lockupV1_1>;
46+
type L1_2 = LeafKeys<typeof lockupV1_2>;
47+
type L2_0 = LeafKeys<typeof lockupV2_0>;
48+
49+
// Final exported type: only these known keys allowed
50+
export type ContractNames = Record<
51+
A1_1 | A1_2 | A1_3 | F1_0 | F1_1 | LEGACY_1_0 | LEGACY_1_1 | L1_0 | L1_1 | L1_2 | L2_0,
52+
string
53+
>;
54+
55+
// Runtime: flatten & merge across all releases
56+
const manifests = [
57+
airdropsV1_1,
58+
airdropsV1_2,
59+
airdropsV1_3,
60+
flowV1_0,
61+
flowV1_1,
62+
legacyV1_0,
63+
legacyV1_1,
64+
lockupV1_0,
65+
lockupV1_1,
66+
lockupV1_2,
67+
lockupV2_0,
68+
];
69+
const flattened = manifests.map(flattenNames);
70+
71+
const names: ContractNames = _.merge({}, ...flattened) as ContractNames;
72+
73+
export default names;

src/helpers.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getChain } from "@src/chains";
22
import _ from "lodash";
33
import type { Sablier } from "./types";
4-
import Version from "./version";
54

65
/**
76
* Get the explorer URL for a contract. Compatible with Etherscan, Blockscout, etc.
@@ -13,23 +12,6 @@ export function getContractExplorerURL(explorerURL: string, contractAddress: Sab
1312
return `${explorerURL}/address/${contractAddress}`;
1413
}
1514

16-
/**
17-
* Check if a version is valid for a given protocol
18-
* @see {@link Version}
19-
* @param protocol - The protocol to check (airdrops, flow, legacy, lockup)
20-
* @param version - The version to validate
21-
* @returns Whether the version is valid for the given protocol
22-
*/
23-
export function isValidVersion(protocol: Sablier.Protocol, version: Sablier.Version): boolean {
24-
const protocolMap = {
25-
airdrops: Version.Airdrops,
26-
flow: Version.Flow,
27-
legacy: Version.Legacy,
28-
lockup: Version.Lockup,
29-
};
30-
return _.some(_.values(protocolMap[protocol]), (v) => v === version);
31-
}
32-
3315
export function sortChains<T extends { name: string }>(chains: T[]): T[] {
3416
return chains.sort((a, b) => a.name.localeCompare(b.name));
3517
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export * from "./helpers";
33
export * from "./indexers";
44
export * from "./releases";
55
export * from "./types";
6+
export * as contracts from "./contracts";
67
export * as nativeTokens from "./native-tokens";
78
export { default as Version } from "./version";

src/releases/airdrops/index.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ import { release as releaseV1_1 } from "./v1.1";
33
import { release as releaseV1_2 } from "./v1.2";
44
import { release as releaseV1_3 } from "./v1.3";
55

6-
export const airdrops: Sablier.Release[] = [releaseV1_1, releaseV1_2, releaseV1_3];
6+
const airdrops: Sablier.Release[] = [releaseV1_1, releaseV1_2, releaseV1_3];
77

8-
export const airdropsByVersion: Record<Sablier.Version.Airdrops, Sablier.Release> = {
9-
"v1.1": releaseV1_1,
10-
"v1.2": releaseV1_2,
11-
"v1.3": releaseV1_3,
12-
};
8+
export default airdrops;

0 commit comments

Comments
 (0)