fix: consolidate viem types, typed window

This commit is contained in:
Arseniy Klempner 2025-11-12 16:16:05 -08:00
parent a8f1bfa4d8
commit f07f3cf5a9
No known key found for this signature in database
GPG Key ID: 51653F18863BD24B
7 changed files with 55 additions and 42 deletions

View File

@ -44,8 +44,7 @@
"watch:test": "mocha --watch",
"prepublish": "npm run build",
"reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build",
"setup:contract-abi": "./generate_contract_abi.sh",
"generate:contract": "npx wagmi generate"
"setup:contract-abi": "./generate_contract_abi.sh"
},
"engines": {
"node": ">=22"

View File

@ -3,15 +3,15 @@ import {
type Address,
decodeEventLog,
getContract,
GetContractReturnType,
type GetContractReturnType,
type Hash,
publicActions,
PublicClient,
WalletClient
type PublicClient,
type WalletClient
} from "viem";
import { IdentityCredential } from "../identity.js";
import { DecryptedCredentials } from "../keystore/types.js";
import type { DecryptedCredentials } from "../keystore/types.js";
import type { RpcClient } from "../utils/index.js";
import {
DEFAULT_RATE_LIMIT,
@ -32,7 +32,7 @@ export class RLNBaseContract {
typeof wakuRlnV2Abi,
PublicClient | WalletClient
>;
public rpcClient: WalletClient & PublicClient;
public rpcClient: RpcClient;
private rateLimit: number;
private minRateLimit?: number;
private maxRateLimit?: number;
@ -45,8 +45,7 @@ export class RLNBaseContract {
log.info("Initializing RLNBaseContract", { address, rateLimit });
this.rpcClient = rpcClient.extend(publicActions) as WalletClient &
PublicClient;
this.rpcClient = rpcClient;
this.contract = getContract({
address,
abi: wakuRlnV2Abi,
@ -223,7 +222,7 @@ export class RLNBaseContract {
try {
await this.contract.simulate.extendMemberships([[idCommitmentBigInt]], {
chain: this.rpcClient.chain,
account: (this.rpcClient as WalletClient).account!.address
account: this.rpcClient.account.address
});
} catch (err) {
throw new Error("Simulating extending membership failed: " + err);
@ -231,7 +230,7 @@ export class RLNBaseContract {
const hash = await this.contract.write.extendMemberships(
[[idCommitmentBigInt]],
{
account: this.rpcClient.account!,
account: this.rpcClient.account,
chain: this.rpcClient.chain
}
);
@ -261,7 +260,7 @@ export class RLNBaseContract {
[[idCommitmentBigInt], eraseFromMembershipSet],
{
chain: this.rpcClient.chain,
account: (this.rpcClient as WalletClient).account!.address
account: this.rpcClient.account.address
}
);
} catch (err) {
@ -272,7 +271,7 @@ export class RLNBaseContract {
[[idCommitmentBigInt], eraseFromMembershipSet],
{
chain: this.rpcClient.chain,
account: this.rpcClient.account!
account: this.rpcClient.account
}
);
await this.rpcClient.waitForTransactionReceipt({ hash });
@ -301,7 +300,7 @@ export class RLNBaseContract {
[idCommitmentBigInt, rateLimit, []],
{
chain: this.rpcClient.chain,
account: (this.rpcClient as WalletClient).account!.address
account: this.rpcClient.account.address
}
);
} catch (err) {
@ -312,7 +311,7 @@ export class RLNBaseContract {
[idCommitmentBigInt, rateLimit, []],
{
chain: this.rpcClient.chain,
account: this.rpcClient.account!
account: this.rpcClient.account
}
);
await this.rpcClient.waitForTransactionReceipt({ hash });
@ -333,7 +332,7 @@ export class RLNBaseContract {
try {
await this.contract.simulate.withdraw([token as Address], {
chain: this.rpcClient.chain,
account: (this.rpcClient as WalletClient).account!.address
account: this.rpcClient.account.address
});
} catch (err) {
throw new Error("Error simulating withdraw: " + err);
@ -341,7 +340,7 @@ export class RLNBaseContract {
const hash = await this.contract.write.withdraw([token as Address], {
chain: this.rpcClient.chain,
account: this.rpcClient.account!
account: this.rpcClient.account
});
await this.rpcClient.waitForTransactionReceipt({ hash });
@ -351,6 +350,12 @@ export class RLNBaseContract {
identity: IdentityCredential
): Promise<DecryptedCredentials | undefined> {
try {
if (!this.rpcClient.account) {
throw new Error(
"Failed to registerWithIdentity: no account set in wallet client"
);
}
log.info(
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
);
@ -377,7 +382,7 @@ export class RLNBaseContract {
[identity.IDCommitmentBigInt, this.rateLimit, []],
{
chain: this.rpcClient.chain,
account: (this.rpcClient as WalletClient).account!.address
account: this.rpcClient.account.address
}
);
@ -385,7 +390,7 @@ export class RLNBaseContract {
[identity.IDCommitmentBigInt, this.rateLimit, []],
{
chain: this.rpcClient.chain,
account: this.rpcClient.account!
account: this.rpcClient.account
}
);

View File

@ -1,4 +1,6 @@
import { Address, WalletClient } from "viem";
import { Address } from "viem";
import { RpcClient } from "../utils/index.js";
export type Member = {
idCommitment: string;
@ -6,7 +8,7 @@ export type Member = {
};
export interface RLNContractOptions {
rpcClient: WalletClient;
rpcClient: RpcClient;
address: Address;
rateLimit?: number;
}

View File

@ -1,5 +1,5 @@
import { Logger } from "@waku/utils";
import { publicActions, PublicClient, WalletClient } from "viem";
import { publicActions } from "viem";
import { RLN_CONTRACT } from "./contract/constants.js";
import { RLNBaseContract } from "./contract/rln_base_contract.js";
@ -10,7 +10,7 @@ import type {
} from "./keystore/index.js";
import { KeystoreEntity, Password } from "./keystore/types.js";
import { RegisterMembershipOptions, StartRLNOptions } from "./types.js";
import { createViemClientFromWindow } from "./utils/index.js";
import { createViemClientFromWindow, RpcClient } from "./utils/index.js";
import { Zerokit } from "./zerokit.js";
const log = new Logger("rln:credentials");
@ -24,7 +24,7 @@ export class RLNCredentialsManager {
protected starting = false;
public contract: undefined | RLNBaseContract;
public rpcClient: undefined | (WalletClient & PublicClient);
public rpcClient: undefined | RpcClient;
protected keystore = Keystore.create();
public credentials: undefined | DecryptedCredentials;
@ -128,7 +128,7 @@ export class RLNCredentialsManager {
protected async determineStartOptions(
options: StartRLNOptions,
credentials: KeystoreEntity | undefined
): Promise<StartRLNOptions & { rpcClient: WalletClient & PublicClient }> {
): Promise<StartRLNOptions & { rpcClient: RpcClient }> {
let chainId = credentials?.membership.chainId;
const address =
credentials?.membership.address ||
@ -140,11 +140,9 @@ export class RLNCredentialsManager {
log.info(`Using Linea contract with chainId: ${chainId}`);
}
let rpcClient: (WalletClient & PublicClient) | undefined =
options.rpcClient?.extend(publicActions) as WalletClient & PublicClient;
if (!rpcClient) {
rpcClient = await createViemClientFromWindow();
}
const rpcClient: RpcClient = options.walletClient
? options.walletClient.extend(publicActions)
: await createViemClientFromWindow();
const currentChainId = rpcClient.chain?.id;
log.info(`Current chain ID: ${currentChainId}`);

View File

@ -10,7 +10,7 @@ export type StartRLNOptions = {
/**
* If not set - will attempt to create from provider injected in window.
*/
rpcClient?: WalletClient;
walletClient?: WalletClient;
/**
* If not set - will use default SEPOLIA_CONTRACT address.
*/

View File

@ -1,4 +1,4 @@
export { createViemClientFromWindow } from "./walletClient.js";
export { createViemClientFromWindow, RpcClient } from "./rpcClient.js";
export { BytesUtils } from "./bytes.js";
export { sha256, poseidonHash } from "./hash.js";
export { dateToEpoch, epochIntToBytes, epochBytesToInt } from "./epoch.js";

View File

@ -1,17 +1,25 @@
import "viem/window";
import {
type Address,
createWalletClient,
custom,
PublicActions,
publicActions,
PublicClient,
WalletClient
} from "viem";
import { lineaSepolia } from "viem/chains";
export const createViemClientFromWindow = async (): Promise<
WalletClient & PublicClient
> => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ethereum = (window as any).ethereum;
export type RpcClient = WalletClient & PublicActions;
/**
* Checks window for injected Ethereum provider, requests user to connect, and creates an RPC client object
* capable of performing both read and write operations on the blockchain.
*
* If the wallet is not connected to the Linea Sepolia network, it will attempt to switch to it.
* If the wallet does not have the Linea Sepolia network added, it will attempt to add it.
*/
export const createViemClientFromWindow = async (): Promise<RpcClient> => {
const ethereum = window.ethereum;
if (!ethereum) {
throw Error(
@ -21,10 +29,10 @@ export const createViemClientFromWindow = async (): Promise<
const [account] = await ethereum.request({ method: "eth_requestAccounts" });
const rpcClient = createWalletClient({
account,
const rpcClient: RpcClient = createWalletClient({
account: account as Address,
chain: lineaSepolia,
transport: custom(ethereum)
transport: custom(window.ethereum!)
}).extend(publicActions);
// Ensure wallet is connected to Linea Sepolia
@ -39,6 +47,7 @@ export const createViemClientFromWindow = async (): Promise<
error.code === 4902
) {
await rpcClient.addChain({ chain: lineaSepolia });
await rpcClient.switchChain({ id: lineaSepolia.id });
} else {
throw error;
}