Skip to content

Commit 0f65961

Browse files
authored
Merge pull request #744 from AugurProject/t3code/migrate-javascript-to-typescript
Migrate handwritten JavaScript to strict TypeScript
2 parents 3ae7e43 + 5ffd43c commit 0f65961

55 files changed

Lines changed: 5053 additions & 1767 deletions

Some content is hidden

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

AGENTS.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Run `bun run check:generated-clean` only for CI/release freshness work or when c
109109
Generated outputs are intentionally untracked, except for the documentation outputs and
110110
vendored deployment input listed below. The documentation outputs are tracked because the
111111
static documentation site loads them directly;
112-
`bun run docs:check-charts`, `bun run docs:check-contract-reference`, and
112+
`bun run docs:check-charts`, `bun run docs:check-runtime`, `bun run docs:check-contract-reference`, and
113113
`bun run docs:check-index` enforce their freshness. `bun run check:uniswap-deployment-artifact`
114114
pins the deployment input and prevents its large upstream packages from entering the lockfile.
115115

@@ -122,9 +122,15 @@ pins the deployment input and prevents its large upstream packages from entering
122122
| `ui/js/**` | UI TypeScript build |
123123
| `ui/vendor/**` | `bun run ui:vendor` |
124124
| `docs/assets/js/chartRuntime.js` | `bun run docs:build-charts` |
125+
| `docs/assets/js/docsShell.js` | `bun run docs:build-runtime` |
126+
| `docs/assets/js/responsiveDocs.js` | `bun run docs:build-runtime` |
127+
| `docs/assets/js/interactiveTools.js` | `bun run docs:build-runtime` |
128+
| `docs/assets/js/invariantExplorer.js` | `bun run docs:build-runtime` |
129+
| `docs/assets/js/mmrProofPlanner.js` | `bun run docs:build-runtime` |
125130
| `docs/assets/js/docsData.js` | `bun run docs:build-index` |
126131
| `docs/assets/js/docsSearchData.js` | `bun run docs:build-index` |
127132
| `docs/reference/contracts.html` | `bun run docs:generate-contract-reference` |
133+
| `bots/open-oracle-arbitrager/docs/chart-runtime.js` | `cd bots/open-oracle-arbitrager && bun run build:docs`; validate with `bun run check:generated` |
128134
| `scripts/artifacts/uniswap-deployment.json` | Pinned bytecode from the upstream package versions recorded in the artifact; validate with `bun run check:uniswap-deployment-artifact` |
129135

130136
Do not regenerate or commit these outputs unless the task requires them or a required check reports a missing expected artifact. A deployment workflow that adds another tracked generated artifact must update this policy and add a dirty-diff freshness check in the same change.

augurScan/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ node_modules/
22
.env
33
coverage/
44
qa/
5+
public/app.js

augurScan/Dockerfile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@ WORKDIR /workspace
33
COPY augurScan/package.json augurScan/bun.lock ./
44
RUN bun install --frozen-lockfile --production
55

6+
FROM oven/bun:1.3.14-alpine AS browser-build
7+
WORKDIR /workspace/augurScan
8+
COPY augurScan/package.json augurScan/bun.lock ./
9+
RUN bun install --frozen-lockfile
10+
COPY augurScan/browser ./browser
11+
COPY augurScan/scripts/build-browser.ts ./scripts/build-browser.ts
12+
COPY augurScan/public ./public
13+
RUN bun run build
14+
615
FROM oven/bun:1.3.14-alpine AS runtime
716
WORKDIR /workspace
817
ENV NODE_ENV=production
918
COPY --from=dependencies /workspace/node_modules ./node_modules
1019
COPY augurScan/package.json augurScan/bun.lock ./
1120
COPY augurScan/config ./augurScan/config
1221
COPY augurScan/migrations ./augurScan/migrations
13-
COPY augurScan/public ./augurScan/public
22+
COPY --from=browser-build /workspace/augurScan/public ./augurScan/public
1423
COPY augurScan/src ./augurScan/src
1524
COPY shared/ts/ethereum.ts ./shared/ts/ethereum.ts
1625
USER bun

augurScan/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ bun test
4646
bun run dev
4747
```
4848

49+
The browser source lives under `browser/`. `bun run build` bundles `browser/app.ts` and its helpers to the stable browser entry point `public/app.js`; that generated file is ignored and must not be edited. `bun run dev`, `bun run start`, and `bun run qa:serve` build it automatically, and development mode rebuilds it when browser TypeScript changes.
50+
4951
Local development expects PostgreSQL at `POSTGRES_URL`. The server applies SQL migrations under a PostgreSQL advisory lock before serving. Migration 005 introduces range-index dataset cursors and rejects an older database if it has an indexed network checkpoint or any block row. No legacy checkpoint or activity compatibility is retained. For the Compose setup, first run the shared-network command under **Start with Docker**. Then run `docker compose down --volumes` to delete the old database and `docker compose up --build` to rebuild it from each network's automatically discovered effective start. A database without a checkpoint or block row migrates in place.
5052

5153
`POSTGRES_URL` must connect directly to PostgreSQL or through a session-mode pooler. The per-network writer lease is a session-level advisory lock and is not compatible with transaction-mode pooling. At acquisition, augurScan records the PostgreSQL backend PID and verifies that later lease checks remain on that backend. If a proxy moves the reserved connection, the indexer reports an actionable `DatabaseConsistencyError` instead of treating the new backend as the lease owner.

augurScan/biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
}
2121
},
2222
"files": {
23-
"includes": ["**", "!node_modules", "!bun.lock", "!config/abis.json"]
23+
"includes": ["**", "!node_modules", "!bun.lock", "!config/abis.json", "!public/app.js"]
2424
}
2525
}
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
export type JsonPrimitive = string | number | boolean | null
2+
export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
3+
4+
export const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value)
5+
export const isString = (value: unknown): value is string => typeof value === 'string'
6+
export const isNullableString = (value: unknown): value is string | null => value === null || isString(value)
7+
export const isStringOrNumber = (value: unknown): value is string | number => isString(value) || typeof value === 'number'
8+
export const isJsonValue = (value: unknown): value is JsonValue => {
9+
if (value === null || ['string', 'number', 'boolean'].includes(typeof value)) return true
10+
if (Array.isArray(value)) return value.every(isJsonValue)
11+
return isRecord(value) && Object.values(value).every(isJsonValue)
12+
}
13+
export const isJsonRecord = (value: unknown): value is Record<string, JsonValue> => isRecord(value) && Object.values(value).every(isJsonValue)
14+
export const isNullableJsonRecord = (value: unknown): value is Record<string, JsonValue> | null => value === null || isJsonRecord(value)
15+
16+
export const isNetworkRecordValue = (value: unknown): boolean =>
17+
isRecord(value) &&
18+
isString(value['chain_id']) &&
19+
isString(value['id']) &&
20+
isString(value['name']) &&
21+
isString(value['start_block']) &&
22+
isNullableString(value['indexed_block']) &&
23+
isNullableString(value['indexed_hash']) &&
24+
isNullableString(value['indexed_timestamp']) &&
25+
isNullableString(value['observed_block']) &&
26+
isNullableString(value['finalized_block']) &&
27+
isString(value['phase']) &&
28+
isNullableString(value['last_poll_at']) &&
29+
isNullableString(value['last_success_at']) &&
30+
typeof value['consecutive_failures'] === 'number' &&
31+
(value['last_reorg_at'] === undefined || isNullableString(value['last_reorg_at'])) &&
32+
(value['next_retry_at'] === undefined || isNullableString(value['next_retry_at'])) &&
33+
isNullableString(value['last_error']) &&
34+
isString(value['explorer_base_url'])
35+
36+
export const isAddressIdentityValue = (value: unknown): boolean =>
37+
isRecord(value) &&
38+
typeof value['chainId'] === 'number' &&
39+
Number.isSafeInteger(value['chainId']) &&
40+
isString(value['address']) &&
41+
(value['label'] === undefined || isString(value['label'])) &&
42+
(value['kind'] === undefined || isString(value['kind']))
43+
44+
export const isChartRowValue = (value: unknown): boolean =>
45+
isRecord(value) && isString(value['timestamp']) && Object.values(value).every((item) => item === undefined || isJsonValue(item))
46+
47+
export const isAmmPriceValue = (value: unknown): boolean =>
48+
isRecord(value) &&
49+
isString(value['timestamp']) &&
50+
isString(value['block_number']) &&
51+
isString(value['conditional_yes_bps']) &&
52+
isString(value['conditional_no_bps']) &&
53+
isString(value['yes_reserve_atto_shares']) &&
54+
isString(value['no_reserve_atto_shares'])
55+
56+
export const isRepEthPriceValue = (value: unknown): boolean =>
57+
isRecord(value) &&
58+
isString(value['timestamp']) &&
59+
isString(value['block_number']) &&
60+
isString(value['event_name']) &&
61+
isNullableString(value['report_id']) &&
62+
isString(value['rep_per_eth_1e18']) &&
63+
isNullableString(value['settlement_timestamp'])
64+
65+
export const isUniswapPriceValue = (value: unknown): boolean =>
66+
isRecord(value) &&
67+
isString(value['timestamp']) &&
68+
isString(value['block_number']) &&
69+
isString(value['venue']) &&
70+
isString(value['market_id']) &&
71+
isString(value['contract_address']) &&
72+
isString(value['fee_hundredths_bip']) &&
73+
isString(value['quote_symbol']) &&
74+
isString(value['event_name']) &&
75+
isString(value['rep_per_eth_1e18'])
76+
77+
export const isArgumentDefinition = (value: unknown): boolean =>
78+
isRecord(value) &&
79+
typeof value['index'] === 'number' &&
80+
Number.isInteger(value['index']) &&
81+
isString(value['name']) &&
82+
isString(value['type']) &&
83+
(value['indexed'] === undefined || typeof value['indexed'] === 'boolean')
84+
85+
export const isNullableArgumentDefinitions = (value: unknown): boolean => value === null || (Array.isArray(value) && value.every(isArgumentDefinition))
86+
87+
export const isActivityRecordValue = (value: unknown): boolean =>
88+
isRecord(value) &&
89+
isString(value['chain_id']) &&
90+
isString(value['network_id']) &&
91+
isString(value['block_number']) &&
92+
isString(value['block_hash']) &&
93+
isString(value['block_timestamp']) &&
94+
typeof value['transaction_index'] === 'number' &&
95+
typeof value['log_index'] === 'number' &&
96+
isString(value['tx_hash']) &&
97+
isString(value['emitter_address']) &&
98+
isNullableString(value['contract_label']) &&
99+
isNullableString(value['contract_kind']) &&
100+
isNullableString(value['event_name']) &&
101+
isString(value['summary']) &&
102+
isString(value['decode_status']) &&
103+
typeof value['canonical'] === 'boolean' &&
104+
typeof value['finalized'] === 'boolean' &&
105+
Array.isArray(value['topics']) &&
106+
value['topics'].every(isString) &&
107+
isString(value['data']) &&
108+
isNullableJsonRecord(value['arguments']) &&
109+
isNullableJsonRecord(value['display_arguments']) &&
110+
isNullableArgumentDefinitions(value['argument_schema']) &&
111+
isNullableString(value['origin_address']) &&
112+
isString(value['explorer_base_url'])
113+
114+
export const isRelatedLogRecordValue = (value: unknown): boolean =>
115+
isRecord(value) &&
116+
typeof value['log_index'] === 'number' &&
117+
isString(value['emitter_address']) &&
118+
isNullableString(value['event_name']) &&
119+
isString(value['summary'])
120+
121+
export const isLogDetailValue = (value: unknown): boolean =>
122+
isActivityRecordValue(value) &&
123+
isRecord(value) &&
124+
isNullableString(value['to_address']) &&
125+
isString(value['value']) &&
126+
isString(value['input']) &&
127+
isString(value['gas_used']) &&
128+
isNullableString(value['contract_provenance']) &&
129+
isNullableString(value['event_signature']) &&
130+
isNullableString(value['function_signature']) &&
131+
isNullableString(value['action_summary']) &&
132+
isNullableJsonRecord(value['action_arguments']) &&
133+
isNullableJsonRecord(value['action_display_arguments']) &&
134+
isNullableArgumentDefinitions(value['action_argument_schema']) &&
135+
isJsonRecord(value['receipt']) &&
136+
Array.isArray(value['relatedLogs']) &&
137+
value['relatedLogs'].every(isRelatedLogRecordValue)
138+
139+
export const isAccountTransactionValue = (value: unknown): boolean =>
140+
isRecord(value) &&
141+
isString(value['chain_id']) &&
142+
isString(value['tx_hash']) &&
143+
isString(value['block_hash']) &&
144+
isString(value['block_number']) &&
145+
isString(value['block_timestamp']) &&
146+
typeof value['transaction_index'] === 'number' &&
147+
isString(value['from_address']) &&
148+
isNullableString(value['to_address']) &&
149+
isNullableString(value['to_label']) &&
150+
isNullableString(value['to_kind']) &&
151+
isString(value['value']) &&
152+
isString(value['status']) &&
153+
isString(value['gas_used']) &&
154+
isNullableString(value['function_name']) &&
155+
isNullableString(value['function_signature']) &&
156+
isNullableString(value['action_summary']) &&
157+
isNullableJsonRecord(value['action_arguments']) &&
158+
isNullableJsonRecord(value['action_display_arguments']) &&
159+
isNullableArgumentDefinitions(value['action_argument_schema']) &&
160+
(value['roles'] === undefined || (Array.isArray(value['roles']) && value['roles'].every(isString))) &&
161+
(value['pool_addresses'] === undefined ||
162+
value['pool_addresses'] === null ||
163+
(Array.isArray(value['pool_addresses']) && value['pool_addresses'].every(isString))) &&
164+
isString(value['explorer_base_url'])
165+
166+
export const isTokenBalanceValue = (value: unknown): boolean =>
167+
isRecord(value) &&
168+
isString(value['address']) &&
169+
isString(value['balance']) &&
170+
(value['contractLabel'] === undefined || isNullableString(value['contractLabel'])) &&
171+
(value['name'] === undefined || isNullableString(value['name'])) &&
172+
(value['universeId'] === undefined || isNullableString(value['universeId'])) &&
173+
isNullableString(value['symbol']) &&
174+
(value['decimals'] === null || typeof value['decimals'] === 'number') &&
175+
isString(value['blockNumber'])
176+
177+
export const isPoolAssociationValue = (value: unknown): boolean =>
178+
isRecord(value) && isString(value['address']) && isNullableString(value['label']) && isNullableString(value['questionTitle'])
179+
180+
export const isVaultPositionValue = (value: unknown): boolean =>
181+
isRecord(value) &&
182+
isString(value['poolAddress']) &&
183+
isNullableString(value['questionTitle']) &&
184+
isString(value['repBackingUnits']) &&
185+
isStringOrNumber(value['capacityOwnershipAttoRep']) &&
186+
isStringOrNumber(value['claimableFeesAttoEth']) &&
187+
isString(value['blockNumber'])
188+
189+
export const isNativeBalanceDetailValue = (value: unknown): boolean => isRecord(value) && isString(value['balance']) && isString(value['blockNumber'])
190+
191+
export const isRichListRecordValue = (value: unknown): boolean =>
192+
isRecord(value) &&
193+
isString(value['chain_id']) &&
194+
isString(value['network_id']) &&
195+
isString(value['explorer_base_url']) &&
196+
isString(value['address']) &&
197+
isNullableString(value['label']) &&
198+
isNullableString(value['kind']) &&
199+
isStringOrNumber(value['transaction_count']) &&
200+
isStringOrNumber(value['interaction_count']) &&
201+
isStringOrNumber(value['pool_count']) &&
202+
isStringOrNumber(value['vault_count']) &&
203+
(value['active_vault_count'] === undefined || isStringOrNumber(value['active_vault_count'])) &&
204+
(value['weth_balance'] === undefined || isString(value['weth_balance'])) &&
205+
(value['native_balance'] === undefined || isString(value['native_balance'])) &&
206+
(value['sampled_native_count'] === undefined || isStringOrNumber(value['sampled_native_count'])) &&
207+
(value['sampled_rep_token_count'] === undefined || isStringOrNumber(value['sampled_rep_token_count'])) &&
208+
(value['rep_token_count'] === undefined || isStringOrNumber(value['rep_token_count'])) &&
209+
(value['sampled_weth_token_count'] === undefined || isStringOrNumber(value['sampled_weth_token_count'])) &&
210+
(value['weth_token_count'] === undefined || isStringOrNumber(value['weth_token_count'])) &&
211+
(value['oldest_balance_block'] === undefined || isNullableString(value['oldest_balance_block'])) &&
212+
(value['last_balance_refresh'] === undefined || isNullableString(value['last_balance_refresh'])) &&
213+
(value['rep_balances_truncated'] === undefined || typeof value['rep_balances_truncated'] === 'boolean') &&
214+
(value['weth_balances_truncated'] === undefined || typeof value['weth_balances_truncated'] === 'boolean') &&
215+
Array.isArray(value['rep_balances']) &&
216+
value['rep_balances'].every(isTokenBalanceValue) &&
217+
Array.isArray(value['weth_balances']) &&
218+
value['weth_balances'].every(isTokenBalanceValue) &&
219+
(value['native_balance_detail'] === null || isNativeBalanceDetailValue(value['native_balance_detail'])) &&
220+
Array.isArray(value['pool_associations']) &&
221+
value['pool_associations'].every(isPoolAssociationValue) &&
222+
Array.isArray(value['vault_positions']) &&
223+
value['vault_positions'].every(isVaultPositionValue)
224+
225+
const hasStringFields = (value: Record<string, unknown>, fields: readonly string[]): boolean => fields.every((field) => isString(value[field]))
226+
const hasNullableStringFields = (value: Record<string, unknown>, fields: readonly string[]): boolean => fields.every((field) => isNullableString(value[field]))
227+
const hasOptionalNullableStringFields = (value: Record<string, unknown>, fields: readonly string[]): boolean =>
228+
fields.every((field) => value[field] === undefined || isNullableString(value[field]))
229+
230+
export const isPoolStateEntityValue = (value: unknown): boolean =>
231+
isRecord(value) &&
232+
hasStringFields(value, [
233+
'chain_id',
234+
'network_id',
235+
'pool_address',
236+
'parent_address',
237+
'universe_id',
238+
'question_id',
239+
'truth_auction_address',
240+
'coordinator_address',
241+
'share_token_address',
242+
'security_multiplier_bps',
243+
'initial_priority_fee_atto_eth_per_gas',
244+
'initial_retention_rate',
245+
'initial_settlement_collateral_atto_eth',
246+
'vault_count',
247+
'child_count',
248+
]) &&
249+
hasNullableStringFields(value, [
250+
'question_title',
251+
'settlement_collateral_atto_eth',
252+
'total_capacity_ownership_atto_rep',
253+
'fee_eligible_capacity_ownership_atto_rep',
254+
'total_claimable_vault_fees_atto_eth',
255+
'unallocated_accrued_fees_atto_eth',
256+
'current_retention_rate',
257+
'snapshot_block',
258+
])
259+
260+
export const isVaultStateEntityValue = (value: unknown): boolean =>
261+
isRecord(value) &&
262+
hasStringFields(value, [
263+
'chain_id',
264+
'network_id',
265+
'pool_address',
266+
'vault_address',
267+
'rep_backing_units',
268+
'capacity_ownership_atto_rep',
269+
'claimable_fees_atto_eth',
270+
'fee_index',
271+
'vault_fee_remainder',
272+
'resulting_total_rep_backing_units',
273+
'resulting_fee_eligible_capacity_ownership_atto_rep',
274+
'block_number',
275+
]) &&
276+
isNullableString(value['question_title'])
277+
278+
export const isQuestionStateEntityValue = (value: unknown): boolean =>
279+
isRecord(value) &&
280+
hasStringFields(value, [
281+
'chain_id',
282+
'network_id',
283+
'question_id',
284+
'title',
285+
'description',
286+
'created_timestamp',
287+
'start_time',
288+
'end_time',
289+
'num_ticks',
290+
'display_value_min',
291+
'display_value_max',
292+
'answer_unit',
293+
'pool_count',
294+
'fork_count',
295+
]) &&
296+
Array.isArray(value['outcome_options']) &&
297+
value['outcome_options'].every(isString) &&
298+
(value['block_number'] === undefined || isString(value['block_number']))
299+
300+
export const isUniverseStateEntityValue = (value: unknown): boolean =>
301+
isRecord(value) &&
302+
hasStringFields(value, [
303+
'chain_id',
304+
'network_id',
305+
'universe_id',
306+
'parent_universe_id',
307+
'forking_outcome_index',
308+
'reputation_token_address',
309+
'theoretical_supply_atto_rep',
310+
'child_count',
311+
'pool_count',
312+
]) &&
313+
hasOptionalNullableStringFields(value, [
314+
'active_fork_question_id',
315+
'active_fork_time',
316+
'forker_address',
317+
'fork_threshold_atto_rep',
318+
'migration_rep_balance_atto_rep',
319+
])

0 commit comments

Comments
 (0)