Skip to content

Commit

Permalink
Merge pull request #175 from cashubtc/linter
Browse files Browse the repository at this point in the history
DX: Enforce explicit type annotations on function parameters
  • Loading branch information
gandlafbtc authored Sep 19, 2024
2 parents 8909ece + 3e4b721 commit c6d2174
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 40 deletions.
9 changes: 4 additions & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// .eslintrc
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
Expand All @@ -10,12 +9,12 @@
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],

"rules": {
"@typescript-eslint/no-unused-vars": "warn",
// to enforce using type for object type definitions, can be type or interface
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/typedef": ["error", { "parameter": true, "arrowParameter": true }],
"@typescript-eslint/consistent-type-definitions": ["warn", "type"],
"@typescript-eslint/array-type": ["warn", { "default": "generic" }],
"@typescript-eslint/array-type": ["error", { "default": "generic" }],
"require-await": "off",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/require-await": "error",
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/consistent-type-exports": "warn",
"no-else-return": "warn"
Expand Down
3 changes: 1 addition & 2 deletions src/CashuMint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import type {
MintResponse,
PostRestorePayload,
MeltQuotePayload,
MeltQuoteResponse,
MintContactInfo
MeltQuoteResponse
} from './model/types/index.js';
import { MeltQuoteState } from './model/types/index.js';
import request from './request.js';
Expand Down
41 changes: 23 additions & 18 deletions src/CashuWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
CheckStateEnum,
SerializedBlindedSignature,
MeltQuoteState,
MintQuoteResponse
CheckStateEntry
} from './model/types/index.js';
import {
bytesToNumber,
Expand Down Expand Up @@ -165,7 +165,7 @@ class CashuWallet {
}
): Promise<Array<Proof>> {
const proofs: Array<Proof> = [];
const amount = tokenEntry.proofs.reduce((total, curr) => total + curr.amount, 0);
const amount = tokenEntry.proofs.reduce((total: number, curr: Proof) => total + curr.amount, 0);
let preference = options?.preference;
if (!preference) {
preference = getDefaultAmountPreference(amount);
Expand Down Expand Up @@ -215,13 +215,16 @@ class CashuWallet {
}
): Promise<SendResponse> {
if (options?.preference) {
amount = options?.preference?.reduce((acc, curr) => acc + curr.amount * curr.count, 0);
amount = options?.preference?.reduce(
(acc: number, curr: AmountPreference) => acc + curr.amount * curr.count,
0
);
}
const keyset = await this.getKeys(options?.keysetId);
let amountAvailable = 0;
const proofsToSend: Array<Proof> = [];
const proofsToKeep: Array<Proof> = [];
proofs.forEach((proof) => {
proofs.forEach((proof: Proof) => {
if (amountAvailable >= amount) {
proofsToKeep.push(proof);
return;
Expand Down Expand Up @@ -255,7 +258,7 @@ class CashuWallet {
const splitProofsToKeep: Array<Proof> = [];
const splitProofsToSend: Array<Proof> = [];
let amountKeepCounter = 0;
proofs.forEach((proof) => {
proofs.forEach((proof: Proof) => {
if (amountKeepCounter < amountKeep) {
amountKeepCounter += proof.amount;
splitProofsToKeep.push(proof);
Expand Down Expand Up @@ -295,9 +298,11 @@ class CashuWallet {
const { outputs, promises } = await this.mint.restore({ outputs: blindedMessages });

// Collect and map the secrets and blinding factors with the blinded messages that were returned from the mint
const validRs = rs.filter((r, i) => outputs.map((o) => o.B_).includes(blindedMessages[i].B_));
const validSecrets = secrets.filter((s, i) =>
outputs.map((o) => o.B_).includes(blindedMessages[i].B_)
const validRs = rs.filter((_: bigint, i: number) =>
outputs.map((o: SerializedBlindedMessage) => o.B_).includes(blindedMessages[i].B_)
);
const validSecrets = secrets.filter((_: Uint8Array, i: number) =>
outputs.map((o: SerializedBlindedMessage) => o.B_).includes(blindedMessages[i].B_)
);

return {
Expand All @@ -313,9 +318,9 @@ class CashuWallet {
const allKeys = await this.mint.getKeys(keysetId);
let keys;
if (keysetId) {
keys = allKeys.keysets.find((k) => k.id === keysetId);
keys = allKeys.keysets.find((k: MintKeys) => k.id === keysetId);
} else {
keys = allKeys.keysets.find((k) => (unit ? k.unit === unit : k.unit === 'sat'));
keys = allKeys.keysets.find((k: MintKeys) => (unit ? k.unit === unit : k.unit === 'sat'));
}
if (!keys) {
throw new Error(
Expand Down Expand Up @@ -516,8 +521,8 @@ class CashuWallet {
): Promise<MeltTokensResponse> {
const decodedToken = getDecodedToken(token);
const proofs = decodedToken.token
.filter((x) => x.mint === this.mint.mintUrl)
.flatMap((t) => t.proofs);
.filter((x: TokenEntry) => x.mint === this.mint.mintUrl)
.flatMap((t: TokenEntry) => t.proofs);
return this.payLnInvoice(invoice, proofs, meltQuote, {
keysetId: options?.keysetId,
counter: options?.counter
Expand Down Expand Up @@ -546,7 +551,7 @@ class CashuWallet {
payload: SwapPayload;
blindedMessages: BlindedTransaction;
} {
const totalAmount = proofsToSend.reduce((total, curr) => total + curr.amount, 0);
const totalAmount = proofsToSend.reduce((total: number, curr: Proof) => total + curr.amount, 0);
const keepBlindedMessages = this.createRandomBlindedMessages(
totalAmount - amount,
keyset.id,
Expand All @@ -565,7 +570,7 @@ class CashuWallet {
);
if (privkey) {
proofsToSend = getSignedProofs(
proofsToSend.map((p) => {
proofsToSend.map((p: Proof) => {
return {
amount: p.amount,
C: pointFromHex(p.C),
Expand Down Expand Up @@ -601,15 +606,15 @@ class CashuWallet {
*/
async checkProofsSpent<T extends { secret: string }>(proofs: Array<T>): Promise<Array<T>> {
const enc = new TextEncoder();
const Ys = proofs.map((p) => hashToCurve(enc.encode(p.secret)).toHex(true));
const Ys = proofs.map((p: T) => hashToCurve(enc.encode(p.secret)).toHex(true));
const payload = {
// array of Ys of proofs to check
Ys: Ys
};
const { states } = await this.mint.check(payload);

return proofs.filter((_, i) => {
const state = states.find((state) => state.Y === Ys[i]);
return proofs.filter((_: T, i: number) => {
const state = states.find((state: CheckStateEntry) => state.Y === Ys[i]);
return state && state.state === CheckStateEnum.SPENT;
});
}
Expand Down Expand Up @@ -734,7 +739,7 @@ class CashuWallet {
const A = pointFromHex(keyset.keys[p.amount]);
return constructProofFromPromise(blindSignature, r, secret, A);
})
.map((p) => serializeProof(p) as Proof);
.map((p: NUT11Proof) => serializeProof(p) as Proof);
}
}

Expand Down
30 changes: 15 additions & 15 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
encodeBase64ToJson,
encodeBase64toUint8,
encodeJsonToBase64,
encodeUint8toBase64,
encodeUint8toBase64Url
} from './base64.js';
import {
Expand All @@ -11,7 +10,6 @@ import {
Proof,
Token,
TokenEntry,
TokenV2,
TokenV4Template,
V4InnerToken,
V4ProofTemplate
Expand All @@ -27,7 +25,7 @@ function splitAmount(value: number, amountPreference?: Array<AmountPreference>):
chunks.push(...getPreference(value, amountPreference));
value =
value -
chunks.reduce((curr, acc) => {
chunks.reduce((curr: number, acc: number) => {
return curr + acc;
}, 0);
}
Expand All @@ -47,7 +45,7 @@ function isPowerOfTwo(number: number) {
function getPreference(amount: number, preferredAmounts: Array<AmountPreference>): Array<number> {
const chunks: Array<number> = [];
let accumulator = 0;
preferredAmounts.forEach((pa) => {
preferredAmounts.forEach((pa: AmountPreference) => {
if (!isPowerOfTwo(pa.amount)) {
throw new Error(
'Provided amount preferences contain non-power-of-2 numbers. Use only ^2 numbers'
Expand All @@ -66,7 +64,7 @@ function getPreference(amount: number, preferredAmounts: Array<AmountPreference>

function getDefaultAmountPreference(amount: number): Array<AmountPreference> {
const amounts = splitAmount(amount);
return amounts.map((a) => {
return amounts.map((a: number) => {
return { amount: a, count: 1 };
});
}
Expand Down Expand Up @@ -117,9 +115,11 @@ function getEncodedTokenV4(token: Token): string {
m: mint,
u: token.unit || 'sat',
t: Object.keys(idMap).map(
(id): V4InnerToken => ({
(id: string): V4InnerToken => ({
i: hexToBytes(id),
p: idMap[id].map((p): V4ProofTemplate => ({ a: p.amount, s: p.secret, c: hexToBytes(p.C) }))
p: idMap[id].map(
(p: Proof): V4ProofTemplate => ({ a: p.amount, s: p.secret, c: hexToBytes(p.C) })
)
})
)
} as TokenV4Template;
Expand All @@ -143,7 +143,7 @@ function getEncodedTokenV4(token: Token): string {
function getDecodedToken(token: string) {
// remove prefixes
const uriPrefixes = ['web+cashu://', 'cashu://', 'cashu:', 'cashu'];
uriPrefixes.forEach((prefix) => {
uriPrefixes.forEach((prefix: string) => {
if (!token.startsWith(prefix)) {
return;
}
Expand All @@ -170,8 +170,8 @@ function handleTokens(token: string): Token {
u: string;
};
const mergedTokenEntry: TokenEntry = { mint: tokenData.m, proofs: [] };
tokenData.t.forEach((tokenEntry) =>
tokenEntry.p.forEach((p) => {
tokenData.t.forEach((tokenEntry: V4InnerToken) =>
tokenEntry.p.forEach((p: V4ProofTemplate) => {
mergedTokenEntry.proofs.push({
secret: p.s,
C: bytesToHex(p.c),
Expand All @@ -191,9 +191,9 @@ function handleTokens(token: string): Token {
*/
export function deriveKeysetId(keys: Keys) {
const pubkeysConcat = Object.entries(keys)
.sort((a, b) => +a[0] - +b[0])
.map(([, pubKey]) => hexToBytes(pubKey))
.reduce((prev, curr) => mergeUInt8Arrays(prev, curr), new Uint8Array());
.sort((a: [string, string], b: [string, string]) => +a[0] - +b[0])
.map(([, pubKey]: [unknown, string]) => hexToBytes(pubKey))
.reduce((prev: Uint8Array, curr: Uint8Array) => mergeUInt8Arrays(prev, curr), new Uint8Array());
const hash = sha256(pubkeysConcat);
const hashHex = Buffer.from(hash).toString('hex').slice(0, 14);
return '00' + hashHex;
Expand All @@ -208,7 +208,7 @@ function mergeUInt8Arrays(a1: Uint8Array, a2: Uint8Array): Uint8Array {
}

export function sortProofsById(proofs: Array<Proof>) {
return proofs.sort((a, b) => a.id.localeCompare(b.id));
return proofs.sort((a: Proof, b: Proof) => a.id.localeCompare(b.id));
}

export function isObj(v: unknown): v is object {
Expand All @@ -226,7 +226,7 @@ export function checkResponse(data: { error?: string; detail?: string }) {
}

export function joinUrls(...parts: Array<string>): string {
return parts.map((part) => part.replace(/(^\/+|\/+$)/g, '')).join('/');
return parts.map((part: string) => part.replace(/(^\/+|\/+$)/g, '')).join('/');
}

export function sanitizeUrl(url: string): string {
Expand Down

0 comments on commit c6d2174

Please sign in to comment.