Skip to content

Commit

Permalink
Merge pull request #333 from tallycash/strictly-explicit
Browse files Browse the repository at this point in the history
Strictly Explicit: Enable TypeScript strict mode
  • Loading branch information
henryboldi authored Oct 21, 2021
2 parents 284efed + 6d72e59 commit 33b881f
Show file tree
Hide file tree
Showing 47 changed files with 635 additions and 458 deletions.
9 changes: 9 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ module.exports = {
// The rule is also mostly irrelevant to this codebase due to TypeScript
// usage (where .tsx is required).
"react/jsx-filename-extension": [0],
// TypeScript allows us to declare props that are non-optional internally
// but are interpreted as optional externally if they have defaultProps
// defined; the following two adjustments disable eslint-plugin-react
// checks that predate this ability for TS and that no longer apply.
"react/default-props-match-prop-types": [
2,
{ allowRequiredDefaults: true },
],
"react/require-default-props": [0],
// Shared components may have labels associated externally in a way ESLint
// does not detect.
"jsx-a11y/label-has-associated-control": [
Expand Down
42 changes: 22 additions & 20 deletions background/lib/alchemy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Ajv, { JTDDataType } from "ajv/dist/jtd"
import {
AlchemyProvider,
AlchemyWebSocketProvider,
Expand All @@ -8,8 +7,7 @@ import { utils } from "ethers"
import logger from "./logger"
import { AssetTransfer, HexString, SmartContractFungibleAsset } from "../types"
import { ETH, ETHEREUM } from "../constants"

const ajv = new Ajv()
import { jtdValidatorFor } from "./validation"

// JSON Type Definition for the Alchemy assetTransfers API.
// https://docs.alchemy.com/alchemy/documentation/enhanced-apis/transfers-api
Expand Down Expand Up @@ -45,12 +43,9 @@ const alchemyGetAssetTransfersJTD = {
},
} as const

type AlchemyAssetTransferResponse = JTDDataType<
typeof alchemyGetAssetTransfersJTD
>

const isValidAlchemyAssetTransferResponse =
ajv.compile<AlchemyAssetTransferResponse>(alchemyGetAssetTransfersJTD)
const isValidAlchemyAssetTransferResponse = jtdValidatorFor(
alchemyGetAssetTransfersJTD
)

/**
* Use Alchemy's getAssetTransfers call to get historical transfers for an
Expand Down Expand Up @@ -144,7 +139,7 @@ export async function getAssetTransfers(
dataSource: "alchemy",
} as AssetTransfer
})
.filter((t) => t)
.filter((t): t is AssetTransfer => t !== null)
}

// JSON Type Definition for the Alchemy token balance API.
Expand All @@ -167,10 +162,9 @@ const alchemyTokenBalanceJTD = {
additionalProperties: false,
} as const

type AlchemyTokenBalanceResponse = JTDDataType<typeof alchemyTokenBalanceJTD>

const isValidAlchemyTokenBalanceResponse =
ajv.compile<AlchemyTokenBalanceResponse>(alchemyTokenBalanceJTD)
const isValidAlchemyTokenBalanceResponse = jtdValidatorFor(
alchemyTokenBalanceJTD
)

/**
* Use Alchemy's getTokenBalances call to get balances for a particular address.
Expand Down Expand Up @@ -204,7 +198,16 @@ export async function getTokenBalances(

// TODO log balances with errors, consider returning an error type
return json.tokenBalances
.filter((b) => b.error === null && b.tokenBalance !== null)
.filter(
(
b
): b is typeof json["tokenBalances"][0] & {
tokenBalance: Exclude<
typeof json["tokenBalances"][0]["tokenBalance"],
null
>
} => b.error === null && b.tokenBalance !== null
)
.map((tokenBalance) => ({
contractAddress: tokenBalance.contractAddress,
amount:
Expand All @@ -228,10 +231,9 @@ const alchemyTokenMetadataJTD = {
additionalProperties: false,
} as const

type AlchemyTokenMetadataResponse = JTDDataType<typeof alchemyTokenMetadataJTD>

const isValidAlchemyTokenMetadataResponse =
ajv.compile<AlchemyTokenMetadataResponse>(alchemyTokenMetadataJTD)
const isValidAlchemyTokenMetadataResponse = jtdValidatorFor(
alchemyTokenMetadataJTD
)

/**
* Use Alchemy's getTokenMetadata call to get metadata for a token contract on
Expand Down Expand Up @@ -262,8 +264,8 @@ export async function getTokenMetadata(
name: json.name,
symbol: json.symbol,
metadata: {
logoURL: json.logo,
tokenLists: [],
...(json.logo ? { logoURL: json.logo } : {}),
},
homeNetwork: ETHEREUM, // TODO make multi-network friendly
contractAddress,
Expand Down
10 changes: 6 additions & 4 deletions background/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function genericLogger(level: LogLevel, input: unknown[]) {

console.log(...input)

const stackTrace = new Error().stack.split("\n").filter((line) => {
const stackTrace = new Error().stack?.split("\n")?.filter((line) => {
// Remove empty lines from the output
// Chrome prepends the word "Error" to the first line of the trace, but Firefox doesn't
// Let's ignore that for consistency between browsers!
Expand All @@ -69,9 +69,11 @@ function genericLogger(level: LogLevel, input: unknown[]) {
return true
})

// The first two lines of the stack trace will always be generated by this
// file, so let's ignore them.
console.log(stackTrace.slice(2).join("\n"))
if (typeof stackTrace !== "undefined") {
// The first two lines of the stack trace will always be generated by this
// file, so let's ignore them.
console.log(stackTrace.slice(2).join("\n"))
}
console.groupEnd()
}

Expand Down
54 changes: 24 additions & 30 deletions background/lib/prices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const COINGECKO_API_ROOT = "https://api.coingecko.com/api/v3"
export async function getPrice(
coingeckoCoinId = "ethereum",
currencySymbol = "usd"
): Promise<number> {
): Promise<number | null> {
const url = `${COINGECKO_API_ROOT}/simple/price?ids=${coingeckoCoinId}&vs_currencies=${currencySymbol}&include_last_updated_at=true`

const json = await fetchJson(url)
Expand All @@ -29,11 +29,7 @@ export async function getPrice(
return null
}

return json
? parseFloat(
json[coingeckoCoinId][currencySymbol] as string // FIXME Drop as when strict mode arrives and price schema type can include this.
)
: null
return json?.[coingeckoCoinId]?.[currencySymbol] || null
}

function multiplyByFloat(n: bigint, f: number, precision: number) {
Expand Down Expand Up @@ -67,34 +63,32 @@ export async function getPrices(
validate.errors
)

return null
return []
}

return assets.reduce((acc, asset) => {
const resolutionTime = Date.now()
return assets.flatMap((asset) => {
const simpleCoinPrices = json[asset.metadata.coinGeckoId]
return acc.concat(
vsCurrencies
.map((c) => {
const symbol = c.symbol.toLowerCase()
if (symbol in simpleCoinPrices) {
return {
pair: [c, asset],
amounts: [
multiplyByFloat(
BigInt(10) ** BigInt(c.decimals),
simpleCoinPrices[symbol] as number, // FIXME Drop as when strict mode arrives and price schema type can include this.
8
),
BigInt(1),
],
time: Date.now(),
} as PricePoint

return vsCurrencies
.map<PricePoint | undefined>((c) => {
const symbol = c.symbol.toLowerCase()
const coinPrice = simpleCoinPrices?.[symbol]

if (coinPrice) {
return {
pair: [c, asset],
amounts: [
multiplyByFloat(BigInt(10) ** BigInt(c.decimals), coinPrice, 8),
BigInt(1),
],
time: resolutionTime,
}
return undefined
})
.filter((p) => p)
)
}, [])
}
return undefined
})
.filter((p): p is PricePoint => p !== undefined)
})
}

/*
Expand Down
9 changes: 5 additions & 4 deletions background/lib/tokenList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ export function networkAssetsFromLists(
metadata: {
...original.metadata,
...asset.metadata,
tokenLists: original.metadata.tokenLists.concat(
asset.metadata.tokenLists
),
tokenLists:
original.metadata?.tokenLists?.concat(
asset.metadata?.tokenLists ?? []
) ?? [],
},
}
} else {
Expand All @@ -99,7 +100,7 @@ export function networkAssetsFromLists(

const merged = fungibleAssets.reduce(tokenReducer, {})
return Object.entries(merged)
.map(([k, v]) => v)
.map(([, v]) => v)
.slice()
.sort((a, b) =>
(a.metadata?.tokenLists?.length || 0) >
Expand Down
Loading

0 comments on commit 33b881f

Please sign in to comment.