diff --git a/packages/wallet/core/src/envelope.ts b/packages/wallet/core/src/envelope.ts index 16912f102..d59da6671 100644 --- a/packages/wallet/core/src/envelope.ts +++ b/packages/wallet/core/src/envelope.ts @@ -1,15 +1,15 @@ -import { Config, Payload, Signature } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Config, Payload, Signature } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' export type Envelope = { - readonly wallet: Address.Address + readonly wallet: Address.Checksummed readonly chainId: bigint readonly configuration: Config.Config readonly payload: T } export type Signature = { - address: Address.Address + address: Address.Checksummed signature: Signature.SignatureOfSignerLeaf } diff --git a/packages/wallet/core/src/preconditions/codec.ts b/packages/wallet/core/src/preconditions/codec.ts index a6c2a13f7..f025fdb82 100644 --- a/packages/wallet/core/src/preconditions/codec.ts +++ b/packages/wallet/core/src/preconditions/codec.ts @@ -1,4 +1,4 @@ -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' import { Precondition, NativeBalancePrecondition, @@ -41,7 +41,7 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi switch (p.type) { case 'native-balance': precondition = new NativeBalancePrecondition( - Address.from(data.address), + Address.checksum(data.address), data.min ? BigInt(data.min) : undefined, data.max ? BigInt(data.max) : undefined, ) @@ -49,8 +49,8 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi case 'erc20-balance': precondition = new Erc20BalancePrecondition( - Address.from(data.address), - Address.from(data.token), + Address.checksum(data.address), + Address.checksum(data.token), data.min ? BigInt(data.min) : undefined, data.max ? BigInt(data.max) : undefined, ) @@ -58,17 +58,17 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi case 'erc20-approval': precondition = new Erc20ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), - Address.from(data.operator), + Address.checksum(data.address), + Address.checksum(data.token), + Address.checksum(data.operator), BigInt(data.min), ) break case 'erc721-ownership': precondition = new Erc721OwnershipPrecondition( - Address.from(data.address), - Address.from(data.token), + Address.checksum(data.address), + Address.checksum(data.token), BigInt(data.tokenId), data.owned, ) @@ -76,17 +76,17 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi case 'erc721-approval': precondition = new Erc721ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), + Address.checksum(data.address), + Address.checksum(data.token), BigInt(data.tokenId), - Address.from(data.operator), + Address.checksum(data.operator), ) break case 'erc1155-balance': precondition = new Erc1155BalancePrecondition( - Address.from(data.address), - Address.from(data.token), + Address.checksum(data.address), + Address.checksum(data.token), BigInt(data.tokenId), data.min ? BigInt(data.min) : undefined, data.max ? BigInt(data.max) : undefined, @@ -95,10 +95,10 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi case 'erc1155-approval': precondition = new Erc1155ApprovalPrecondition( - Address.from(data.address), - Address.from(data.token), + Address.checksum(data.address), + Address.checksum(data.token), BigInt(data.tokenId), - Address.from(data.operator), + Address.checksum(data.operator), BigInt(data.min), ) break @@ -120,13 +120,23 @@ export function decodePrecondition(p: IntentPrecondition): Precondition | undefi } } +type PreconditionData = { + address: Address.Checksummed + token: Address.Checksummed + operator: Address.Checksummed + min: string + max: string + tokenId: string + owned: boolean +} + export function encodePrecondition(p: Precondition): string { - const data: any = {} + const data: Partial = {} switch (p.type()) { case 'native-balance': { const native = p as NativeBalancePrecondition - data.address = native.address.toString() + data.address = native.address if (native.min !== undefined) data.min = native.min.toString() if (native.max !== undefined) data.max = native.max.toString() break @@ -134,8 +144,8 @@ export function encodePrecondition(p: Precondition): string { case 'erc20-balance': { const erc20 = p as Erc20BalancePrecondition - data.address = erc20.address.toString() - data.token = erc20.token.toString() + data.address = erc20.address + data.token = erc20.token if (erc20.min !== undefined) data.min = erc20.min.toString() if (erc20.max !== undefined) data.max = erc20.max.toString() break @@ -143,17 +153,17 @@ export function encodePrecondition(p: Precondition): string { case 'erc20-approval': { const erc20 = p as Erc20ApprovalPrecondition - data.address = erc20.address.toString() - data.token = erc20.token.toString() - data.operator = erc20.operator.toString() + data.address = erc20.address + data.token = erc20.token + data.operator = erc20.operator data.min = erc20.min.toString() break } case 'erc721-ownership': { const erc721 = p as Erc721OwnershipPrecondition - data.address = erc721.address.toString() - data.token = erc721.token.toString() + data.address = erc721.address + data.token = erc721.token data.tokenId = erc721.tokenId.toString() if (erc721.owned !== undefined) data.owned = erc721.owned break @@ -161,17 +171,17 @@ export function encodePrecondition(p: Precondition): string { case 'erc721-approval': { const erc721 = p as Erc721ApprovalPrecondition - data.address = erc721.address.toString() - data.token = erc721.token.toString() + data.address = erc721.address + data.token = erc721.token data.tokenId = erc721.tokenId.toString() - data.operator = erc721.operator.toString() + data.operator = erc721.operator break } case 'erc1155-balance': { const erc1155 = p as Erc1155BalancePrecondition - data.address = erc1155.address.toString() - data.token = erc1155.token.toString() + data.address = erc1155.address + data.token = erc1155.token data.tokenId = erc1155.tokenId.toString() if (erc1155.min !== undefined) data.min = erc1155.min.toString() if (erc1155.max !== undefined) data.max = erc1155.max.toString() @@ -180,10 +190,10 @@ export function encodePrecondition(p: Precondition): string { case 'erc1155-approval': { const erc1155 = p as Erc1155ApprovalPrecondition - data.address = erc1155.address.toString() - data.token = erc1155.token.toString() + data.address = erc1155.address + data.token = erc1155.token data.tokenId = erc1155.tokenId.toString() - data.operator = erc1155.operator.toString() + data.operator = erc1155.operator data.min = erc1155.min.toString() break } diff --git a/packages/wallet/core/src/preconditions/types.ts b/packages/wallet/core/src/preconditions/types.ts index 23a9db22c..dafc0945e 100644 --- a/packages/wallet/core/src/preconditions/types.ts +++ b/packages/wallet/core/src/preconditions/types.ts @@ -1,4 +1,4 @@ -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' export interface Precondition { type(): string @@ -7,7 +7,7 @@ export interface Precondition { export class NativeBalancePrecondition implements Precondition { constructor( - public readonly address: Address.Address, + public readonly address: Address.Checksummed, public readonly min?: bigint, public readonly max?: bigint, ) {} @@ -29,8 +29,8 @@ export class NativeBalancePrecondition implements Precondition { export class Erc20BalancePrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, public readonly min?: bigint, public readonly max?: bigint, ) {} @@ -55,9 +55,9 @@ export class Erc20BalancePrecondition implements Precondition { export class Erc20ApprovalPrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, - public readonly operator: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, + public readonly operator: Address.Checksummed, public readonly min: bigint, ) {} @@ -84,8 +84,8 @@ export class Erc20ApprovalPrecondition implements Precondition { export class Erc721OwnershipPrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, public readonly tokenId: bigint, public readonly owned?: boolean, ) {} @@ -110,10 +110,10 @@ export class Erc721OwnershipPrecondition implements Precondition { export class Erc721ApprovalPrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, public readonly tokenId: bigint, - public readonly operator: Address.Address, + public readonly operator: Address.Checksummed, ) {} type(): string { @@ -139,8 +139,8 @@ export class Erc721ApprovalPrecondition implements Precondition { export class Erc1155BalancePrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, public readonly tokenId: bigint, public readonly min?: bigint, public readonly max?: bigint, @@ -169,10 +169,10 @@ export class Erc1155BalancePrecondition implements Precondition { export class Erc1155ApprovalPrecondition implements Precondition { constructor( - public readonly address: Address.Address, - public readonly token: Address.Address, + public readonly address: Address.Checksummed, + public readonly token: Address.Checksummed, public readonly tokenId: bigint, - public readonly operator: Address.Address, + public readonly operator: Address.Checksummed, public readonly min: bigint, ) {} diff --git a/packages/wallet/core/src/relayer/bundler.ts b/packages/wallet/core/src/relayer/bundler.ts index 9456a7826..86e235c3d 100644 --- a/packages/wallet/core/src/relayer/bundler.ts +++ b/packages/wallet/core/src/relayer/bundler.ts @@ -1,5 +1,5 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { UserOperation } from 'ox/erc4337' import { OperationStatus } from './relayer.js' @@ -9,13 +9,13 @@ export interface Bundler { id: string estimateLimits( - wallet: Address.Address, + wallet: Address.Checksummed, payload: Payload.Calls4337_07, ): Promise<{ speed?: 'slow' | 'standard' | 'fast'; payload: Payload.Calls4337_07 }[]> - relay(entrypoint: Address.Address, userOperation: UserOperation.RpcV07): Promise<{ opHash: Hex.Hex }> + relay(entrypoint: Address.Checksummed, userOperation: UserOperation.RpcV07): Promise<{ opHash: Hex.Hex }> status(opHash: Hex.Hex, chainId: bigint): Promise - isAvailable(entrypoint: Address.Address, chainId: bigint): Promise + isAvailable(entrypoint: Address.Checksummed, chainId: bigint): Promise } export function isBundler(relayer: any): relayer is Bundler { diff --git a/packages/wallet/core/src/relayer/bundlers/pimlico.ts b/packages/wallet/core/src/relayer/bundlers/pimlico.ts index 2a791d4d5..90009f028 100644 --- a/packages/wallet/core/src/relayer/bundlers/pimlico.ts +++ b/packages/wallet/core/src/relayer/bundlers/pimlico.ts @@ -1,6 +1,6 @@ -import { Payload } from '@0xsequence/wallet-primitives' +import { Address, Payload } from '@0xsequence/wallet-primitives' import { Bundler } from '../bundler.js' -import { Provider, Hex, Address, RpcTransport } from 'ox' +import { Provider, Hex, RpcTransport } from 'ox' import { UserOperation } from 'ox/erc4337' import { OperationStatus } from '../relayer.js' @@ -28,10 +28,10 @@ export class PimlicoBundler implements Bundler { this.bundlerRpcUrl = bundlerRpcUrl } - async isAvailable(entrypoint: Address.Address, chainId: bigint): Promise { + async isAvailable(entrypoint: Address.Checksummed, chainId: bigint): Promise { const [bundlerChainId, supportedEntryPoints] = await Promise.all([ this.bundlerRpc('eth_chainId', []), - this.bundlerRpc('eth_supportedEntryPoints', []), + this.bundlerRpc('eth_supportedEntryPoints', []), ]) if (chainId !== BigInt(bundlerChainId)) { @@ -41,13 +41,13 @@ export class PimlicoBundler implements Bundler { return supportedEntryPoints.some((ep) => Address.isEqual(ep, entrypoint)) } - async relay(entrypoint: Address.Address, userOperation: UserOperation.RpcV07): Promise<{ opHash: Hex.Hex }> { + async relay(entrypoint: Address.Checksummed, userOperation: UserOperation.RpcV07): Promise<{ opHash: Hex.Hex }> { const status = await this.bundlerRpc('eth_sendUserOperation', [userOperation, entrypoint]) return { opHash: status } } async estimateLimits( - wallet: Address.Address, + wallet: Address.Checksummed, payload: Payload.Calls4337_07, ): Promise< { diff --git a/packages/wallet/core/src/relayer/relayer.ts b/packages/wallet/core/src/relayer/relayer.ts index d03ca5a01..77c97c07e 100644 --- a/packages/wallet/core/src/relayer/relayer.ts +++ b/packages/wallet/core/src/relayer/relayer.ts @@ -1,10 +1,10 @@ -import { Payload, Precondition } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload, Precondition } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { FeeToken, GetMetaTxnReceiptReturn } from './standard/rpc/index.js' export interface FeeOption { token: FeeToken - to: string + to: Address.Checksummed value: string gasLimit: number } @@ -61,15 +61,15 @@ export interface Relayer { type: string id: string - isAvailable(wallet: Address.Address, chainId: bigint): Promise + isAvailable(wallet: Address.Checksummed, chainId: bigint): Promise feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> - relay(to: Address.Address, data: Hex.Hex, chainId: bigint, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> + relay(to: Address.Checksummed, data: Hex.Hex, chainId: bigint, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> status(opHash: Hex.Hex, chainId: bigint): Promise diff --git a/packages/wallet/core/src/relayer/standard/eip6963.ts b/packages/wallet/core/src/relayer/standard/eip6963.ts index 903c9fbba..f3a604b7f 100644 --- a/packages/wallet/core/src/relayer/standard/eip6963.ts +++ b/packages/wallet/core/src/relayer/standard/eip6963.ts @@ -1,8 +1,8 @@ import { createStore, EIP6963ProviderInfo, EIP6963ProviderDetail } from 'mipd' import { EIP1193ProviderAdapter, LocalRelayer } from './local.js' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' -import { Address, Hex } from 'ox' -import { Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' import { IntentPrecondition } from './rpc/relayer.gen.js' export class EIP6963Relayer implements Relayer { @@ -19,19 +19,19 @@ export class EIP6963Relayer implements Relayer { this.relayer = new LocalRelayer(new EIP1193ProviderAdapter(detail.provider)) } - isAvailable(wallet: Address.Address, chainId: bigint): Promise { + isAvailable(wallet: Address.Checksummed, chainId: bigint): Promise { return this.relayer.isAvailable(wallet, chainId) } feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { return this.relayer.feeOptions(wallet, chainId, calls) } - async relay(to: Address.Address, data: Hex.Hex, chainId: bigint, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { + async relay(to: Address.Checksummed, data: Hex.Hex, chainId: bigint, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { return this.relayer.relay(to, data, chainId) } diff --git a/packages/wallet/core/src/relayer/standard/local.ts b/packages/wallet/core/src/relayer/standard/local.ts index 70dd52121..21c34dfc2 100644 --- a/packages/wallet/core/src/relayer/standard/local.ts +++ b/packages/wallet/core/src/relayer/standard/local.ts @@ -1,9 +1,18 @@ -import { Constants, Payload } from '@0xsequence/wallet-primitives' +import { Address, Constants, Payload } from '@0xsequence/wallet-primitives' import { EIP1193Provider } from 'mipd' -import { AbiFunction, Address, Bytes, Hex, TransactionReceipt } from 'ox' +import { AbiFunction, Bytes, Hex, TransactionReceipt } from 'ox' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' import { IntentPrecondition } from './rpc/relayer.gen.js' -import { decodePrecondition } from '../../preconditions/index.js' +import { + decodePrecondition, + Erc1155ApprovalPrecondition, + Erc1155BalancePrecondition, + Erc20ApprovalPrecondition, + Erc20BalancePrecondition, + Erc721ApprovalPrecondition, + Erc721OwnershipPrecondition, + NativeBalancePrecondition, +} from '../../preconditions/index.js' import { erc20BalanceOf, erc20Allowance, @@ -16,9 +25,9 @@ import { type GenericProviderTransactionReceipt = 'success' | 'failed' | 'unknown' export interface GenericProvider { - sendTransaction(args: { to: Address.Address; data: Hex.Hex }, chainId: bigint): Promise - getBalance(address: Address.Address): Promise - call(args: { to: Address.Address; data: Hex.Hex }): Promise + sendTransaction(args: { to: Address.Checksummed; data: Hex.Hex }, chainId: bigint): Promise + getBalance(address: Address.Checksummed): Promise + call(args: { to: Address.Checksummed; data: Hex.Hex }): Promise getTransactionReceipt(txHash: Hex.Hex, chainId: bigint): Promise } @@ -29,7 +38,7 @@ export class LocalRelayer implements Relayer { constructor(public readonly provider: GenericProvider) {} - isAvailable(_wallet: Address.Address, _chainId: bigint): Promise { + isAvailable(_wallet: Address.Checksummed, _chainId: bigint): Promise { return Promise.resolve(true) } @@ -48,7 +57,7 @@ export class LocalRelayer implements Relayer { } feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { @@ -70,7 +79,7 @@ export class LocalRelayer implements Relayer { } async relay( - to: Address.Address, + to: Address.Checksummed, data: Hex.Hex, chainId: bigint, quote?: FeeQuote, @@ -171,8 +180,8 @@ export class LocalRelayer implements Relayer { switch (decoded.type()) { case 'native-balance': { - const native = decoded as any - const balance = await this.provider.getBalance(native.address.toString()) + const native = decoded as NativeBalancePrecondition + const balance = await this.provider.getBalance(native.address) if (native.min !== undefined && balance < native.min) { return false } @@ -183,10 +192,10 @@ export class LocalRelayer implements Relayer { } case 'erc20-balance': { - const erc20 = decoded as any - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address.toString()]) + const erc20 = decoded as Erc20BalancePrecondition + const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address]) const result = await this.provider.call({ - to: erc20.token.toString(), + to: erc20.token, data, }) const balance = BigInt(result) @@ -200,10 +209,10 @@ export class LocalRelayer implements Relayer { } case 'erc20-approval': { - const erc20 = decoded as any - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address.toString(), erc20.operator.toString()]) + const erc20 = decoded as Erc20ApprovalPrecondition + const data = AbiFunction.encodeData(erc20Allowance, [erc20.address, erc20.operator]) const result = await this.provider.call({ - to: erc20.token.toString(), + to: erc20.token, data, }) const allowance = BigInt(result) @@ -211,33 +220,33 @@ export class LocalRelayer implements Relayer { } case 'erc721-ownership': { - const erc721 = decoded as any + const erc721 = decoded as Erc721OwnershipPrecondition const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) const result = await this.provider.call({ - to: erc721.token.toString(), + to: erc721.token, data, }) - const owner = '0x' + result.slice(26) - const isOwner = owner.toLowerCase() === erc721.address.toString().toLowerCase() + const owner = Address.checksum(`0x${result.slice(26)}`) + const isOwner = Address.isEqual(owner, erc721.address) return erc721.owned === undefined ? isOwner : erc721.owned === isOwner } case 'erc721-approval': { - const erc721 = decoded as any + const erc721 = decoded as Erc721ApprovalPrecondition const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) const result = await this.provider.call({ - to: erc721.token.toString(), + to: erc721.token, data, }) - const approved = '0x' + result.slice(26) - return approved.toLowerCase() === erc721.operator.toString().toLowerCase() + const approved = Address.checksum(`0x${result.slice(26)}`) + return Address.isEqual(approved, erc721.operator) } case 'erc1155-balance': { - const erc1155 = decoded as any - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address.toString(), erc1155.tokenId]) + const erc1155 = decoded as Erc1155BalancePrecondition + const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address, erc1155.tokenId]) const result = await this.provider.call({ - to: erc1155.token.toString(), + to: erc1155.token, data, }) const balance = BigInt(result) @@ -251,13 +260,10 @@ export class LocalRelayer implements Relayer { } case 'erc1155-approval': { - const erc1155 = decoded as any - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [ - erc1155.address.toString(), - erc1155.operator.toString(), - ]) + const erc1155 = decoded as Erc1155ApprovalPrecondition + const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) const result = await this.provider.call({ - to: erc1155.token.toString(), + to: erc1155.token, data, }) return BigInt(result) === 1n @@ -288,8 +294,8 @@ export class EIP1193ProviderAdapter implements GenericProvider { } } - async sendTransaction(args: { to: Address.Address; data: Hex.Hex }, chainId: bigint) { - const accounts: Address.Address[] = await this.provider.request({ method: 'eth_requestAccounts' }) + async sendTransaction(args: { to: Address.Checksummed; data: Hex.Hex }, chainId: bigint) { + const accounts: Address.Checksummed[] = await this.provider.request({ method: 'eth_requestAccounts' }) const from = accounts[0] if (!from) { @@ -313,7 +319,7 @@ export class EIP1193ProviderAdapter implements GenericProvider { return tx } - async getBalance(address: Address.Address) { + async getBalance(address: Address.Checksummed) { const balance = await this.provider.request({ method: 'eth_getBalance', params: [address, 'latest'], @@ -321,7 +327,7 @@ export class EIP1193ProviderAdapter implements GenericProvider { return BigInt(balance) } - async call(args: { to: Address.Address; data: Hex.Hex }) { + async call(args: { to: Address.Checksummed; data: Hex.Hex }) { return await this.provider.request({ method: 'eth_call', params: [args, 'latest'], diff --git a/packages/wallet/core/src/relayer/standard/pk-relayer.ts b/packages/wallet/core/src/relayer/standard/pk-relayer.ts index 4ad5e14e9..8b8a9943d 100644 --- a/packages/wallet/core/src/relayer/standard/pk-relayer.ts +++ b/packages/wallet/core/src/relayer/standard/pk-relayer.ts @@ -1,5 +1,5 @@ -import { Payload, Precondition } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox' +import { Address, Payload, Precondition } from '@0xsequence/wallet-primitives' +import { Hex, Provider, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox' import { LocalRelayer } from './local.js' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' @@ -21,9 +21,8 @@ export class PkRelayer implements Relayer { throw new Error('Provider chain id does not match relayer chain id') } - const oxArgs = { ...args, to: args.to as `0x${string}`, data: args.data as `0x${string}` } // Estimate gas with a safety buffer - const estimatedGas = BigInt(await this.provider.request({ method: 'eth_estimateGas', params: [oxArgs] })) + const estimatedGas = BigInt(await this.provider.request({ method: 'eth_estimateGas', params: [args] })) const safeGasLimit = estimatedGas > 21000n ? (estimatedGas * 12n) / 10n : 50000n // Get base fee and priority fee @@ -51,8 +50,8 @@ export class PkRelayer implements Relayer { chainId: Number(chainId), type: 'eip1559', from: relayerAddress, - to: oxArgs.to, - data: oxArgs.data, + to: args.to, + data: args.data, gas: safeGasLimit, maxFeePerGas: maxFeePerGas, maxPriorityFeePerGas: priorityFee, @@ -72,17 +71,14 @@ export class PkRelayer implements Relayer { }) return tx }, - getBalance: async (address: string): Promise => { + getBalance: async (address): Promise => { const balanceHex = await this.provider.request({ method: 'eth_getBalance', - params: [address as Address.Address, 'latest'], + params: [address, 'latest'], }) return BigInt(balanceHex) }, - call: async (args: { to: string; data: string }): Promise => { - const callArgs = { to: args.to as `0x${string}`, data: args.data as `0x${string}` } - return await this.provider.request({ method: 'eth_call', params: [callArgs, 'latest'] }) - }, + call: (args) => this.provider.request({ method: 'eth_call', params: [args, 'latest'] }), getTransactionReceipt: async (txHash: string, chainId: bigint) => { Hex.assert(txHash) @@ -101,20 +97,20 @@ export class PkRelayer implements Relayer { }) } - async isAvailable(_wallet: Address.Address, chainId: bigint): Promise { + async isAvailable(_wallet: Address.Checksummed, chainId: bigint): Promise { const providerChainId = BigInt(await this.provider.request({ method: 'eth_chainId' })) return providerChainId === chainId } feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { return this.relayer.feeOptions(wallet, chainId, calls) } - async relay(to: Address.Address, data: Hex.Hex, chainId: bigint, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { + async relay(to: Address.Checksummed, data: Hex.Hex, chainId: bigint, _?: FeeQuote): Promise<{ opHash: Hex.Hex }> { const providerChainId = BigInt(await this.provider.request({ method: 'eth_chainId' })) if (providerChainId !== chainId) { throw new Error('Provider chain id does not match relayer chain id') diff --git a/packages/wallet/core/src/relayer/standard/rpc/index.ts b/packages/wallet/core/src/relayer/standard/rpc/index.ts index 963ca2455..2d3ecf6c4 100644 --- a/packages/wallet/core/src/relayer/standard/rpc/index.ts +++ b/packages/wallet/core/src/relayer/standard/rpc/index.ts @@ -6,8 +6,8 @@ import { IntentPrecondition, } from './relayer.gen.js' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../../relayer.js' -import { Address, Hex, Bytes, AbiFunction } from 'ox' -import { Constants, Payload } from '@0xsequence/wallet-primitives' +import { Hex, Bytes, AbiFunction } from 'ox' +import { Address, Constants, Payload } from '@0xsequence/wallet-primitives' import { ETHTxnStatus, FeeToken as RpcFeeToken } from './relayer.gen.js' import { decodePrecondition } from '../../../preconditions/index.js' import { @@ -62,12 +62,12 @@ export class RpcRelayer implements Relayer { }) } - isAvailable(_wallet: Address.Address, chainId: bigint): Promise { + isAvailable(_wallet: Address.Checksummed, chainId: bigint): Promise { return Promise.resolve(BigInt(this.chainId) === chainId) } async feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { @@ -81,16 +81,18 @@ export class RpcRelayer implements Relayer { data: Bytes.toHex(data), }) - const quote = result.quote ? ({ _tag: 'FeeQuote', _quote: result.quote } as FeeQuote) : undefined - const options = result.options.map((option) => ({ - token: { - ...option.token, - contractAddress: this.mapRpcFeeTokenToAddress(option.token), - }, - to: option.to, - value: option.value, - gasLimit: option.gasLimit, - })) + const quote: FeeQuote | undefined = result.quote ? { _tag: 'FeeQuote', _quote: result.quote } : undefined + const options = result.options.map((option) => { + return { + token: { + ...option.token, + contractAddress: this.mapRpcFeeTokenToAddress(option.token), + }, + to: Address.checksum(option.to), + value: option.value, + gasLimit: option.gasLimit, + } + }) return { options, quote } } catch (e) { @@ -100,8 +102,8 @@ export class RpcRelayer implements Relayer { } async sendMetaTxn( - walletAddress: Address.Address, - to: Address.Address, + walletAddress: Address.Checksummed, + to: Address.Checksummed, data: Hex.Hex, chainId: bigint, quote?: FeeQuote, @@ -129,7 +131,7 @@ export class RpcRelayer implements Relayer { } async relay( - to: Address.Address, + to: Address.Checksummed, data: Hex.Hex, chainId: bigint, quote?: FeeQuote, @@ -172,7 +174,7 @@ export class RpcRelayer implements Relayer { return { status: 'unknown' } } - switch (receipt.status as ETHTxnStatus) { + switch (receipt.status) { case ETHTxnStatus.QUEUED: case ETHTxnStatus.PENDING_PRECONDITION: case ETHTxnStatus.SENT: @@ -210,7 +212,7 @@ export class RpcRelayer implements Relayer { case 'native-balance': { const native = decoded as any try { - const balance = await this.provider.getBalance({ address: native.address.toString() as `0x${string}` }) + const balance = await this.provider.getBalance({ address: native.address }) const minWei = native.min !== undefined ? BigInt(native.min) : undefined const maxWei = native.max !== undefined ? BigInt(native.max) : undefined @@ -235,12 +237,12 @@ export class RpcRelayer implements Relayer { case 'erc20-balance': { const erc20 = decoded as any try { - const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address.toString()]) - const result = await this.provider.call({ - to: erc20.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const balance = BigInt(result.toString()) + const data = AbiFunction.encodeData(erc20BalanceOf, [erc20.address]) + const result = await this.provider.call({ to: erc20.token, data }) + if (result.data === undefined) { + throw new Error('no response for erc-20 balance query') + } + const balance = BigInt(result.data) const minWei = erc20.min !== undefined ? BigInt(erc20.min) : undefined const maxWei = erc20.max !== undefined ? BigInt(erc20.max) : undefined @@ -264,12 +266,12 @@ export class RpcRelayer implements Relayer { case 'erc20-approval': { const erc20 = decoded as any try { - const data = AbiFunction.encodeData(erc20Allowance, [erc20.address.toString(), erc20.operator.toString()]) - const result = await this.provider.call({ - to: erc20.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const allowance = BigInt(result.toString()) + const data = AbiFunction.encodeData(erc20Allowance, [erc20.address, erc20.operator]) + const result = await this.provider.call({ to: erc20.token, data }) + if (result.data === undefined) { + throw new Error('no response for erc-20 allowance query') + } + const allowance = BigInt(result.data) const minAllowance = BigInt(erc20.min) return allowance >= minAllowance } catch (error) { @@ -282,13 +284,13 @@ export class RpcRelayer implements Relayer { const erc721 = decoded as any try { const data = AbiFunction.encodeData(erc721OwnerOf, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const resultHex = result.toString() as `0x${string}` - const owner = resultHex.slice(-40) - const isOwner = owner.toLowerCase() === erc721.address.toString().slice(2).toLowerCase() + const result = await this.provider.call({ to: erc721.token, data }) + const resultHex = result.data + if (resultHex === undefined) { + throw new Error('no response for erc-721 ownership query') + } + const owner = Address.checksum(`0x${resultHex.slice(-40)}`) + const isOwner = Address.isEqual(owner, erc721.address) const expectedOwnership = erc721.owned !== undefined ? erc721.owned : true return isOwner === expectedOwnership } catch (error) { @@ -301,13 +303,13 @@ export class RpcRelayer implements Relayer { const erc721 = decoded as any try { const data = AbiFunction.encodeData(erc721GetApproved, [erc721.tokenId]) - const result = await this.provider.call({ - to: erc721.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const resultHex = result.toString() as `0x${string}` - const approved = resultHex.slice(-40) - return approved.toLowerCase() === erc721.operator.toString().slice(2).toLowerCase() + const result = await this.provider.call({ to: erc721.token, data }) + const resultHex = result.data + if (resultHex === undefined) { + throw new Error('no response for erc-721 approval query') + } + const approved = Address.checksum(resultHex.slice(-40)) + return Address.isEqual(approved, erc721.operator) } catch (error) { console.error('Error checking ERC721 approval:', error) return false @@ -317,12 +319,12 @@ export class RpcRelayer implements Relayer { case 'erc1155-balance': { const erc1155 = decoded as any try { - const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address.toString(), erc1155.tokenId]) - const result = await this.provider.call({ - to: erc1155.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - const balance = BigInt(result.toString()) + const data = AbiFunction.encodeData(erc1155BalanceOf, [erc1155.address, erc1155.tokenId]) + const result = await this.provider.call({ to: erc1155.token, data }) + if (result.data === undefined) { + throw new Error('no response for erc-1155 balance query') + } + const balance = BigInt(result.data) const minWei = erc1155.min !== undefined ? BigInt(erc1155.min) : undefined const maxWei = erc1155.max !== undefined ? BigInt(erc1155.max) : undefined @@ -346,15 +348,12 @@ export class RpcRelayer implements Relayer { case 'erc1155-approval': { const erc1155 = decoded as any try { - const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [ - erc1155.address.toString(), - erc1155.operator.toString(), - ]) - const result = await this.provider.call({ - to: erc1155.token.toString() as `0x${string}`, - data: data as `0x${string}`, - }) - return BigInt(result.toString()) === 1n + const data = AbiFunction.encodeData(erc1155IsApprovedForAll, [erc1155.address, erc1155.operator]) + const result = await this.provider.call({ to: erc1155.token, data }) + if (result.data === undefined) { + throw new Error('no response for erc-1155 approval query') + } + return BigInt(result.data) === 1n } catch (error) { console.error('Error checking ERC1155 approval:', error) return false @@ -366,9 +365,9 @@ export class RpcRelayer implements Relayer { } } - private mapRpcFeeTokenToAddress(rpcToken: RpcFeeToken): Address.Address { + private mapRpcFeeTokenToAddress(rpcToken: RpcFeeToken): Address.Checksummed { if (rpcToken.type === FeeTokenType.ERC20_TOKEN && rpcToken.contractAddress) { - return Address.from(rpcToken.contractAddress) + return Address.checksum(rpcToken.contractAddress) } return Constants.ZeroAddress // Default to zero address for native token or unsupported types } diff --git a/packages/wallet/core/src/relayer/standard/sequence.ts b/packages/wallet/core/src/relayer/standard/sequence.ts index 77d26c764..8d9382b99 100644 --- a/packages/wallet/core/src/relayer/standard/sequence.ts +++ b/packages/wallet/core/src/relayer/standard/sequence.ts @@ -1,6 +1,6 @@ import { ETHTxnStatus, IntentPrecondition, Relayer as Service } from '@0xsequence/relayer' -import { Payload } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Bytes, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { AbiFunction, Bytes, Hex } from 'ox' import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../relayer.js' export class SequenceRelayer implements Relayer { @@ -14,12 +14,12 @@ export class SequenceRelayer implements Relayer { this.service = new Service(host, fetch) } - async isAvailable(_wallet: Address.Address, _chainId: bigint): Promise { + async isAvailable(_wallet: Address.Checksummed, _chainId: bigint): Promise { return true } async feeOptions( - wallet: Address.Address, + wallet: Address.Checksummed, _chainId: bigint, calls: Payload.Call[], ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { @@ -32,7 +32,7 @@ export class SequenceRelayer implements Relayer { const { options, quote } = await this.service.feeOptions({ wallet, to, data }) return { - options, + options: options.map((option) => ({ ...option, to: Address.checksum(option.to) })), quote: quote ? { _tag: 'FeeQuote', _quote: quote } : undefined, } } @@ -42,7 +42,12 @@ export class SequenceRelayer implements Relayer { return false } - async relay(to: Address.Address, data: Hex.Hex, _chainId: bigint, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> { + async relay( + to: Address.Checksummed, + data: Hex.Hex, + _chainId: bigint, + quote?: FeeQuote, + ): Promise<{ opHash: Hex.Hex }> { const walletAddress = to // TODO: pass wallet address or stop requiring it const { txnHash } = await this.service.sendMetaTxn({ diff --git a/packages/wallet/core/src/signers/index.ts b/packages/wallet/core/src/signers/index.ts index 61eb55346..5423654fa 100644 --- a/packages/wallet/core/src/signers/index.ts +++ b/packages/wallet/core/src/signers/index.ts @@ -1,5 +1,5 @@ -import { Config, Payload, Signature } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Config, Payload, Signature } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import * as State from '../state/index.js' export * as Pk from './pk/index.js' @@ -8,21 +8,21 @@ export * as Session from './session/index.js' export * from './session-manager.js' export interface Signer { - readonly address: MaybePromise + readonly address: MaybePromise sign: ( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, ) => Config.SignerSignature } export interface SapientSigner { - readonly address: MaybePromise + readonly address: MaybePromise readonly imageHash: MaybePromise signSapient: ( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, imageHash: Hex.Hex, @@ -30,7 +30,7 @@ export interface SapientSigner { } export interface Witnessable { - witness: (stateWriter: State.Writer, wallet: Address.Address, extra?: Object) => Promise + witness: (stateWriter: State.Writer, wallet: Address.Checksummed, extra?: Object) => Promise } type MaybePromise = T | Promise diff --git a/packages/wallet/core/src/signers/passkey.ts b/packages/wallet/core/src/signers/passkey.ts index 8de63fdb7..35970849f 100644 --- a/packages/wallet/core/src/signers/passkey.ts +++ b/packages/wallet/core/src/signers/passkey.ts @@ -1,5 +1,5 @@ -import { Hex, Bytes, Address, P256, Hash } from 'ox' -import { Payload, Extensions } from '@0xsequence/wallet-primitives' +import { Hex, Bytes, P256, Hash } from 'ox' +import { Address, Payload, Extensions } from '@0xsequence/wallet-primitives' import type { Signature as SignatureTypes } from '@0xsequence/wallet-primitives' import { WebAuthnP256 } from 'ox' import { State } from '../index.js' @@ -22,7 +22,7 @@ export type CreaetePasskeyOptions = { export type WitnessMessage = { action: 'consent-to-be-part-of-wallet' - wallet: Address.Address + wallet: Address.Checksummed publicKey: Extensions.Passkeys.PublicKey timestamp: number metadata?: Extensions.Passkeys.PasskeyMetadata @@ -41,7 +41,7 @@ export class Passkey implements SapientSigner, Witnessable { public readonly credentialId: string public readonly publicKey: Extensions.Passkeys.PublicKey - public readonly address: Address.Address + public readonly address: Address.Checksummed public readonly imageHash: Hex.Hex public readonly embedMetadata: boolean public readonly metadata?: Extensions.Passkeys.PasskeyMetadata @@ -58,7 +58,7 @@ export class Passkey implements SapientSigner, Witnessable { static async loadFromWitness( stateReader: State.Reader, extensions: Pick, - wallet: Address.Address, + wallet: Address.Checksummed, imageHash: Hex.Hex, ) { // In the witness we will find the public key, and may find the credential id @@ -193,10 +193,7 @@ export class Passkey implements SapientSigner, Witnessable { const signers = await Promise.all( imageHashes.map(async (imageHash) => { const wallets = await stateReader.getWalletsForSapient(extensions.passkeys, imageHash) - return Object.keys(wallets).map((wallet) => ({ - wallet: Address.from(wallet), - imageHash, - })) + return Object.keys(wallets).map((wallet) => ({ wallet: Address.checksum(wallet), imageHash })) }), ) @@ -222,7 +219,7 @@ export class Passkey implements SapientSigner, Witnessable { } async signSapient( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, imageHash: Hex.Hex, @@ -260,7 +257,7 @@ export class Passkey implements SapientSigner, Witnessable { } } - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise { + async witness(stateWriter: State.Writer, wallet: Address.Checksummed, extra?: Object): Promise { const payload = Payload.fromMessage( Hex.fromString( JSON.stringify({ diff --git a/packages/wallet/core/src/signers/pk/encrypted.ts b/packages/wallet/core/src/signers/pk/encrypted.ts index becc2b41a..69ebb7493 100644 --- a/packages/wallet/core/src/signers/pk/encrypted.ts +++ b/packages/wallet/core/src/signers/pk/encrypted.ts @@ -1,11 +1,12 @@ -import { Hex, Address, PublicKey, Secp256k1, Bytes } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex, PublicKey, Secp256k1, Bytes } from 'ox' import { PkStore } from './index.js' export interface EncryptedData { iv: Uint8Array data: ArrayBuffer keyPointer: string - address: Address.Address + address: Address.Checksummed publicKey: PublicKey.PublicKey } @@ -21,8 +22,8 @@ export class EncryptedPksDb { this.tableName = tableName } - private computeDbKey(address: Address.Address): string { - return `pk_${address.toLowerCase()}` + private computeDbKey(address: Address.Checksummed): `pk_${Address.Checksummed}` { + return `pk_${address}` } private openDB(): Promise { @@ -105,23 +106,23 @@ export class EncryptedPksDb { return encrypted } - async getEncryptedEntry(address: Address.Address): Promise { + async getEncryptedEntry(address: Address.Checksummed): Promise { const dbKey = this.computeDbKey(address) return this.getData(dbKey) } - async getEncryptedPkStore(address: Address.Address): Promise { + async getEncryptedPkStore(address: Address.Checksummed): Promise { const entry = await this.getEncryptedEntry(address) if (!entry) return return new EncryptedPkStore(entry) } - async listAddresses(): Promise { + async listAddresses(): Promise { const allEntries = await this.getAllData() return allEntries.map((entry) => entry.address) } - async remove(address: Address.Address) { + async remove(address: Address.Checksummed) { const dbKey = this.computeDbKey(address) await this.putData(dbKey, undefined) const keyPointer = this.localStorageKeyPrefix + address @@ -132,7 +133,7 @@ export class EncryptedPksDb { export class EncryptedPkStore implements PkStore { constructor(private readonly encrypted: EncryptedData) {} - address(): Address.Address { + address(): Address.Checksummed { return this.encrypted.address } @@ -151,7 +152,8 @@ export class EncryptedPkStore implements PkStore { this.encrypted.data, ) const decoder = new TextDecoder() - const privateKey = decoder.decode(decryptedBuffer) as Hex.Hex + const privateKey = decoder.decode(decryptedBuffer) + Hex.assert(privateKey) return Secp256k1.sign({ payload: digest, privateKey }) } } diff --git a/packages/wallet/core/src/signers/pk/index.ts b/packages/wallet/core/src/signers/pk/index.ts index b7dc1087c..05ce75b1e 100644 --- a/packages/wallet/core/src/signers/pk/index.ts +++ b/packages/wallet/core/src/signers/pk/index.ts @@ -1,11 +1,11 @@ import type { Payload as PayloadTypes, Signature as SignatureTypes } from '@0xsequence/wallet-primitives' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, PublicKey, Secp256k1 } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Bytes, Hex, PublicKey, Secp256k1 } from 'ox' import { Signer as SignerInterface, Witnessable } from '../index.js' import { State } from '../../index.js' export interface PkStore { - address(): Address.Address + address(): Address.Checksummed publicKey(): PublicKey.PublicKey signDigest(digest: Bytes.Bytes): Promise<{ r: bigint; s: bigint; yParity: number }> } @@ -13,7 +13,7 @@ export interface PkStore { export class MemoryPkStore implements PkStore { constructor(private readonly privateKey: Hex.Hex) {} - address(): Address.Address { + address(): Address.Checksummed { return Address.fromPublicKey(this.publicKey()) } @@ -29,7 +29,7 @@ export class MemoryPkStore implements PkStore { export class Pk implements SignerInterface, Witnessable { private readonly privateKey: PkStore - public readonly address: Address.Address + public readonly address: Address.Checksummed public readonly pubKey: PublicKey.PublicKey constructor(privateKey: Hex.Hex | PkStore) { @@ -39,7 +39,7 @@ export class Pk implements SignerInterface, Witnessable { } async sign( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: PayloadTypes.Parented, ): Promise { @@ -52,7 +52,7 @@ export class Pk implements SignerInterface, Witnessable { return { ...signature, type: 'hash' } } - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise { + async witness(stateWriter: State.Writer, wallet: Address.Checksummed, extra?: Object): Promise { const payload = Payload.fromMessage( Hex.fromString( JSON.stringify({ diff --git a/packages/wallet/core/src/signers/session-manager.ts b/packages/wallet/core/src/signers/session-manager.ts index 301d54b05..5add71dfd 100644 --- a/packages/wallet/core/src/signers/session-manager.ts +++ b/packages/wallet/core/src/signers/session-manager.ts @@ -1,4 +1,5 @@ import { + Address, Config, Constants, Payload, @@ -6,14 +7,14 @@ import { SessionSignature, Signature as SignatureTypes, } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Hex, Provider } from 'ox' +import { AbiFunction, Hex, Provider } from 'ox' import * as State from '../state/index.js' import { Wallet } from '../wallet.js' import { SapientSigner } from './index.js' import { Explicit, Implicit, isExplicitSessionSigner, SessionSigner, UsageLimit } from './session/index.js' export type SessionManagerOptions = { - sessionManagerAddress: Address.Address + sessionManagerAddress: Address.Checksummed stateProvider?: State.Provider implicitSigners?: Implicit[] explicitSigners?: Explicit[] @@ -22,7 +23,7 @@ export type SessionManagerOptions = { export class SessionManager implements SapientSigner { public readonly stateProvider: State.Provider - public readonly address: Address.Address + public readonly address: Address.Checksummed private readonly _implicitSigners: Implicit[] private readonly _explicitSigners: Explicit[] @@ -101,7 +102,11 @@ export class SessionManager implements SapientSigner { }) } - async findSignersForCalls(wallet: Address.Address, chainId: bigint, calls: Payload.Call[]): Promise { + async findSignersForCalls( + wallet: Address.Checksummed, + chainId: bigint, + calls: Payload.Call[], + ): Promise { // Only use signers that match the topology const topology = await this.topology const identitySigner = SessionConfig.getIdentitySigner(topology) @@ -152,7 +157,7 @@ export class SessionManager implements SapientSigner { } async prepareIncrement( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], ): Promise { @@ -207,7 +212,7 @@ export class SessionManager implements SapientSigner { } async signSapient( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, imageHash: Hex.Hex, @@ -258,8 +263,8 @@ export class SessionManager implements SapientSigner { } // Encode the signature - const explicitSigners: Address.Address[] = [] - const implicitSigners: Address.Address[] = [] + const explicitSigners: Address.Checksummed[] = [] + const implicitSigners: Address.Checksummed[] = [] await Promise.all( signers.map(async (signer) => { if (isExplicitSessionSigner(signer)) { @@ -284,7 +289,7 @@ export class SessionManager implements SapientSigner { } async isValidSapientSignature( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signature: SignatureTypes.SignatureOfSapientSignerLeaf, diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/packages/wallet/core/src/signers/session/explicit.ts index 56d7f1c4c..19cd8fa93 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/packages/wallet/core/src/signers/session/explicit.ts @@ -1,16 +1,16 @@ -import { Payload, Permission, SessionSignature, Constants } from '@0xsequence/wallet-primitives' -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider } from 'ox' +import { Address, Payload, Permission, SessionSignature, Constants } from '@0xsequence/wallet-primitives' +import { AbiFunction, AbiParameters, Bytes, Hash, Hex, Provider } from 'ox' import { MemoryPkStore, PkStore } from '../pk/index.js' import { ExplicitSessionSigner, UsageLimit } from './session.js' export type ExplicitParams = Omit -const VALUE_TRACKING_ADDRESS: Address.Address = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' +const VALUE_TRACKING_ADDRESS = Address.checksum('0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') export class Explicit implements ExplicitSessionSigner { private readonly _privateKey: PkStore - public readonly address: Address.Address + public readonly address: Address.Checksummed public readonly sessionPermissions: Permission.SessionPermissions constructor(privateKey: Hex.Hex | PkStore, sessionPermissions: ExplicitParams) { @@ -23,10 +23,10 @@ export class Explicit implements ExplicitSessionSigner { } async findSupportedPermission( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { if (this.sessionPermissions.chainId !== 0n && this.sessionPermissions.chainId !== chainId) { @@ -97,8 +97,8 @@ export class Explicit implements ExplicitSessionSigner { async validatePermission( permission: Permission.Permission, call: Payload.Call, - wallet: Address.Address, - sessionManagerAddress: Address.Address, + wallet: Address.Checksummed, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { if (!Address.isEqual(permission.target, call.to)) { @@ -155,10 +155,10 @@ export class Explicit implements ExplicitSessionSigner { } async supportedCall( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { if ( @@ -177,14 +177,14 @@ export class Explicit implements ExplicitSessionSigner { } async signCall( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, nonce: { space: bigint nonce: bigint }, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { let permissionIndex: number @@ -218,8 +218,8 @@ export class Explicit implements ExplicitSessionSigner { } private async readCurrentUsageLimit( - wallet: Address.Address, - sessionManagerAddress: Address.Address, + wallet: Address.Checksummed, + sessionManagerAddress: Address.Checksummed, usageHash: Hex.Hex, provider: Provider.Provider, ): Promise { @@ -242,10 +242,10 @@ export class Explicit implements ExplicitSessionSigner { } async prepareIncrements( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { const increments: { usageHash: Hex.Hex; increment: bigint }[] = [] diff --git a/packages/wallet/core/src/signers/session/implicit.ts b/packages/wallet/core/src/signers/session/implicit.ts index 0bd2f0172..1d6206ee4 100644 --- a/packages/wallet/core/src/signers/session/implicit.ts +++ b/packages/wallet/core/src/signers/session/implicit.ts @@ -1,5 +1,11 @@ -import { Attestation, Payload, Signature as SequenceSignature, SessionSignature } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Bytes, Hex, Provider, Secp256k1, Signature } from 'ox' +import { + Address, + Attestation, + Payload, + Signature as SequenceSignature, + SessionSignature, +} from '@0xsequence/wallet-primitives' +import { AbiFunction, Bytes, Hex, Provider, Secp256k1, Signature } from 'ox' import { MemoryPkStore, PkStore } from '../pk/index.js' import { SessionSigner } from './session.js' @@ -8,13 +14,13 @@ export type AttestationParams = Omit export class Implicit implements SessionSigner { private readonly _privateKey: PkStore private readonly _identitySignature: SequenceSignature.RSY - public readonly address: Address.Address + public readonly address: Address.Checksummed constructor( privateKey: Hex.Hex | PkStore, private readonly _attestation: Attestation.Attestation, identitySignature: SequenceSignature.RSY | Hex.Hex, - private readonly _sessionManager: Address.Address, + private readonly _sessionManager: Address.Checksummed, ) { this._privateKey = typeof privateKey === 'string' ? new MemoryPkStore(privateKey) : privateKey this.address = this._privateKey.address() @@ -28,7 +34,7 @@ export class Implicit implements SessionSigner { typeof identitySignature === 'string' ? Signature.fromHex(identitySignature) : identitySignature } - get identitySigner(): Address.Address { + get identitySigner(): Address.Checksummed { // Recover identity signer from attestions and identity signature const attestationHash = Attestation.hash(this._attestation) const identityPubKey = Secp256k1.recoverPublicKey({ payload: attestationHash, signature: this._identitySignature }) @@ -36,10 +42,10 @@ export class Implicit implements SessionSigner { } async supportedCall( - wallet: Address.Address, + wallet: Address.Checksummed, _chainId: bigint, call: Payload.Call, - _sessionManagerAddress: Address.Address, + _sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { if (!provider) { @@ -83,14 +89,14 @@ export class Implicit implements SessionSigner { } async signCall( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, nonce: { space: bigint nonce: bigint }, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ): Promise { const isSupported = await this.supportedCall(wallet, chainId, call, sessionManagerAddress, provider) diff --git a/packages/wallet/core/src/signers/session/session.ts b/packages/wallet/core/src/signers/session/session.ts index a720f529a..d28d7728a 100644 --- a/packages/wallet/core/src/signers/session/session.ts +++ b/packages/wallet/core/src/signers/session/session.ts @@ -1,28 +1,28 @@ -import { Payload, SessionSignature } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider } from 'ox' +import { Address, Payload, SessionSignature } from '@0xsequence/wallet-primitives' +import { Hex, Provider } from 'ox' export interface SessionSigner { - address: Address.Address | Promise + address: Address.Checksummed | Promise /// Check if the signer supports the call supportedCall: ( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ) => Promise /// Sign the call. Will throw if the call is not supported. signCall: ( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, call: Payload.Call, nonce: { space: bigint nonce: bigint }, - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider?: Provider.Provider, ) => Promise } @@ -34,10 +34,10 @@ export type UsageLimit = { export interface ExplicitSessionSigner extends SessionSigner { prepareIncrements: ( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, calls: Payload.Call[], - sessionManagerAddress: Address.Address, + sessionManagerAddress: Address.Checksummed, provider: Provider.Provider, ) => Promise } diff --git a/packages/wallet/core/src/state/cached.ts b/packages/wallet/core/src/state/cached.ts index 62af81a34..aef2eecc5 100644 --- a/packages/wallet/core/src/state/cached.ts +++ b/packages/wallet/core/src/state/cached.ts @@ -1,7 +1,6 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { MaybePromise, Provider } from './index.js' -import { Config, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives' -import { normalizeAddressKeys } from './utils.js' +import { Address, Config, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives' export class Cached implements Provider { constructor( @@ -25,7 +24,7 @@ export class Cached implements Provider { return config } - async getDeploy(wallet: Address.Address): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { + async getDeploy(wallet: Address.Checksummed): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { const cached = await this.args.cache.getDeploy(wallet) if (cached) { return cached @@ -37,24 +36,23 @@ export class Cached implements Provider { return deploy } - async getWallets(signer: Address.Address): Promise<{ - [wallet: Address.Address]: { + async getWallets(signer: Address.Checksummed): Promise<{ + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSignerLeaf } }> { // Get both from cache and source - const cached = normalizeAddressKeys(await this.args.cache.getWallets(signer)) - const source = normalizeAddressKeys(await this.args.source.getWallets(signer)) + const cached = await this.args.cache.getWallets(signer) + const source = await this.args.source.getWallets(signer) // Merge and deduplicate const deduplicated = { ...cached, ...source } // Sync values to source that are not in cache, and vice versa - for (const [walletAddress, data] of Object.entries(deduplicated)) { - Address.assert(walletAddress) - + for (const [wallet, data] of Object.entries(deduplicated)) { + const walletAddress = Address.checksum(wallet) if (!source[walletAddress]) { await this.args.source.saveWitnesses(walletAddress, data.chainId, data.payload, { type: 'unrecovered-signer', @@ -75,10 +73,10 @@ export class Cached implements Provider { } async getWalletsForSapient( - signer: Address.Address, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise<{ - [wallet: Address.Address]: { + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSapientSignerLeaf @@ -91,7 +89,7 @@ export class Cached implements Provider { // Sync values to source that are not in cache, and vice versa for (const [wallet, data] of Object.entries(deduplicated)) { - const walletAddress = Address.from(wallet) + const walletAddress = Address.checksum(wallet) if (!source[walletAddress]) { await this.args.source.saveWitnesses(walletAddress, data.chainId, data.payload, { type: 'unrecovered-signer', @@ -112,8 +110,8 @@ export class Cached implements Provider { } async getWitnessFor( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, ): Promise<{ chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | undefined> { const cached = await this.args.cache.getWitnessFor(wallet, signer) if (cached) { @@ -133,8 +131,8 @@ export class Cached implements Provider { } async getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise< { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf } | undefined @@ -155,7 +153,7 @@ export class Cached implements Provider { } async getConfigurationUpdates( - wallet: Address.Address, + wallet: Address.Checksummed, fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise> { @@ -181,7 +179,7 @@ export class Cached implements Provider { } saveWitnesses( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signatures: Signature.RawTopology, @@ -190,7 +188,7 @@ export class Cached implements Provider { } saveUpdate( - wallet: Address.Address, + wallet: Address.Checksummed, configuration: Config.Config, signature: Signature.RawSignature, ): MaybePromise { @@ -213,7 +211,7 @@ export class Cached implements Provider { | { chainId: bigint payload: Payload.Parented - wallet: Address.Address + wallet: Address.Checksummed } | undefined > { @@ -229,7 +227,7 @@ export class Cached implements Provider { return source } - savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: bigint): MaybePromise { + savePayload(wallet: Address.Checksummed, payload: Payload.Parented, chainId: bigint): MaybePromise { return this.args.source.savePayload(wallet, payload, chainId) } } diff --git a/packages/wallet/core/src/state/index.ts b/packages/wallet/core/src/state/index.ts index 2ec3db9d0..ab6786586 100644 --- a/packages/wallet/core/src/state/index.ts +++ b/packages/wallet/core/src/state/index.ts @@ -1,15 +1,15 @@ -import { Address, Hex } from 'ox' -import { Context, Config, Payload, Signature, GenericTree } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' +import { Address, Context, Config, Payload, Signature, GenericTree } from '@0xsequence/wallet-primitives' export type Provider = Reader & Writer export interface Reader { getConfiguration(imageHash: Hex.Hex): MaybePromise - getDeploy(wallet: Address.Address): MaybePromise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> + getDeploy(wallet: Address.Checksummed): MaybePromise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> - getWallets(signer: Address.Address): MaybePromise<{ - [wallet: Address.Address]: { + getWallets(signer: Address.Checksummed): MaybePromise<{ + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSignerLeaf @@ -17,10 +17,10 @@ export interface Reader { }> getWalletsForSapient( - signer: Address.Address, + signer: Address.Checksummed, imageHash: Hex.Hex, ): MaybePromise<{ - [wallet: Address.Address]: { + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSapientSignerLeaf @@ -28,22 +28,22 @@ export interface Reader { }> getWitnessFor( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, ): MaybePromise< { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | undefined > getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, imageHash: Hex.Hex, ): MaybePromise< { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf } | undefined > getConfigurationUpdates( - wallet: Address.Address, + wallet: Address.Checksummed, fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): MaybePromise> @@ -51,21 +51,21 @@ export interface Reader { getTree(rootHash: Hex.Hex): MaybePromise getPayload( opHash: Hex.Hex, - ): MaybePromise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Address } | undefined> + ): MaybePromise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Checksummed } | undefined> } export interface Writer { saveWallet(deployConfiguration: Config.Config, context: Context.Context): MaybePromise saveWitnesses( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signatures: Signature.RawTopology, ): MaybePromise saveUpdate( - wallet: Address.Address, + wallet: Address.Checksummed, configuration: Config.Config, signature: Signature.RawSignature, ): MaybePromise @@ -74,7 +74,7 @@ export interface Writer { saveConfiguration(config: Config.Config): MaybePromise saveDeploy(imageHash: Hex.Hex, context: Context.Context): MaybePromise - savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: bigint): MaybePromise + savePayload(wallet: Address.Checksummed, payload: Payload.Parented, chainId: bigint): MaybePromise } export type MaybePromise = T | Promise diff --git a/packages/wallet/core/src/state/local/index.ts b/packages/wallet/core/src/state/local/index.ts index f76f858b5..46477cbc8 100644 --- a/packages/wallet/core/src/state/local/index.ts +++ b/packages/wallet/core/src/state/local/index.ts @@ -1,16 +1,7 @@ -import { - Context, - Payload, - Signature, - Config, - Address as SequenceAddress, - Extensions, - GenericTree, -} from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, PersonalMessage, Secp256k1 } from 'ox' +import { Address, Config, Context, Extensions, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives' +import { Bytes, Hex, PersonalMessage, Secp256k1 } from 'ox' import { Provider as ProviderInterface } from '../index.js' import { MemoryStore } from './memory.js' -import { normalizeAddressKeys } from '../utils.js' export interface Store { // top level configurations store @@ -19,40 +10,40 @@ export interface Store { // counterfactual wallets loadCounterfactualWallet: ( - wallet: Address.Address, + wallet: Address.Checksummed, ) => Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> - saveCounterfactualWallet: (wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context) => Promise + saveCounterfactualWallet: (wallet: Address.Checksummed, imageHash: Hex.Hex, context: Context.Context) => Promise // payloads loadPayloadOfSubdigest: ( subdigest: Hex.Hex, - ) => Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Address } | undefined> + ) => Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed } | undefined> savePayloadOfSubdigest: ( subdigest: Hex.Hex, - payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Address }, + payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed }, ) => Promise // signatures - loadSubdigestsOfSigner: (signer: Address.Address) => Promise + loadSubdigestsOfSigner: (signer: Address.Checksummed) => Promise loadSignatureOfSubdigest: ( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, ) => Promise saveSignatureOfSubdigest: ( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf, ) => Promise // sapient signatures - loadSubdigestsOfSapientSigner: (signer: Address.Address, imageHash: Hex.Hex) => Promise + loadSubdigestsOfSapientSigner: (signer: Address.Checksummed, imageHash: Hex.Hex) => Promise loadSapientSignatureOfSubdigest: ( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, ) => Promise saveSapientSignatureOfSubdigest: ( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf, @@ -77,7 +68,11 @@ export class Provider implements ProviderInterface { // Save both the configuration and the deploy hash await this.saveConfig(deployConfiguration) const imageHash = Config.hashConfiguration(deployConfiguration) - await this.saveCounterfactualWallet(SequenceAddress.from(imageHash, context), Hex.fromBytes(imageHash), context) + await this.saveCounterfactualWallet( + Address.fromDeployConfiguration(imageHash, context), + Hex.fromBytes(imageHash), + context, + ) } async saveConfig(config: Config.Config): Promise { @@ -92,23 +87,23 @@ export class Provider implements ProviderInterface { } saveCounterfactualWallet( - wallet: Address.Address, + wallet: Address.Checksummed, imageHash: Hex.Hex, context: Context.Context, ): void | Promise { this.store.saveCounterfactualWallet(wallet, imageHash, context) } - getDeploy(wallet: Address.Address): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { + getDeploy(wallet: Address.Checksummed): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { return this.store.loadCounterfactualWallet(wallet) } private async getWalletsGeneric( subdigests: Hex.Hex[], loadSignatureFn: (subdigest: Hex.Hex) => Promise, - ): Promise> { + ): Promise> { const payloads = await Promise.all(subdigests.map((sd) => this.store.loadPayloadOfSubdigest(sd))) - const response: Record = {} + const response: Record = {} for (const payload of payloads) { if (!payload) { @@ -139,27 +134,23 @@ export class Provider implements ProviderInterface { return response } - async getWallets(signer: Address.Address) { - return normalizeAddressKeys( - await this.getWalletsGeneric( - await this.store.loadSubdigestsOfSigner(signer), - (subdigest) => this.store.loadSignatureOfSubdigest(signer, subdigest), - ), + async getWallets(signer: Address.Checksummed) { + return this.getWalletsGeneric( + await this.store.loadSubdigestsOfSigner(signer), + (subdigest) => this.store.loadSignatureOfSubdigest(signer, subdigest), ) } - async getWalletsForSapient(signer: Address.Address, imageHash: Hex.Hex) { - return normalizeAddressKeys( - await this.getWalletsGeneric( - await this.store.loadSubdigestsOfSapientSigner(signer, imageHash), - (subdigest) => this.store.loadSapientSignatureOfSubdigest(signer, subdigest, imageHash), - ), + async getWalletsForSapient(signer: Address.Checksummed, imageHash: Hex.Hex) { + return this.getWalletsGeneric( + await this.store.loadSubdigestsOfSapientSigner(signer, imageHash), + (subdigest) => this.store.loadSapientSignatureOfSubdigest(signer, subdigest, imageHash), ) } getWitnessFor( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, ): | { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | Promise<{ chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | undefined> @@ -169,8 +160,8 @@ export class Provider implements ProviderInterface { } getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, imageHash: Hex.Hex, ): | { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf } @@ -183,7 +174,7 @@ export class Provider implements ProviderInterface { } async saveWitnesses( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signatures: Signature.RawTopology, @@ -199,7 +190,7 @@ export class Provider implements ProviderInterface { } async getConfigurationUpdates( - wallet: Address.Address, + wallet: Address.Checksummed, fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise<{ imageHash: Hex.Hex; signature: Signature.RawSignature }[]> { @@ -289,7 +280,7 @@ export class Provider implements ProviderInterface { const encoded = Signature.fillLeaves(fromConfig.topology, (leaf) => { if (Config.isSapientSignerLeaf(leaf)) { const sapientSignature = signaturesOfSigners.find( - ({ signer, imageHash }: { signer: Address.Address; imageHash?: Hex.Hex }) => { + ({ signer, imageHash }: { signer: Address.Checksummed; imageHash?: Hex.Hex }) => { return imageHash && Address.isEqual(signer, leaf.address) && imageHash === leaf.imageHash }, )?.signature @@ -343,7 +334,7 @@ export class Provider implements ProviderInterface { } async saveUpdate( - wallet: Address.Address, + wallet: Address.Checksummed, configuration: Config.Config, signature: Signature.RawSignature, ): Promise { @@ -374,17 +365,19 @@ export class Provider implements ProviderInterface { if (Signature.isRawSignerLeaf(topology)) { const type = topology.signature.type if (type === 'eth_sign' || type === 'hash') { - const address = Secp256k1.recoverAddress({ - payload: type === 'eth_sign' ? PersonalMessage.getSignPayload(subdigest) : subdigest, - signature: topology.signature, - }) + const address = Address.checksum( + Secp256k1.recoverAddress({ + payload: type === 'eth_sign' ? PersonalMessage.getSignPayload(subdigest) : subdigest, + signature: topology.signature, + }), + ) return this.store.saveSignatureOfSubdigest(address, subdigest, topology.signature) } if (Signature.isSignatureOfSapientSignerLeaf(topology.signature)) { - switch (topology.signature.address.toLowerCase()) { - case this.extensions.passkeys.toLowerCase(): + switch (topology.signature.address) { + case this.extensions.passkeys: const decoded = Extensions.Passkeys.decode(Bytes.fromHex(topology.signature.data)) if (!Extensions.Passkeys.isValidSignature(subdigest, decoded)) { @@ -418,7 +411,7 @@ export class Provider implements ProviderInterface { saveDeploy(imageHash: Hex.Hex, context: Context.Context): Promise { return this.store.saveCounterfactualWallet( - SequenceAddress.from(Bytes.fromHex(imageHash), context), + Address.fromDeployConfiguration(Bytes.fromHex(imageHash), context), imageHash, context, ) @@ -426,12 +419,12 @@ export class Provider implements ProviderInterface { async getPayload( opHash: Hex.Hex, - ): Promise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Address } | undefined> { + ): Promise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Checksummed } | undefined> { const data = await this.store.loadPayloadOfSubdigest(opHash) return data ? { chainId: data.chainId, payload: data.content, wallet: data.wallet } : undefined } - savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: bigint): Promise { + savePayload(wallet: Address.Checksummed, payload: Payload.Parented, chainId: bigint): Promise { const subdigest = Hex.fromBytes(Payload.hash(wallet, chainId, payload)) return this.store.savePayloadOfSubdigest(subdigest, { content: payload, chainId, wallet }) } diff --git a/packages/wallet/core/src/state/local/indexed-db.ts b/packages/wallet/core/src/state/local/indexed-db.ts index 51b8c59d4..1278147bb 100644 --- a/packages/wallet/core/src/state/local/indexed-db.ts +++ b/packages/wallet/core/src/state/local/indexed-db.ts @@ -1,5 +1,5 @@ -import { Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { Store } from './index.js' const DB_VERSION = 1 @@ -96,12 +96,16 @@ export class IndexedDbStore implements Store { await this.put(storeName, key, Array.from(setData)) } - private getSignatureKey(signer: Address.Address, subdigest: Hex.Hex): string { - return `${signer.toLowerCase()}-${subdigest.toLowerCase()}` + private getSignatureKey(signer: Address.Checksummed, subdigest: Hex.Hex): `${Address.Checksummed}-${Hex.Hex}` { + return `${signer}-0x${subdigest.slice(2).toLowerCase()}` } - private getSapientSignatureKey(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex): string { - return `${signer.toLowerCase()}-${imageHash.toLowerCase()}-${subdigest.toLowerCase()}` + private getSapientSignatureKey( + signer: Address.Checksummed, + subdigest: Hex.Hex, + imageHash: Hex.Hex, + ): `${Address.Checksummed}-${Hex.Hex}-${Hex.Hex}` { + return `${signer}-0x${imageHash.slice(2).toLowerCase()}-0x${subdigest.slice(2).toLowerCase()}` } async loadConfig(imageHash: Hex.Hex): Promise { @@ -113,81 +117,90 @@ export class IndexedDbStore implements Store { } async loadCounterfactualWallet( - wallet: Address.Address, + wallet: Address.Checksummed, ): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { - return this.get(STORE_WALLETS, wallet.toLowerCase()) + return this.get(STORE_WALLETS, wallet) } - async saveCounterfactualWallet(wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context): Promise { - await this.put(STORE_WALLETS, wallet.toLowerCase(), { imageHash, context }) + async saveCounterfactualWallet( + wallet: Address.Checksummed, + imageHash: Hex.Hex, + context: Context.Context, + ): Promise { + await this.put(STORE_WALLETS, wallet, { imageHash, context }) } async loadPayloadOfSubdigest( subdigest: Hex.Hex, - ): Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Address } | undefined> { + ): Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed } | undefined> { return this.get(STORE_PAYLOADS, subdigest.toLowerCase()) } async savePayloadOfSubdigest( subdigest: Hex.Hex, - payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Address }, + payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed }, ): Promise { await this.put(STORE_PAYLOADS, subdigest.toLowerCase(), payload) } - async loadSubdigestsOfSigner(signer: Address.Address): Promise { - const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signer.toLowerCase()) - return Array.from(dataSet) as Hex.Hex[] + async loadSubdigestsOfSigner(signer: Address.Checksummed): Promise { + const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signer) + return Array.from(dataSet).map((subdigest) => { + Hex.assert(subdigest) + return subdigest + }) } async loadSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, ): Promise { const key = this.getSignatureKey(signer, subdigest) - return this.get(STORE_SIGNATURES, key.toLowerCase()) + return this.get(STORE_SIGNATURES, key) } async saveSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf, ): Promise { const key = this.getSignatureKey(signer, subdigest) - await this.put(STORE_SIGNATURES, key.toLowerCase(), signature) + await this.put(STORE_SIGNATURES, key, signature) - const signerKey = signer.toLowerCase() const subdigestKey = subdigest.toLowerCase() - const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signerKey) + const dataSet = await this.getSet(STORE_SIGNER_SUBDIGESTS, signer) dataSet.add(subdigestKey) - await this.putSet(STORE_SIGNER_SUBDIGESTS, signerKey, dataSet) + await this.putSet(STORE_SIGNER_SUBDIGESTS, signer, dataSet) } - async loadSubdigestsOfSapientSigner(signer: Address.Address, imageHash: Hex.Hex): Promise { - const key = `${signer.toLowerCase()}-${imageHash.toLowerCase()}` + async loadSubdigestsOfSapientSigner(signer: Address.Checksummed, imageHash: Hex.Hex): Promise { + const key = `${signer}-${imageHash.toLowerCase()}` const dataSet = await this.getSet(STORE_SAPIENT_SIGNER_SUBDIGESTS, key) - return Array.from(dataSet) as Hex.Hex[] + return Array.from(dataSet).map((subdigest) => { + Hex.assert(subdigest) + return subdigest + }) } async loadSapientSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, ): Promise { const key = this.getSapientSignatureKey(signer, subdigest, imageHash) - return this.get(STORE_SAPIENT_SIGNATURES, key.toLowerCase()) + return this.get(STORE_SAPIENT_SIGNATURES, key) } async saveSapientSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf, ): Promise { - const fullKey = this.getSapientSignatureKey(signer, subdigest, imageHash).toLowerCase() + const fullKey = this.getSapientSignatureKey(signer, subdigest, imageHash) await this.put(STORE_SAPIENT_SIGNATURES, fullKey, signature) - const signerKey = `${signer.toLowerCase()}-${imageHash.toLowerCase()}` + const signerKey = `${signer}-${imageHash.toLowerCase()}` const subdigestKey = subdigest.toLowerCase() const dataSet = await this.getSet(STORE_SAPIENT_SIGNER_SUBDIGESTS, signerKey) dataSet.add(subdigestKey) diff --git a/packages/wallet/core/src/state/local/memory.ts b/packages/wallet/core/src/state/local/memory.ts index 9a23339ae..1550dea28 100644 --- a/packages/wallet/core/src/state/local/memory.ts +++ b/packages/wallet/core/src/state/local/memory.ts @@ -1,18 +1,21 @@ -import { Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Context, Payload, Signature, Config, GenericTree } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { Store } from './index.js' export class MemoryStore implements Store { - private configs = new Map<`0x${string}`, Config.Config>() - private counterfactualWallets = new Map<`0x${string}`, { imageHash: Hex.Hex; context: Context.Context }>() - private payloads = new Map<`0x${string}`, { content: Payload.Parented; chainId: bigint; wallet: Address.Address }>() - private signerSubdigests = new Map>() - private signatures = new Map<`0x${string}`, Signature.SignatureOfSignerLeaf>() + private configs = new Map() + private counterfactualWallets = new Map() + private payloads = new Map() + private signerSubdigests = new Map>() + private signatures = new Map<`${Address.Checksummed}-${Hex.Hex}`, Signature.SignatureOfSignerLeaf>() - private sapientSignerSubdigests = new Map>() - private sapientSignatures = new Map<`0x${string}`, Signature.SignatureOfSapientSignerLeaf>() + private sapientSignerSubdigests = new Map<`${Address.Checksummed}-${Hex.Hex}`, Set>() + private sapientSignatures = new Map< + `${Address.Checksummed}-${Hex.Hex}-${Hex.Hex}`, + Signature.SignatureOfSapientSignerLeaf + >() - private trees = new Map<`0x${string}`, GenericTree.Tree>() + private trees = new Map() private deepCopy(value: T): T { // modern runtime → fast native path @@ -38,106 +41,113 @@ export class MemoryStore implements Store { return out as T } - private getSignatureKey(signer: Address.Address, subdigest: Hex.Hex): string { - return `${signer.toLowerCase()}-${subdigest.toLowerCase()}` + private getSignatureKey(signer: Address.Checksummed, subdigest: Hex.Hex): `${Address.Checksummed}-${Hex.Hex}` { + return `${signer}-0x${subdigest.slice(2).toLowerCase()}` } - private getSapientSignatureKey(signer: Address.Address, subdigest: Hex.Hex, imageHash: Hex.Hex): string { - return `${signer.toLowerCase()}-${imageHash.toLowerCase()}-${subdigest.toLowerCase()}` + private getSapientSignatureKey( + signer: Address.Checksummed, + subdigest: Hex.Hex, + imageHash: Hex.Hex, + ): `${Address.Checksummed}-${Hex.Hex}-${Hex.Hex}` { + return `${signer}-0x${imageHash.slice(2).toLowerCase()}-0x${subdigest.slice(2).toLowerCase()}` } async loadConfig(imageHash: Hex.Hex): Promise { - const config = this.configs.get(imageHash.toLowerCase() as `0x${string}`) + const config = this.configs.get(`0x${imageHash.slice(2).toLowerCase()}`) return config ? this.deepCopy(config) : undefined } async saveConfig(imageHash: Hex.Hex, config: Config.Config): Promise { - this.configs.set(imageHash.toLowerCase() as `0x${string}`, this.deepCopy(config)) + this.configs.set(`0x${imageHash.slice(2).toLowerCase()}`, this.deepCopy(config)) } async loadCounterfactualWallet( - wallet: Address.Address, + wallet: Address.Checksummed, ): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { - const counterfactualWallet = this.counterfactualWallets.get(wallet.toLowerCase() as `0x${string}`) + const counterfactualWallet = this.counterfactualWallets.get(wallet) return counterfactualWallet ? this.deepCopy(counterfactualWallet) : undefined } - async saveCounterfactualWallet(wallet: Address.Address, imageHash: Hex.Hex, context: Context.Context): Promise { - this.counterfactualWallets.set(wallet.toLowerCase() as `0x${string}`, this.deepCopy({ imageHash, context })) + async saveCounterfactualWallet( + wallet: Address.Checksummed, + imageHash: Hex.Hex, + context: Context.Context, + ): Promise { + this.counterfactualWallets.set(wallet, this.deepCopy({ imageHash, context })) } async loadPayloadOfSubdigest( subdigest: Hex.Hex, - ): Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Address } | undefined> { - const payload = this.payloads.get(subdigest.toLowerCase() as `0x${string}`) + ): Promise<{ content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed } | undefined> { + const payload = this.payloads.get(`0x${subdigest.slice(2).toLowerCase()}`) return payload ? this.deepCopy(payload) : undefined } async savePayloadOfSubdigest( subdigest: Hex.Hex, - payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Address }, + payload: { content: Payload.Parented; chainId: bigint; wallet: Address.Checksummed }, ): Promise { - this.payloads.set(subdigest.toLowerCase() as `0x${string}`, this.deepCopy(payload)) + this.payloads.set(`0x${subdigest.slice(2).toLowerCase()}`, this.deepCopy(payload)) } - async loadSubdigestsOfSigner(signer: Address.Address): Promise { - const subdigests = this.signerSubdigests.get(signer.toLowerCase() as `0x${string}`) - return subdigests ? Array.from(subdigests).map((s) => s as Hex.Hex) : [] + async loadSubdigestsOfSigner(signer: Address.Checksummed): Promise { + const subdigests = this.signerSubdigests.get(signer) + return subdigests ? Array.from(subdigests) : [] } async loadSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, ): Promise { const key = this.getSignatureKey(signer, subdigest) - const signature = this.signatures.get(key as `0x${string}`) + const signature = this.signatures.get(key) return signature ? this.deepCopy(signature) : undefined } async saveSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, signature: Signature.SignatureOfSignerLeaf, ): Promise { const key = this.getSignatureKey(signer, subdigest) - this.signatures.set(key as `0x${string}`, this.deepCopy(signature)) - const signerKey = signer.toLowerCase() - const subdigestKey = subdigest.toLowerCase() + const subdigestKey: Hex.Hex = `0x${subdigest.slice(2).toLowerCase()}` + this.signatures.set(key, this.deepCopy(signature)) - if (!this.signerSubdigests.has(signerKey)) { - this.signerSubdigests.set(signerKey, new Set()) + if (!this.signerSubdigests.has(signer)) { + this.signerSubdigests.set(signer, new Set()) } - this.signerSubdigests.get(signerKey)!.add(subdigestKey) + this.signerSubdigests.get(signer)!.add(subdigestKey) } - async loadSubdigestsOfSapientSigner(signer: Address.Address, imageHash: Hex.Hex): Promise { - const key = `${signer.toLowerCase()}-${imageHash.toLowerCase()}` + async loadSubdigestsOfSapientSigner(signer: Address.Checksummed, imageHash: Hex.Hex): Promise { + const key: `${Address.Checksummed}-${Hex.Hex}` = `${signer}-0x${imageHash.slice(2).toLowerCase()}` const subdigests = this.sapientSignerSubdigests.get(key) - return subdigests ? Array.from(subdigests).map((s) => s as Hex.Hex) : [] + return subdigests ? Array.from(subdigests) : [] } async loadSapientSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, ): Promise { const key = this.getSapientSignatureKey(signer, subdigest, imageHash) - const signature = this.sapientSignatures.get(key as `0x${string}`) + const signature = this.sapientSignatures.get(key) return signature ? this.deepCopy(signature) : undefined } async saveSapientSignatureOfSubdigest( - signer: Address.Address, + signer: Address.Checksummed, subdigest: Hex.Hex, imageHash: Hex.Hex, signature: Signature.SignatureOfSapientSignerLeaf, ): Promise { const key = this.getSapientSignatureKey(signer, subdigest, imageHash) - this.sapientSignatures.set(key as `0x${string}`, this.deepCopy(signature)) + this.sapientSignatures.set(key, this.deepCopy(signature)) - const signerKey = `${signer.toLowerCase()}-${imageHash.toLowerCase()}` - const subdigestKey = subdigest.toLowerCase() + const signerKey: `${Address.Checksummed}-${Hex.Hex}` = `${signer}-0x${imageHash.slice(2).toLowerCase()}` + const subdigestKey: Hex.Hex = `0x${subdigest.slice(2).toLowerCase()}` if (!this.sapientSignerSubdigests.has(signerKey)) { this.sapientSignerSubdigests.set(signerKey, new Set()) @@ -146,11 +156,11 @@ export class MemoryStore implements Store { } async loadTree(rootHash: Hex.Hex): Promise { - const tree = this.trees.get(rootHash.toLowerCase() as `0x${string}`) + const tree = this.trees.get(`0x${rootHash.slice(2).toLowerCase()}`) return tree ? this.deepCopy(tree) : undefined } async saveTree(rootHash: Hex.Hex, tree: GenericTree.Tree): Promise { - this.trees.set(rootHash.toLowerCase() as `0x${string}`, this.deepCopy(tree)) + this.trees.set(`0x${rootHash.slice(2).toLowerCase()}`, this.deepCopy(tree)) } } diff --git a/packages/wallet/core/src/state/remote/dev-http.ts b/packages/wallet/core/src/state/remote/dev-http.ts index fe97a42ba..357aa2c6e 100644 --- a/packages/wallet/core/src/state/remote/dev-http.ts +++ b/packages/wallet/core/src/state/remote/dev-http.ts @@ -1,5 +1,5 @@ -import { Address, Hex } from 'ox' -import { Config, Context, GenericTree, Payload, Signature, Utils } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' +import { Address, Config, Context, GenericTree, Payload, Signature, Utils } from '@0xsequence/wallet-primitives' import { Provider } from '../index.js' export class DevHttpProvider implements Provider { @@ -118,12 +118,12 @@ export class DevHttpProvider implements Provider { return config } - async getDeploy(wallet: Address.Address): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { + async getDeploy(wallet: Address.Checksummed): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { return this.request('GET', `/deploy/${wallet}`) } - async getWallets(signer: Address.Address): Promise<{ - [wallet: Address.Address]: { + async getWallets(signer: Address.Checksummed): Promise<{ + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSignerLeaf @@ -134,10 +134,10 @@ export class DevHttpProvider implements Provider { } async getWalletsForSapient( - signer: Address.Address, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise<{ - [wallet: Address.Address]: { + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSapientSignerLeaf @@ -148,8 +148,8 @@ export class DevHttpProvider implements Provider { } async getWitnessFor( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, ): Promise< | { chainId: bigint @@ -163,8 +163,8 @@ export class DevHttpProvider implements Provider { } async getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise< | { @@ -179,7 +179,7 @@ export class DevHttpProvider implements Provider { } async getConfigurationUpdates( - wallet: Address.Address, + wallet: Address.Checksummed, fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise> { @@ -199,7 +199,7 @@ export class DevHttpProvider implements Provider { } async saveWitnesses( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signatures: Signature.RawTopology, @@ -209,7 +209,7 @@ export class DevHttpProvider implements Provider { } async saveUpdate( - wallet: Address.Address, + wallet: Address.Checksummed, configuration: Config.Config, signature: Signature.RawSignature, ): Promise { @@ -233,7 +233,7 @@ export class DevHttpProvider implements Provider { | { chainId: bigint payload: Payload.Parented - wallet: Address.Address + wallet: Address.Checksummed } | undefined > { @@ -241,13 +241,13 @@ export class DevHttpProvider implements Provider { | { chainId: bigint payload: Payload.Parented - wallet: Address.Address + wallet: Address.Checksummed } | undefined >('GET', `/payload/${opHash}`) } - async savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: bigint): Promise { + async savePayload(wallet: Address.Checksummed, payload: Payload.Parented, chainId: bigint): Promise { return this.request('POST', '/payload', { wallet, payload, chainId }) } } diff --git a/packages/wallet/core/src/state/sequence/index.ts b/packages/wallet/core/src/state/sequence/index.ts index 5685479b3..44848f3ec 100644 --- a/packages/wallet/core/src/state/sequence/index.ts +++ b/packages/wallet/core/src/state/sequence/index.ts @@ -1,8 +1,7 @@ -import { Config, Constants, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, Signature as oxSignature } from 'ox' +import { Address, Config, Constants, Context, GenericTree, Payload, Signature } from '@0xsequence/wallet-primitives' +import { Bytes, Hex, Signature as oxSignature } from 'ox' import { Provider as ProviderInterface } from '../index.js' import { Sessions, SignatureType } from './sessions.gen.js' -import { normalizeAddressKeys } from '../utils.js' export class Provider implements ProviderInterface { private readonly service: Sessions @@ -21,98 +20,104 @@ export class Provider implements ProviderInterface { return fromServiceConfig(config) } - async getDeploy(wallet: Address.Address): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { + async getDeploy(wallet: Address.Checksummed): Promise<{ imageHash: Hex.Hex; context: Context.Context } | undefined> { const { deployHash, context } = await this.service.deployHash({ wallet }) Hex.assert(deployHash) - Address.assert(context.factory) - Address.assert(context.mainModule) - Address.assert(context.mainModuleUpgradable) Hex.assert(context.walletCreationCode) return { imageHash: deployHash, context: { - factory: context.factory, - stage1: context.mainModule, - stage2: context.mainModuleUpgradable, + factory: Address.checksum(context.factory), + stage1: Address.checksum(context.mainModule), + stage2: Address.checksum(context.mainModuleUpgradable), creationCode: context.walletCreationCode, }, } } - async getWallets(signer: Address.Address): Promise<{ - [wallet: Address.Address]: { + async getWallets(signer: Address.Checksummed): Promise<{ + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSignerLeaf } }> { - const result = await this.service.wallets({ signer }) - const wallets = normalizeAddressKeys(result.wallets) + const { wallets } = await this.service.wallets({ signer }) return Object.fromEntries( - Object.entries(wallets).map(([wallet, signature]) => { - Address.assert(wallet) - Hex.assert(signature.signature) + Object.entries(wallets).map( + ([wallet, signature]): [ + Address.Checksummed, + { + chainId: bigint + payload: Payload.Parented + signature: Signature.SignatureOfSignerLeaf + }, + ] => { + Hex.assert(signature.signature) - switch (signature.type) { - case SignatureType.EIP712: - return [ - wallet, - { - chainId: BigInt(signature.chainID), - payload: fromServicePayload(signature.payload), - signature: { type: 'hash', ...oxSignature.from(signature.signature) }, - }, - ] - case SignatureType.EthSign: - return [ - wallet, - { - chainId: BigInt(signature.chainID), - payload: fromServicePayload(signature.payload), - signature: { type: 'eth_sign', ...oxSignature.from(signature.signature) }, - }, - ] - case SignatureType.EIP1271: - return [ - wallet, - { - chainId: BigInt(signature.chainID), - payload: fromServicePayload(signature.payload), - signature: { type: 'erc1271', address: signer, data: signature.signature }, - }, - ] - case SignatureType.Sapient: - throw new Error(`unexpected sapient signature by ${signer}`) - case SignatureType.SapientCompact: - throw new Error(`unexpected compact sapient signature by ${signer}`) - } - }), + switch (signature.type) { + case SignatureType.EIP712: + return [ + Address.checksum(wallet), + { + chainId: BigInt(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'hash', ...oxSignature.from(signature.signature) }, + }, + ] + case SignatureType.EthSign: + return [ + Address.checksum(wallet), + { + chainId: BigInt(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'eth_sign', ...oxSignature.from(signature.signature) }, + }, + ] + case SignatureType.EIP1271: + return [ + Address.checksum(wallet), + { + chainId: BigInt(signature.chainID), + payload: fromServicePayload(signature.payload), + signature: { type: 'erc1271', address: signer, data: signature.signature }, + }, + ] + case SignatureType.Sapient: + throw new Error(`unexpected sapient signature by ${signer}`) + case SignatureType.SapientCompact: + throw new Error(`unexpected compact sapient signature by ${signer}`) + } + }, + ), ) } async getWalletsForSapient( - signer: Address.Address, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise<{ - [wallet: Address.Address]: { + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: Signature.SignatureOfSapientSignerLeaf } }> { - const result = await this.service.wallets({ signer, sapientHash: imageHash }) - const wallets = normalizeAddressKeys(result.wallets) + const { wallets } = await this.service.wallets({ signer, sapientHash: imageHash }) return Object.fromEntries( Object.entries(wallets).map( ([wallet, signature]): [ - Address.Address, - { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf }, + Address.Checksummed, + { + chainId: bigint + payload: Payload.Parented + signature: Signature.SignatureOfSapientSignerLeaf + }, ] => { - Address.assert(wallet) Hex.assert(signature.signature) switch (signature.type) { @@ -124,7 +129,7 @@ export class Provider implements ProviderInterface { throw new Error(`unexpected erc-1271 signature by ${signer}`) case SignatureType.Sapient: return [ - wallet, + Address.checksum(wallet), { chainId: BigInt(signature.chainID), payload: fromServicePayload(signature.payload), @@ -133,7 +138,7 @@ export class Provider implements ProviderInterface { ] case SignatureType.SapientCompact: return [ - wallet, + Address.checksum(wallet), { chainId: BigInt(signature.chainID), payload: fromServicePayload(signature.payload), @@ -147,8 +152,8 @@ export class Provider implements ProviderInterface { } async getWitnessFor( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, ): Promise<{ chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSignerLeaf } | undefined> { try { const { witness } = await this.service.witness({ signer, wallet }) @@ -183,8 +188,8 @@ export class Provider implements ProviderInterface { } async getWitnessForSapient( - wallet: Address.Address, - signer: Address.Address, + wallet: Address.Checksummed, + signer: Address.Checksummed, imageHash: Hex.Hex, ): Promise< { chainId: bigint; payload: Payload.Parented; signature: Signature.SignatureOfSapientSignerLeaf } | undefined @@ -218,7 +223,7 @@ export class Provider implements ProviderInterface { } async getConfigurationUpdates( - wallet: Address.Address, + wallet: Address.Checksummed, fromImageHash: Hex.Hex, options?: { allUpdates?: boolean }, ): Promise> { @@ -249,16 +254,14 @@ export class Provider implements ProviderInterface { async getPayload( opHash: Hex.Hex, - ): Promise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Address } | undefined> { + ): Promise<{ chainId: bigint; payload: Payload.Parented; wallet: Address.Checksummed } | undefined> { const { version, payload, wallet, chainID } = await this.service.payload({ digest: opHash }) if (version !== 3) { throw new Error(`invalid payload version ${version}, expected version 3`) } - Address.assert(wallet) - - return { payload: fromServicePayload(payload), wallet, chainId: BigInt(chainID) } + return { payload: fromServicePayload(payload), wallet: Address.checksum(wallet), chainId: BigInt(chainID) } } async saveWallet(deployConfiguration: Config.Config, context: Context.Context): Promise { @@ -277,7 +280,7 @@ export class Provider implements ProviderInterface { } async saveWitnesses( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, signatures: Signature.RawTopology, @@ -319,7 +322,7 @@ export class Provider implements ProviderInterface { } async saveUpdate( - wallet: Address.Address, + wallet: Address.Checksummed, configuration: Config.Config, signature: Signature.RawSignature, ): Promise { @@ -344,7 +347,7 @@ export class Provider implements ProviderInterface { // TODO: save deploy hash even if we don't have its configuration } - async savePayload(wallet: Address.Address, payload: Payload.Parented, chainId: bigint): Promise { + async savePayload(wallet: Address.Checksummed, payload: Payload.Parented, chainId: bigint): Promise { await this.service.savePayload({ version: 3, payload: getServicePayload(payload), @@ -396,14 +399,10 @@ function getServiceConfig(config: Config.Config): ServiceConfig { } function fromServiceConfig(config: ServiceConfig): Config.Config { - if (config.checkpointer !== undefined) { - Address.assert(config.checkpointer) - } - return { threshold: BigInt(config.threshold), checkpoint: BigInt(config.checkpoint), - checkpointer: config.checkpointer, + checkpointer: config.checkpointer ? Address.checksum(config.checkpointer) : undefined, topology: fromServiceConfigTree(config.tree), } } @@ -445,18 +444,16 @@ function fromServiceConfigTree(tree: ServiceConfigTree): Config.Topology { if ('weight' in tree) { if ('address' in tree) { - Address.assert(tree.address) - if (tree.imageHash) { Hex.assert(tree.imageHash) return { type: 'sapient-signer', - address: tree.address, + address: Address.checksum(tree.address), weight: BigInt(tree.weight), imageHash: tree.imageHash, } } else { - return { type: 'signer', address: tree.address, weight: BigInt(tree.weight) } + return { type: 'signer', address: Address.checksum(tree.address), weight: BigInt(tree.weight) } } } @@ -535,11 +532,10 @@ function getServicePayloadCall(call: Payload.Call): ServicePayloadCall { } function fromServicePayloadCall(call: ServicePayloadCall): Payload.Call { - Address.assert(call.to) Hex.assert(call.data) return { - to: call.to, + to: Address.checksum(call.to), value: BigInt(call.value), data: call.data, gasLimit: BigInt(call.gasLimit), diff --git a/packages/wallet/core/src/state/utils.ts b/packages/wallet/core/src/state/utils.ts index bae6e4248..f68cf031e 100644 --- a/packages/wallet/core/src/state/utils.ts +++ b/packages/wallet/core/src/state/utils.ts @@ -1,10 +1,9 @@ -import { Payload, Signature } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload, Signature } from '@0xsequence/wallet-primitives' import { Reader } from './index.js' import { isSapientSigner, SapientSigner, Signer } from '../signers/index.js' export type WalletWithWitness = { - wallet: Address.Address + wallet: Address.Checksummed chainId: bigint payload: Payload.Parented signature: S extends SapientSigner ? Signature.SignatureOfSapientSignerLeaf : Signature.SignatureOfSignerLeaf @@ -16,9 +15,8 @@ export async function getWalletsFor( ): Promise>> { const wallets = await retrieveWallets(stateReader, signer) return Object.entries(wallets).map(([wallet, { chainId, payload, signature }]) => { - Hex.assert(wallet) return { - wallet, + wallet: Address.checksum(wallet), chainId, payload, signature, @@ -30,7 +28,7 @@ async function retrieveWallets( stateReader: Reader, signer: S, ): Promise<{ - [wallet: `0x${string}`]: { + [wallet: Address.Checksummed]: { chainId: bigint payload: Payload.Parented signature: S extends SapientSigner ? Signature.SignatureOfSapientSignerLeaf : Signature.SignatureOfSignerLeaf @@ -48,12 +46,3 @@ async function retrieveWallets( return stateReader.getWallets(await signer.address) as unknown as any } } - -export function normalizeAddressKeys>(obj: T): Record { - return Object.fromEntries( - Object.entries(obj).map(([wallet, signature]) => { - const checksumAddress = Address.checksum(wallet) - return [checksumAddress, signature] - }), - ) as Record -} diff --git a/packages/wallet/core/src/utils/session/permission-builder.ts b/packages/wallet/core/src/utils/session/permission-builder.ts index 08b279509..414845be9 100644 --- a/packages/wallet/core/src/utils/session/permission-builder.ts +++ b/packages/wallet/core/src/utils/session/permission-builder.ts @@ -1,5 +1,5 @@ -import { Permission } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Bytes } from 'ox' +import { Address, Permission } from '@0xsequence/wallet-primitives' +import { AbiFunction, Bytes } from 'ox' /** * Parses a human-readable signature like @@ -31,18 +31,18 @@ function isDynamicType(type: string): boolean { } export class PermissionBuilder { - private target: Address.Address + private target: Address.Checksummed private rules: Permission.ParameterRule[] = [] private fnTypes?: string[] private fnNames?: (string | undefined)[] private allowAllSet: boolean = false private exactCalldataSet: boolean = false - private constructor(target: Address.Address) { + private constructor(target: Address.Checksummed) { this.target = target } - static for(target: Address.Address): PermissionBuilder { + static for(target: Address.Checksummed): PermissionBuilder { return new PermissionBuilder(target) } @@ -172,7 +172,7 @@ export class PermissionBuilder { withAddressParam( param: string | number, - value: Address.Address, + value: Address.Checksummed, operation: Permission.ParameterOperation = Permission.ParameterOperation.EQUAL, cumulative = false, ): this { @@ -280,14 +280,14 @@ export class PermissionBuilder { * Builds permissions for an ERC20 token. */ export class ERC20PermissionBuilder { - static buildTransfer(target: Address.Address, limit: bigint): Permission.Permission { + static buildTransfer(target: Address.Checksummed, limit: bigint): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function transfer(address to, uint256 value)') .withUintNParam('value', limit, 256, Permission.ParameterOperation.LESS_THAN_OR_EQUAL, true) .build() } - static buildApprove(target: Address.Address, spender: Address.Address, limit: bigint): Permission.Permission { + static buildApprove(target: Address.Checksummed, spender: Address.Checksummed, limit: bigint): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function approve(address spender, uint256 value)') .withAddressParam('spender', spender) @@ -300,14 +300,18 @@ export class ERC20PermissionBuilder { * Builds permissions for an ERC721 token. */ export class ERC721PermissionBuilder { - static buildTransfer(target: Address.Address, tokenId: bigint): Permission.Permission { + static buildTransfer(target: Address.Checksummed, tokenId: bigint): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function transferFrom(address from, address to, uint256 tokenId)') .withUintNParam('tokenId', tokenId) .build() } - static buildApprove(target: Address.Address, spender: Address.Address, tokenId: bigint): Permission.Permission { + static buildApprove( + target: Address.Checksummed, + spender: Address.Checksummed, + tokenId: bigint, + ): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function approve(address spender, uint256 tokenId)') .withAddressParam('spender', spender) @@ -320,7 +324,7 @@ export class ERC721PermissionBuilder { * Builds permissions for an ERC1155 token. */ export class ERC1155PermissionBuilder { - static buildTransfer(target: Address.Address, tokenId: bigint, limit: bigint): Permission.Permission { + static buildTransfer(target: Address.Checksummed, tokenId: bigint, limit: bigint): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes data)') .withUintNParam('id', tokenId) @@ -328,7 +332,7 @@ export class ERC1155PermissionBuilder { .build() } - static buildApproveAll(target: Address.Address, operator: Address.Address): Permission.Permission { + static buildApproveAll(target: Address.Checksummed, operator: Address.Checksummed): Permission.Permission { return PermissionBuilder.for(target) .forFunction('function setApprovalForAll(address operator, bool approved)') .withAddressParam('operator', operator) diff --git a/packages/wallet/core/src/wallet.ts b/packages/wallet/core/src/wallet.ts index ae37fe36c..dd0ca2702 100644 --- a/packages/wallet/core/src/wallet.ts +++ b/packages/wallet/core/src/wallet.ts @@ -1,13 +1,13 @@ import { + Address, Config, Constants, Context, Erc6492, Payload, - Address as SequenceAddress, Signature as SequenceSignature, } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Bytes, Hex, Provider, TypedData } from 'ox' +import { AbiFunction, Bytes, Hex, Provider, TypedData } from 'ox' import * as Envelope from './envelope.js' import * as State from './state/index.js' import { UserOperation } from 'ox/erc4337' @@ -15,7 +15,7 @@ import { UserOperation } from 'ox/erc4337' export type WalletOptions = { knownContexts: Context.KnownContext[] stateProvider: State.Provider - guest: Address.Address + guest: Address.Checksummed unsafe?: boolean } @@ -26,9 +26,9 @@ export const DefaultWalletOptions: WalletOptions = { } export type WalletStatus = { - address: Address.Address + address: Address.Checksummed isDeployed: boolean - implementation?: Address.Address + implementation?: Address.Checksummed configuration: Config.Config imageHash: Hex.Hex /** Pending updates in reverse chronological order (newest first) */ @@ -47,12 +47,12 @@ export type WalletStatusWithOnchain = WalletStatus & { } export class Wallet { - public readonly guest: Address.Address + public readonly guest: Address.Checksummed public readonly stateProvider: State.Provider public readonly knownContexts: Context.KnownContext[] constructor( - readonly address: Address.Address, + readonly address: Address.Checksummed, options?: Partial, ) { const combinedContexts = [...DefaultWalletOptions.knownContexts, ...(options?.knownContexts ?? [])] @@ -83,14 +83,14 @@ export class Wallet { } await merged.stateProvider.saveWallet(configuration, context) - return new Wallet(SequenceAddress.from(configuration, context), merged) + return new Wallet(Address.fromDeployConfiguration(configuration, context), merged) } async isDeployed(provider: Provider.Provider): Promise { return (await provider.request({ method: 'eth_getCode', params: [this.address, 'pending'] })) !== '0x' } - async buildDeployTransaction(): Promise<{ to: Address.Address; data: Hex.Hex }> { + async buildDeployTransaction(): Promise<{ to: Address.Checksummed; data: Hex.Hex }> { const deployInformation = await this.stateProvider.getDeploy(this.address) if (!deployInformation) { throw new Error(`cannot find deploy information for ${this.address}`) @@ -168,7 +168,7 @@ export class Wallet { provider?: T, ): Promise { let isDeployed = false - let implementation: Address.Address | undefined + let implementation: Address.Checksummed | undefined let chainId: bigint | undefined let imageHash: Hex.Hex let updates: Array<{ imageHash: Hex.Hex; signature: SequenceSignature.RawSignature }> = [] @@ -201,11 +201,7 @@ export class Wallet { method: 'eth_call', params: [{ to: this.address, data: AbiFunction.encodeData(Constants.GET_IMPLEMENTATION) }, 'latest'], }) - .then((res) => { - const address = `0x${res.slice(-40)}` - Address.assert(address, { strict: false }) - return address - }) + .then((res) => Address.checksum(`0x${res.slice(-40)}`)) .catch(() => undefined), ]) @@ -301,7 +297,7 @@ export class Wallet { return BigInt(result) } - async get4337Nonce(provider: Provider.Provider, entrypoint: Address.Address, space: bigint): Promise { + async get4337Nonce(provider: Provider.Provider, entrypoint: Address.Checksummed, space: bigint): Promise { const result = await provider.request({ method: 'eth_call', params: [ @@ -318,7 +314,7 @@ export class Wallet { return BigInt(result) & 0xffffffffffffffffn } - async get4337Entrypoint(provider: Provider.Provider): Promise { + async get4337Entrypoint(provider: Provider.Provider): Promise { const status = await this.getStatus(provider) return status.context.capabilities?.erc4337?.entrypoint } @@ -359,7 +355,7 @@ export class Wallet { // If the wallet is not deployed, then we need to include the initCode on // the 4337 transaction - let factory: Address.Address | undefined + let factory: Address.Checksummed | undefined let factoryData: Hex.Hex | undefined if (!status.isDeployed) { @@ -409,7 +405,7 @@ export class Wallet { async build4337Transaction( provider: Provider.Provider, envelope: Envelope.Signed, - ): Promise<{ operation: UserOperation.RpcV07; entrypoint: Address.Address }> { + ): Promise<{ operation: UserOperation.RpcV07; entrypoint: Address.Checksummed }> { const status = await this.getStatus(provider) const updatedEnvelope = { ...envelope, configuration: status.configuration } diff --git a/packages/wallet/core/test/constants.ts b/packages/wallet/core/test/constants.ts index ddbeb5ade..bd0090522 100644 --- a/packages/wallet/core/test/constants.ts +++ b/packages/wallet/core/test/constants.ts @@ -1,16 +1,17 @@ +import { Address } from '@0xsequence/wallet-primitives' import { config as dotenvConfig } from 'dotenv' -import { Abi, AbiEvent, Address } from 'ox' +import { Abi, AbiEvent } from 'ox' const envFile = process.env.CI ? '.env.test' : '.env.test.local' dotenvConfig({ path: envFile }) -export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a3395aEf228F44' +export const EMITTER_ADDRESS = Address.checksum('0xb7bE532959236170064cf099e1a3395aEf228F44') export const EMITTER_FUNCTIONS = Abi.from(['function explicitEmit()', 'function implicitEmit()']) export const EMITTER_EVENT_TOPICS = [ AbiEvent.encode(AbiEvent.from('event Explicit(address sender)')).topics[0], AbiEvent.encode(AbiEvent.from('event Implicit(address sender)')).topics[0], ] -export const USDC_ADDRESS: Address.Address = '0xaf88d065e77c8cc2239327c5edb3a432268e5831' +export const USDC_ADDRESS = Address.checksum('0xaf88d065e77c8cc2239327c5edb3a432268e5831') // Environment variables export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545' diff --git a/packages/wallet/core/test/envelope.test.ts b/packages/wallet/core/test/envelope.test.ts index 2aa872c37..d8b3f4699 100644 --- a/packages/wallet/core/test/envelope.test.ts +++ b/packages/wallet/core/test/envelope.test.ts @@ -1,14 +1,14 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { describe, expect, it } from 'vitest' -import { Config, Payload, Signature } from '@0xsequence/wallet-primitives' +import { Address, Config, Payload, Signature } from '@0xsequence/wallet-primitives' import * as Envelope from '../src/envelope.js' // Test addresses and data -const TEST_ADDRESS_1 = Address.from('0x1234567890123456789012345678901234567890') -const TEST_ADDRESS_2 = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const TEST_ADDRESS_3 = Address.from('0x9876543210987654321098765432109876543210') -const TEST_WALLET = Address.from('0xfedcbafedcbafedcbafedcbafedcbafedcbafe00') +const TEST_ADDRESS_1 = Address.checksum('0x1234567890123456789012345678901234567890') +const TEST_ADDRESS_2 = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_ADDRESS_3 = Address.checksum('0x9876543210987654321098765432109876543210') +const TEST_WALLET = Address.checksum('0xfedcbafedcbafedcbafedcbafedcbafedcbafe00') const TEST_IMAGE_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') const TEST_IMAGE_HASH_2 = Hex.from('0x1111111111111111111111111111111111111111111111111111111111111111') @@ -235,7 +235,7 @@ describe('Envelope', () => { it('should return undefined for non-existent signer', () => { const leaf: Config.SignerLeaf = { type: 'signer', - address: Address.from('0x0000000000000000000000000000000000000000'), + address: Address.checksum('0x0000000000000000000000000000000000000000'), weight: 1n, } const foundSig = Envelope.signatureForLeaf(signedEnvelope, leaf) diff --git a/packages/wallet/core/test/preconditions.test.ts b/packages/wallet/core/test/preconditions.test.ts index 1b66c13df..47ea0eb05 100644 --- a/packages/wallet/core/test/preconditions.test.ts +++ b/packages/wallet/core/test/preconditions.test.ts @@ -1,4 +1,5 @@ -import { Address, Provider, RpcTransport, Secp256k1 } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Provider, RpcTransport, Secp256k1 } from 'ox' import { describe, expect, it, vi } from 'vitest' import { Erc1155ApprovalPrecondition, @@ -12,9 +13,9 @@ import { import { LocalRelayer } from '../src/relayer/standard/local' import { CAN_RUN_LIVE, RPC_URL } from './constants' -const ERC20_IMPLICIT_MINT_CONTRACT = '0x041E0CDC028050519C8e6485B2d9840caf63773F' +const ERC20_IMPLICIT_MINT_CONTRACT = Address.checksum('0x041E0CDC028050519C8e6485B2d9840caf63773F') -function randomAddress(): Address.Address { +function randomAddress(): Address.Checksummed { return Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) } @@ -41,7 +42,7 @@ describe('Preconditions', () => { const testWalletAddress = randomAddress() - const requireContractDeployed = async (provider: Provider.Provider, contract: Address.Address) => { + const requireContractDeployed = async (provider: Provider.Provider, contract: Address.Checksummed) => { const code = await provider.request({ method: 'eth_getCode', params: [contract, 'latest'] }) if (code === '0x') { throw new Error(`Contract ${contract} not deployed`) @@ -62,7 +63,7 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), + address: precondition.address, min: precondition.min?.toString(), max: precondition.max?.toString(), }), @@ -93,8 +94,8 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), + address: precondition.address, + token: precondition.token, min: precondition.min?.toString(), max: precondition.max?.toString(), }), @@ -126,9 +127,9 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), - operator: precondition.operator.toString(), + address: precondition.address, + token: precondition.token, + operator: precondition.operator, min: precondition.min.toString(), }), } @@ -158,8 +159,8 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), + address: precondition.address, + token: precondition.token, tokenId: precondition.tokenId.toString(), owned: precondition.owned, }), @@ -168,7 +169,7 @@ describe('Preconditions', () => { if (!CAN_RUN_LIVE) { // Mock the ownerOf call ;(provider as any).call.mockResolvedValue( - '0x000000000000000000000000' + testWalletAddress.toString().slice(2).toLowerCase(), + `0x000000000000000000000000${testWalletAddress.toString().slice(2).toLowerCase()}`, ) } @@ -193,18 +194,16 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), + address: precondition.address, + token: precondition.token, tokenId: precondition.tokenId.toString(), - operator: precondition.operator.toString(), + operator: precondition.operator, }), } if (!CAN_RUN_LIVE) { // Mock the getApproved call - ;(provider as any).call.mockResolvedValue( - '0x000000000000000000000000' + operator.toString().slice(2).toLowerCase(), - ) + ;(provider as any).call.mockResolvedValue(`0x000000000000000000000000${operator.slice(2).toLowerCase()}`) } const isValid = await relayer.checkPrecondition(intentPrecondition) @@ -228,8 +227,8 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), + address: precondition.address, + token: precondition.token, tokenId: precondition.tokenId.toString(), min: precondition.min?.toString(), max: precondition.max?.toString(), @@ -263,10 +262,10 @@ describe('Preconditions', () => { type: precondition.type(), chainId: chainId.toString(), data: JSON.stringify({ - address: precondition.address.toString(), - token: precondition.token.toString(), + address: precondition.address, + token: precondition.token, tokenId: precondition.tokenId.toString(), - operator: precondition.operator.toString(), + operator: precondition.operator, min: precondition.min.toString(), }), } diff --git a/packages/wallet/core/test/preconditions/codec.test.ts b/packages/wallet/core/test/preconditions/codec.test.ts index f67a016fa..b9e89d24e 100644 --- a/packages/wallet/core/test/preconditions/codec.test.ts +++ b/packages/wallet/core/test/preconditions/codec.test.ts @@ -1,5 +1,5 @@ -import { Address } from 'ox' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import { Address } from '@0xsequence/wallet-primitives' import { decodePrecondition, @@ -18,9 +18,9 @@ import { } from '../../src/preconditions/types.js' // Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const OPERATOR_ADDRESS = Address.from('0x9876543210987654321098765432109876543210') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TOKEN_ADDRESS = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const OPERATOR_ADDRESS = Address.checksum('0x9876543210987654321098765432109876543210') describe('Preconditions Codec', () => { // Mock console.warn to test error logging diff --git a/packages/wallet/core/test/preconditions/selectors.test.ts b/packages/wallet/core/test/preconditions/selectors.test.ts index 22fa55bc4..f6dfcaacc 100644 --- a/packages/wallet/core/test/preconditions/selectors.test.ts +++ b/packages/wallet/core/test/preconditions/selectors.test.ts @@ -1,4 +1,3 @@ -import { Address } from 'ox' import { describe, expect, it } from 'vitest' import { @@ -13,10 +12,11 @@ import { Erc20BalancePrecondition, Erc721OwnershipPrecondition, } from '../../src/preconditions/types.js' +import { Address } from '@0xsequence/wallet-primitives' // Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TOKEN_ADDRESS = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') describe('Preconditions Selectors', () => { describe('extractChainID', () => { diff --git a/packages/wallet/core/test/preconditions/types.test.ts b/packages/wallet/core/test/preconditions/types.test.ts index a4ecda1d9..9cad230f1 100644 --- a/packages/wallet/core/test/preconditions/types.test.ts +++ b/packages/wallet/core/test/preconditions/types.test.ts @@ -1,5 +1,5 @@ -import { Address } from 'ox' import { describe, expect, it } from 'vitest' +import { Address } from '@0xsequence/wallet-primitives' import { NativeBalancePrecondition, @@ -12,9 +12,9 @@ import { } from '../../src/preconditions/types.js' // Test addresses -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TOKEN_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const OPERATOR_ADDRESS = Address.from('0x9876543210987654321098765432109876543210') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TOKEN_ADDRESS = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const OPERATOR_ADDRESS = Address.checksum('0x9876543210987654321098765432109876543210') describe('Preconditions Types', () => { describe('NativeBalancePrecondition', () => { @@ -53,7 +53,7 @@ describe('Preconditions Types', () => { }) it('should validate address is required', () => { - const precondition = new NativeBalancePrecondition('' as Address.Address) + const precondition = new NativeBalancePrecondition('' as Address.Checksummed) const error = precondition.isValid() expect(error).toBeInstanceOf(Error) @@ -88,7 +88,7 @@ describe('Preconditions Types', () => { }) it('should validate address is required', () => { - const precondition = new Erc20BalancePrecondition('' as Address.Address, TOKEN_ADDRESS) + const precondition = new Erc20BalancePrecondition('' as Address.Checksummed, TOKEN_ADDRESS) const error = precondition.isValid() expect(error).toBeInstanceOf(Error) @@ -96,7 +96,7 @@ describe('Preconditions Types', () => { }) it('should validate token address is required', () => { - const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, '' as Address.Address) + const precondition = new Erc20BalancePrecondition(TEST_ADDRESS, '' as Address.Checksummed) const error = precondition.isValid() expect(error).toBeInstanceOf(Error) diff --git a/packages/wallet/core/test/relayer/bundler.test.ts b/packages/wallet/core/test/relayer/bundler.test.ts index 64f54530a..a0db2826b 100644 --- a/packages/wallet/core/test/relayer/bundler.test.ts +++ b/packages/wallet/core/test/relayer/bundler.test.ts @@ -1,13 +1,13 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { UserOperation } from 'ox/erc4337' -import { Payload } from '@0xsequence/wallet-primitives' +import { Address, Payload } from '@0xsequence/wallet-primitives' import { Bundler, isBundler } from '../../src/relayer/bundler.js' import { OperationStatus } from '../../src/relayer/relayer.js' // Test addresses and data -const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_ENTRYPOINT_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_WALLET_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TEST_ENTRYPOINT_ADDRESS = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') const TEST_CHAIN_ID = 1n const TEST_OP_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') @@ -238,11 +238,11 @@ describe('Bundler', () => { describe('Type compatibility', () => { it('should work with Address and Hex types from ox', () => { // Test that the interfaces work correctly with ox types - const address = Address.from('0x1234567890123456789012345678901234567890') + const address = Address.checksum('0x1234567890123456789012345678901234567890') const hex = Hex.from('0xabcdef') const chainId = 1n - expect(Address.validate(address)).toBe(true) + expect(Address.isChecksummed(address)).toBe(true) expect(Hex.validate(hex)).toBe(true) expect(typeof chainId).toBe('bigint') }) diff --git a/packages/wallet/core/test/relayer/relayer.test.ts b/packages/wallet/core/test/relayer/relayer.test.ts index 4b3f8a7ff..c635cfcc5 100644 --- a/packages/wallet/core/test/relayer/relayer.test.ts +++ b/packages/wallet/core/test/relayer/relayer.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Hex } from 'ox' -import { Payload, Precondition } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' +import { Address, Payload, Precondition } from '@0xsequence/wallet-primitives' import { Relayer, isRelayer, @@ -17,8 +17,8 @@ import { import { FeeTokenType } from '../../src/relayer/standard/rpc/index.js' // Test addresses and data -const TEST_WALLET_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_TO_ADDRESS = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_WALLET_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TEST_TO_ADDRESS = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') const TEST_DATA = Hex.from('0x12345678') const TEST_CHAIN_ID = 1n const TEST_OP_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') @@ -338,11 +338,11 @@ describe('Relayer', () => { describe('Type compatibility', () => { it('should work with Address and Hex types from ox', () => { // Test that the interfaces work correctly with ox types - const address = Address.from('0x1234567890123456789012345678901234567890') + const address = Address.checksum('0x1234567890123456789012345678901234567890') const hex = Hex.from('0xabcdef') const chainId = 1n - expect(Address.validate(address)).toBe(true) + expect(Address.isChecksummed(address)).toBe(true) expect(Hex.validate(hex)).toBe(true) expect(typeof chainId).toBe('bigint') }) diff --git a/packages/wallet/core/test/session-manager.test.ts b/packages/wallet/core/test/session-manager.test.ts index 266ac48de..6c25dfa4a 100644 --- a/packages/wallet/core/test/session-manager.test.ts +++ b/packages/wallet/core/test/session-manager.test.ts @@ -1,15 +1,21 @@ -import { AbiEvent, AbiFunction, Address, Bytes, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' +import { + Address, + Attestation, + Extensions, + GenericTree, + Payload, + Permission, + SessionConfig, +} from '@0xsequence/wallet-primitives' +import { AbiEvent, AbiFunction, Bytes, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' import { describe, expect, it } from 'vitest' -import { Attestation, GenericTree, Payload, Permission, SessionConfig } from '../../primitives/src/index.js' import { Envelope, Signers, State, Utils, Wallet } from '../src/index.js' - import { EMITTER_FUNCTIONS, EMITTER_ADDRESS, EMITTER_EVENT_TOPICS, LOCAL_RPC_URL, USDC_ADDRESS } from './constants' -import { Extensions } from '@0xsequence/wallet-primitives' const { PermissionBuilder, ERC20PermissionBuilder } = Utils -function randomAddress(): Address.Address { +function randomAddress(): Address.Checksummed { return Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() })) } @@ -298,7 +304,7 @@ describe('SessionManager', () => { const simulateTransaction = async ( provider: Provider.Provider, - transaction: { to: Address.Address; data: Hex.Hex }, + transaction: { to: Address.Checksummed; data: Hex.Hex }, expectedEventTopic?: Hex.Hex, ) => { console.log('Simulating transaction', transaction) diff --git a/packages/wallet/core/test/state/cached.test.ts b/packages/wallet/core/test/state/cached.test.ts index a4b447c46..52cbfc72a 100644 --- a/packages/wallet/core/test/state/cached.test.ts +++ b/packages/wallet/core/test/state/cached.test.ts @@ -1,12 +1,13 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { describe, expect, it, vi, beforeEach } from 'vitest' +import { Address } from '@0xsequence/wallet-primitives' import { Cached } from '../../src/state/cached.js' import type { Provider } from '../../src/state/index.js' // Test data -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_ADDRESS_2 = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TEST_ADDRESS_2 = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') const TEST_IMAGE_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') const TEST_ROOT_HASH = Hex.from('0xfedcba098765432109876543210987654321098765432109876543210987654321') const TEST_OP_HASH = Hex.from('0x1111111111111111111111111111111111111111111111111111111111111111') @@ -495,8 +496,8 @@ describe('Cached', () => { }) it('should handle address normalization correctly', async () => { - const cacheData = { [TEST_ADDRESS.toLowerCase()]: mockWalletData } - const sourceData = { [TEST_ADDRESS_2.toLowerCase()]: mockWalletData } + const cacheData = { [TEST_ADDRESS]: mockWalletData } + const sourceData = { [TEST_ADDRESS_2]: mockWalletData } vi.mocked(mockCache.getWallets).mockResolvedValue(cacheData) vi.mocked(mockSource.getWallets).mockResolvedValue(sourceData) diff --git a/packages/wallet/core/test/state/debug.test.ts b/packages/wallet/core/test/state/debug.test.ts index 4824297ab..61bc4d514 100644 --- a/packages/wallet/core/test/state/debug.test.ts +++ b/packages/wallet/core/test/state/debug.test.ts @@ -1,11 +1,11 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import { Address } from '@0xsequence/wallet-primitives' import { multiplex } from '../../src/state/debug.js' // Test data -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_HEX = Hex.from('0xabcdef123456') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') const TEST_UINT8ARRAY = new Uint8Array([171, 205, 239, 18, 52, 86]) describe('State Debug', () => { diff --git a/packages/wallet/core/test/state/local/memory.test.ts b/packages/wallet/core/test/state/local/memory.test.ts index 9e46a4b4a..f11b81ba0 100644 --- a/packages/wallet/core/test/state/local/memory.test.ts +++ b/packages/wallet/core/test/state/local/memory.test.ts @@ -1,10 +1,11 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { describe, expect, it, beforeEach } from 'vitest' import { MemoryStore } from '../../../src/state/local/memory.js' +import { Address } from '@0xsequence/wallet-primitives' // Test addresses and data -const TEST_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') +const TEST_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') const TEST_IMAGE_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') const TEST_SUBDIGEST = Hex.from('0xabcdef123456789012345678901234567890abcdef123456789012345678901234') @@ -110,17 +111,6 @@ describe('MemoryStore', () => { }) describe('key normalization', () => { - it('should normalize addresses to lowercase', async () => { - const upperAddress = TEST_ADDRESS.toUpperCase() as Address.Address - const context = { test: 'data' } as any - - await store.saveCounterfactualWallet(upperAddress, TEST_IMAGE_HASH, context) - const retrieved = await store.loadCounterfactualWallet(TEST_ADDRESS.toLowerCase() as Address.Address) - - expect(retrieved).toBeDefined() - expect(retrieved?.imageHash).toBe(TEST_IMAGE_HASH) - }) - it('should normalize hex values to lowercase', async () => { const upperHex = TEST_IMAGE_HASH.toUpperCase() as Hex.Hex const config = { test: 'data' } as any diff --git a/packages/wallet/core/test/state/utils.test.ts b/packages/wallet/core/test/state/utils.test.ts index a825536f2..9f0b03ef8 100644 --- a/packages/wallet/core/test/state/utils.test.ts +++ b/packages/wallet/core/test/state/utils.test.ts @@ -1,15 +1,15 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' -import { getWalletsFor, normalizeAddressKeys } from '../../src/state/utils.js' +import { getWalletsFor } from '../../src/state/utils.js' import type { Reader } from '../../src/state/index.js' import type { Signer, SapientSigner } from '../../src/signers/index.js' -import { Payload, Signature } from '@0xsequence/wallet-primitives' +import { Address, Payload, Signature } from '@0xsequence/wallet-primitives' // Test addresses -const TEST_SIGNER_ADDRESS = Address.from('0x1234567890123456789012345678901234567890') -const TEST_WALLET_ADDRESS_1 = Address.from('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') -const TEST_WALLET_ADDRESS_2 = Address.from('0x9876543210987654321098765432109876543210') +const TEST_SIGNER_ADDRESS = Address.checksum('0x1234567890123456789012345678901234567890') +const TEST_WALLET_ADDRESS_1 = Address.checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd') +const TEST_WALLET_ADDRESS_2 = Address.checksum('0x9876543210987654321098765432109876543210') const TEST_IMAGE_HASH = Hex.from('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef') // Mock data for testing @@ -54,90 +54,6 @@ describe('State Utils', () => { console.warn = originalWarn }) - describe('normalizeAddressKeys', () => { - it('should normalize lowercase addresses to checksum format', () => { - const input = { - '0x1234567890123456789012345678901234567890': 'signature1', - '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd': 'signature2', - } - - const result = normalizeAddressKeys(input) - - // Check that addresses are properly checksummed - expect(result).toHaveProperty('0x1234567890123456789012345678901234567890', 'signature1') - expect(result).toHaveProperty('0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD', 'signature2') - }) - - it('should normalize uppercase addresses to checksum format', () => { - const input = { - '0x1234567890123456789012345678901234567890': 'signature1', - '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd': 'signature2', - } - - const result = normalizeAddressKeys(input) - - expect(result).toHaveProperty('0x1234567890123456789012345678901234567890', 'signature1') - expect(result).toHaveProperty('0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD', 'signature2') - }) - - it('should handle mixed case addresses', () => { - const input = { - '0x1234567890aBcDeF1234567890123456789012Ab': 'signature1', - } - - const result = normalizeAddressKeys(input) - - // Should normalize to proper checksum - const normalizedKey = Object.keys(result)[0] - expect(normalizedKey).toMatch(/^0x[0-9a-fA-F]{40}$/) - expect(result[normalizedKey as Address.Address]).toBe('signature1') - }) - - it('should handle empty object', () => { - const input = {} - const result = normalizeAddressKeys(input) - expect(result).toEqual({}) - }) - - it('should preserve values for different value types', () => { - const input = { - '0x1234567890123456789012345678901234567890': { chainId: 1n, payload: mockPayload }, - '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd': 'string-value', - '0x9876543210987654321098765432109876543210': 123, - } - - const result = normalizeAddressKeys(input) - - expect(Object.values(result)).toHaveLength(3) - expect(Object.values(result)).toContain(input['0x1234567890123456789012345678901234567890']) - expect(Object.values(result)).toContain('string-value') - expect(Object.values(result)).toContain(123) - }) - - it('should handle complex nested objects as values', () => { - const complexValue = { - chainId: 42n, - payload: mockPayload, - signature: mockRegularSignature, - nested: { - deep: { - value: 'test', - }, - }, - } - - const input = { - '0x1234567890123456789012345678901234567890': complexValue, - } - - const result = normalizeAddressKeys(input) - - const normalizedAddress = Object.keys(result)[0] as Address.Address - expect(result[normalizedAddress]).toEqual(complexValue) - expect(result[normalizedAddress].nested.deep.value).toBe('test') - }) - }) - describe('getWalletsFor', () => { let mockStateReader: Reader let mockSigner: Signer diff --git a/packages/wallet/core/test/utils/session/permission-builder.test.ts b/packages/wallet/core/test/utils/session/permission-builder.test.ts index ef03b8978..82c96bd8d 100644 --- a/packages/wallet/core/test/utils/session/permission-builder.test.ts +++ b/packages/wallet/core/test/utils/session/permission-builder.test.ts @@ -1,14 +1,13 @@ -import { AbiFunction, Address, Bytes } from 'ox' +import { AbiFunction, Bytes } from 'ox' import { describe, expect, it } from 'vitest' -import { Permission } from '../../../../primitives/src/index.js' import { Utils } from '../../../src/index.js' -import { Constants } from '@0xsequence/wallet-primitives' +import { Address, Constants, Permission } from '@0xsequence/wallet-primitives' const { PermissionBuilder } = Utils -const TARGET = Address.from('0x1234567890123456789012345678901234567890') -const TARGET2 = Address.from('0x1234567890123456789012345678901234567891') +const TARGET = Address.checksum('0x1234567890123456789012345678901234567890') +const TARGET2 = Address.checksum('0x1234567890123456789012345678901234567891') const UINT256_VALUE = 1000000000000000000n const BYTES32_MAX = Bytes.fromHex('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') const STRING_VALUE = diff --git a/packages/wallet/core/test/wallet.test.ts b/packages/wallet/core/test/wallet.test.ts index 6ce1387de..f8a31108a 100644 --- a/packages/wallet/core/test/wallet.test.ts +++ b/packages/wallet/core/test/wallet.test.ts @@ -1,7 +1,7 @@ -import { Address, Hash, Hex, Provider, RpcTransport, Secp256k1, TypedData } from 'ox' +import { Address, Constants, Config, Erc6492, Payload } from '@0xsequence/wallet-primitives' +import { Hash, Hex, Provider, RpcTransport, Secp256k1, TypedData } from 'ox' import { describe, expect, it } from 'vitest' -import { Constants, Config, Erc6492, Payload } from '../../primitives/src/index.js' import { Envelope, State, Wallet } from '../src/index.js' import { LOCAL_RPC_URL } from './constants.js' diff --git a/packages/wallet/dapp-client/README.md b/packages/wallet/dapp-client/README.md index 181d19317..1cace618b 100644 --- a/packages/wallet/dapp-client/README.md +++ b/packages/wallet/dapp-client/README.md @@ -120,7 +120,7 @@ Disconnects the client and clears all session data from browser storage. Note: t Returns the wallet address of the current session. -- **Returns:** `Address.Address | null` +- **Returns:** `Address.Checksummed | null` --- diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index d6f731233..fcb85fead 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -1,6 +1,6 @@ import { Envelope, Relayer, Signers, State, Wallet } from '@0xsequence/wallet-core' -import { Attestation, Extensions, Payload, SessionConfig } from '@0xsequence/wallet-primitives' -import { AbiFunction, Address, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' +import { Address, Attestation, Constants, Extensions, Payload, SessionConfig } from '@0xsequence/wallet-primitives' +import { AbiFunction, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' import { DappTransport } from './DappTransport.js' @@ -62,7 +62,7 @@ export class ChainSessionManager { private sessions: Session[] = [] - private walletAddress: Address.Address | null = null + private walletAddress: Address.Checksummed | null = null private sessionManager: Signers.SessionManager | null = null private wallet: Wallet | null = null private provider: Provider.Provider | null = null @@ -185,7 +185,7 @@ export class ChainSessionManager { * This is used when a wallet address is known but the session manager for this chain hasn't been instantiated yet. * @param walletAddress The address of the wallet to initialize with. */ - public initializeWithWallet(walletAddress: Address.Address) { + public initializeWithWallet(walletAddress: Address.Checksummed) { if (this.isInitialized) return this.walletAddress = walletAddress @@ -204,7 +204,7 @@ export class ChainSessionManager { * @param implicitSession The main implicit session data, which contains the wallet address. */ private async _loadSessionFromStorage(implicitSession: ImplicitSessionData) { - const walletAddr = Address.from(implicitSession.walletAddress) + const walletAddr = Address.checksum(implicitSession.walletAddress) this.initializeWithWallet(walletAddr) if (implicitSession.chainId === this.chainId) { @@ -221,7 +221,7 @@ export class ChainSessionManager { const allExplicitSessions = await this.sequenceStorage.getExplicitSessions() const walletExplicitSessions = allExplicitSessions.filter( - (s) => Address.isEqual(Address.from(s.walletAddress), walletAddr) && s.chainId === this.chainId, + (s) => Address.isEqual(s.walletAddress, walletAddr) && s.chainId === this.chainId, ) for (const sessionData of walletExplicitSessions) { @@ -278,7 +278,7 @@ export class ChainSessionManager { { path: '/request/connect', redirectUrl: this.redirectUrl }, ) - const receivedAddress = Address.from(connectResponse.walletAddress) + const receivedAddress = connectResponse.walletAddress const { attestation, signature, email, loginMethod } = connectResponse if (!attestation || !signature) throw new InitializationError('Attestation or signature missing for implicit session.') @@ -357,7 +357,7 @@ export class ChainSessionManager { { path: '/request/connect', redirectUrl: this.redirectUrl }, ) - if (!Address.isEqual(Address.from(response.walletAddress), this.walletAddress)) { + if (!Address.isEqual(response.walletAddress, this.walletAddress)) { throw new AddExplicitSessionError('Wallet address mismatch.') } @@ -385,7 +385,7 @@ export class ChainSessionManager { * @throws {ModifyExplicitSessionError} If modifying the session fails. */ async modifyExplicitSession( - sessionAddress: Address.Address, + sessionAddress: Address.Checksummed, newPermissions: Signers.Session.ExplicitParams, ): Promise { if (!this.walletAddress) { @@ -419,8 +419,8 @@ export class ChainSessionManager { ) if ( - !Address.isEqual(Address.from(response.walletAddress), this.walletAddress) && - !Address.isEqual(Address.from(response.sessionAddress), sessionAddress) + !Address.isEqual(response.walletAddress, this.walletAddress) && + !Address.isEqual(response.sessionAddress, sessionAddress) ) { throw new ModifyExplicitSessionError('Wallet or session address mismatch.') } @@ -450,7 +450,7 @@ export class ChainSessionManager { try { const connectResponse = response.payload - const receivedAddress = Address.from(connectResponse.walletAddress) + const receivedAddress = connectResponse.walletAddress const { email, loginMethod } = connectResponse if (response.action === RequestActionType.ADD_IMPLICIT_SESSION) { @@ -536,7 +536,7 @@ export class ChainSessionManager { */ private async _initializeImplicitSessionInternal( pk: Hex.Hex, - address: Address.Address, + address: Address.Checksummed, attestation: Attestation.Attestation, identitySignature: Hex.Hex, saveSession: boolean = false, @@ -674,15 +674,28 @@ export class ChainSessionManager { const callsToSend = calls if (feeOption) { - const transfer = AbiFunction.from(['function transfer(address to, uint256 value)']) - const transferCall: Payload.Call = { - to: feeOption.token.contractAddress as `0x${string}`, - value: BigInt(0), - data: AbiFunction.encodeData(transfer, [feeOption.to as Address.Address, BigInt(feeOption.value)]), - gasLimit: BigInt(feeOption.gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert' as const, + let transferCall: Payload.Call + if (feeOption.token.contractAddress && feeOption.token.contractAddress !== Constants.ZeroAddress) { + const transfer = AbiFunction.from(['function transfer(address to, uint256 value)']) + transferCall = { + to: Address.checksum(feeOption.token.contractAddress), + value: BigInt(0), + data: AbiFunction.encodeData(transfer, [feeOption.to, BigInt(feeOption.value)]), + gasLimit: BigInt(feeOption.gasLimit), + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert' as const, + } + } else { + transferCall = { + to: feeOption.to, + value: BigInt(feeOption.value), + data: '0x', + gasLimit: BigInt(feeOption.gasLimit), + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert' as const, + } } callsToSend.unshift(transferCall) } @@ -745,7 +758,7 @@ export class ChainSessionManager { return true } else if (response.action === RequestActionType.MODIFY_EXPLICIT_SESSION) { const modifyResponse = response.payload as ModifySessionSuccessResponsePayload - if (!Address.isEqual(Address.from(modifyResponse.walletAddress), this.walletAddress!)) { + if (!Address.isEqual(modifyResponse.walletAddress, this.walletAddress!)) { throw new ModifyExplicitSessionError('Wallet address mismatch on redirect response.') } @@ -767,7 +780,7 @@ export class ChainSessionManager { * Gets the wallet address associated with this manager. * @returns The wallet address, or null if not initialized. */ - getWalletAddress(): Address.Address | null { + getWalletAddress(): Address.Checksummed | null { return this.walletAddress } @@ -880,7 +893,7 @@ export class ChainSessionManager { * @param calls The payload calls to include in the transaction. * @returns The signed transaction data ready for relaying. */ - private async _buildAndSignCalls(calls: Payload.Call[]): Promise<{ to: Address.Address; data: Hex.Hex }> { + private async _buildAndSignCalls(calls: Payload.Call[]): Promise<{ to: Address.Checksummed; data: Hex.Hex }> { if (!this.wallet || !this.sessionManager || !this.provider) throw new InitializationError('Session not fully initialized.') @@ -926,7 +939,7 @@ export class ChainSessionManager { * @param chainId The chain ID of the transaction. * @returns The final status of the transaction. */ - private async _waitForTransactionReceipt(opHash: `0x${string}`, chainId: bigint): Promise { + private async _waitForTransactionReceipt(opHash: Hex.Hex, chainId: bigint): Promise { try { while (true) { const currentStatus = await this.relayer.status(opHash, chainId) diff --git a/packages/wallet/dapp-client/src/DappClient.ts b/packages/wallet/dapp-client/src/DappClient.ts index 7c72cf689..f92350f83 100644 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ b/packages/wallet/dapp-client/src/DappClient.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { ChainId } from '@0xsequence/network' import { Relayer, Signers } from '@0xsequence/wallet-core' -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' import { ChainSessionManager } from './ChainSessionManager.js' import { DappTransport } from './DappTransport.js' @@ -68,7 +68,7 @@ export class DappClient { private isInitializing = false - private walletAddress: Address.Address | null = null + private walletAddress: Address.Checksummed | null = null private eventListeners: { [K in keyof DappClientEventMap]?: Set } = {} @@ -168,7 +168,7 @@ export class DappClient { /** * Retrieves the wallet address of the current session. - * @returns The wallet address of the current session, or null if not initialized. {@link Address.Address} + * @returns The wallet address of the current session, or null if not initialized. {@link Address.Checksummed} * * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/get-wallet-address} for more detailed documentation. * @@ -181,13 +181,13 @@ export class DappClient { * console.log('Wallet address:', walletAddress); * } */ - public getWalletAddress(): Address.Address | null { + public getWalletAddress(): Address.Checksummed | null { return this.walletAddress } /** * Retrieves a list of all active sessions (signers) associated with the current wallet. - * @returns An array of all the active sessions. {@link { address: Address.Address, isImplicit: boolean }[]} + * @returns An array of all the active sessions. {@link { address: Address.Checksummed, isImplicit: boolean }[]} * * @see {@link https://docs.sequence.xyz/sdk/typescript/v3/dapp-client/get-all-sessions} for more detailed documentation. * @@ -204,7 +204,7 @@ export class DappClient { const allSessions = new Map() Array.from(this.chainSessionManagers.values()).forEach((chainSessionManager) => { chainSessionManager.getSessions().forEach((session) => { - const uniqueKey = `${session.address.toLowerCase()}-${session.isImplicit}` + const uniqueKey = `${session.address}-${session.isImplicit}` if (!allSessions.has(uniqueKey)) { allSessions.set(uniqueKey, session) } @@ -425,7 +425,7 @@ export class DappClient { /** * Modifies the permissions of an existing explicit session for a given chain and session address. * @param chainId The chain ID on which the explicit session exists. {@link ChainId} - * @param sessionAddress The address of the explicit session to modify. {@link Address.Address} + * @param sessionAddress The address of the explicit session to modify. {@link Address.Checksummed} * @param permissions The new permissions to set for the session. {@link Signers.Session.ExplicitParams} * * @throws If the client or relevant chain is not initialized. {@link InitializationError} @@ -454,7 +454,7 @@ export class DappClient { */ async modifyExplicitSession( chainId: ChainId, - sessionAddress: Address.Address, + sessionAddress: Address.Checksummed, permissions: Signers.Session.ExplicitParams, ): Promise { if (!this.isInitialized || !this.walletAddress) diff --git a/packages/wallet/dapp-client/src/types/index.ts b/packages/wallet/dapp-client/src/types/index.ts index d1827dfc9..f5256f13e 100644 --- a/packages/wallet/dapp-client/src/types/index.ts +++ b/packages/wallet/dapp-client/src/types/index.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Attestation, Payload } from '@0xsequence/wallet-primitives' +import { Address, Attestation, Payload } from '@0xsequence/wallet-primitives' import { Signers } from '@0xsequence/wallet-core' import { ChainId } from '@0xsequence/network' -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import type { TypedData } from 'ox/TypedData' // --- Public Interfaces and Constants --- @@ -20,19 +20,19 @@ export type PreferredLoginMethod = 'google' | 'apple' | 'email' | 'passkey' | 'm // --- Payloads for Transport --- export interface AddExplicitSessionPayload { - sessionAddress: Address.Address + sessionAddress: Address.Checksummed permissions: Signers.Session.ExplicitParams preferredLoginMethod?: PreferredLoginMethod email?: string } export interface ModifySessionPayload { - walletAddress: Address.Address - sessionAddress: Address.Address + walletAddress: Address.Checksummed + sessionAddress: Address.Checksummed permissions: Signers.Session.ExplicitParams } export interface AddImplicitSessionPayload { - sessionAddress: Address.Address + sessionAddress: Address.Checksummed implicitSessionRedirectUrl?: string permissions?: Signers.Session.ExplicitParams preferredLoginMethod?: PreferredLoginMethod @@ -40,19 +40,19 @@ export interface AddImplicitSessionPayload { } export interface SignMessagePayload { - address: Address.Address + address: Address.Checksummed message: string chainId: ChainId } export interface SignTypedDataPayload { - address: Address.Address + address: Address.Checksummed typedData: TypedData chainId: ChainId } export interface ConnectSuccessResponsePayload { - walletAddress: string + walletAddress: Address.Checksummed attestation?: Attestation.Attestation signature?: Hex.Hex email?: string @@ -60,18 +60,18 @@ export interface ConnectSuccessResponsePayload { } export interface ModifySessionSuccessResponsePayload { - walletAddress: string - sessionAddress: string + walletAddress: Address.Checksummed + sessionAddress: Address.Checksummed } export interface SignatureResponse { signature: Hex.Hex - walletAddress: string + walletAddress: Address.Checksummed } export interface ExplicitSessionResponsePayload { - walletAddress: string - sessionAddress: string + walletAddress: Address.Checksummed + sessionAddress: Address.Checksummed } // --- Dapp-facing Types --- @@ -87,7 +87,7 @@ export type Transaction = Partial> export type Session = { - address: Address.Address + address: Address.Checksummed isImplicit: boolean } @@ -165,14 +165,14 @@ export interface BaseRequest { export interface MessageSignatureRequest extends BaseRequest { type: 'message_signature' message: string - address: Address.Address + address: Address.Checksummed chainId: number } export interface TypedDataSignatureRequest extends BaseRequest { type: 'typed_data_signature' typedData: unknown - address: Address.Address + address: Address.Checksummed chainId: number } diff --git a/packages/wallet/dapp-client/src/utils/storage.ts b/packages/wallet/dapp-client/src/utils/storage.ts index 91e5efef0..97aa68eb7 100644 --- a/packages/wallet/dapp-client/src/utils/storage.ts +++ b/packages/wallet/dapp-client/src/utils/storage.ts @@ -1,5 +1,5 @@ -import { Attestation } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Attestation } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { ChainId } from '@0xsequence/network' import { jsonReplacers, jsonRevivers } from './index.js' import { @@ -13,7 +13,7 @@ import { export interface ExplicitSessionData { pk: Hex.Hex - walletAddress: Address.Address + walletAddress: Address.Checksummed chainId: ChainId loginMethod?: PreferredLoginMethod userEmail?: string @@ -21,7 +21,7 @@ export interface ExplicitSessionData { export interface ImplicitSessionData { pk: Hex.Hex - walletAddress: Address.Address + walletAddress: Address.Checksummed attestation: Attestation.Attestation identitySignature: Hex.Hex chainId: ChainId @@ -148,8 +148,11 @@ export class WebStorage implements SequenceStorage { async getAndClearTempSessionPk(): Promise { try { const pk = sessionStorage.getItem(TEMP_SESSION_PK_KEY) + if (pk !== null) { + Hex.assert(pk) + } sessionStorage.removeItem(TEMP_SESSION_PK_KEY) - return pk as Hex.Hex | null + return pk } catch (error) { console.error('Failed to retrieve temp session PK:', error) return null diff --git a/packages/wallet/primitives-cli/src/subcommands/address.ts b/packages/wallet/primitives-cli/src/subcommands/address.ts index 085fb9598..a5cb50a20 100644 --- a/packages/wallet/primitives-cli/src/subcommands/address.ts +++ b/packages/wallet/primitives-cli/src/subcommands/address.ts @@ -1,20 +1,20 @@ -import { Address, Bytes } from 'ox' +import { Bytes, Hex } from 'ox' import type { CommandModule } from 'yargs' -import { Address as SequenceAddress, Context } from '@0xsequence/wallet-primitives' +import { Address, Context } from '@0xsequence/wallet-primitives' export async function doCalculateAddress(options: { - imageHash: string - factory: string - module: string - creationCode?: string + imageHash: Hex.Hex + factory: Address.Checksummed + module: Address.Checksummed + creationCode?: Hex.Hex }): Promise { const context = { - factory: Address.from(options.factory), - stage1: Address.from(options.module), - creationCode: (options.creationCode || Context.Dev2.creationCode) as `0x${string}`, + factory: options.factory, + stage1: options.module, + creationCode: options.creationCode || Context.Dev2.creationCode, } - return SequenceAddress.from(Bytes.fromHex(options.imageHash as `0x${string}`), context) + return Address.fromDeployConfiguration(Bytes.fromHex(options.imageHash), context) } const addressCommand: CommandModule = { @@ -50,11 +50,15 @@ const addressCommand: CommandModule = { }, async (argv) => { const { imageHash, factory, module, creationCode } = argv + + Hex.assert(imageHash) + Hex.assert(creationCode) + console.log( await doCalculateAddress({ - imageHash: imageHash!, - factory: factory!, - module: module!, + imageHash, + factory: Address.checksum(factory), + module: Address.checksum(module), creationCode, }), ) diff --git a/packages/wallet/primitives-cli/src/subcommands/config.ts b/packages/wallet/primitives-cli/src/subcommands/config.ts index cf3e96bd9..9b85deb0f 100644 --- a/packages/wallet/primitives-cli/src/subcommands/config.ts +++ b/packages/wallet/primitives-cli/src/subcommands/config.ts @@ -1,7 +1,7 @@ import type { CommandModule } from 'yargs' -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { fromPosOrStdin } from '../utils.js' -import { Signature, Config } from '@0xsequence/wallet-primitives' +import { Address, Signature, Config } from '@0xsequence/wallet-primitives' export const PossibleElements = [ { @@ -46,36 +46,45 @@ function parseElements(elements: string): Config.Leaf[] { const firstElementType = firstElement!.split(':')[0] if (firstElementType === 'signer') { const [_, address, weight] = firstElement!.split(':') + if (address === undefined) { + throw new Error('no address for signer') + } + if (weight === undefined) { + throw new Error('no weight for signer') + } leaves.push({ type: 'signer', - address: Address.from(address!), - weight: BigInt(weight!), + address: Address.checksum(address), + weight: BigInt(weight), }) remainingElements = remainingElements.slice(firstElement!.length + 1) } else if (firstElementType === 'subdigest') { const [_, digest] = firstElement!.split(':') - leaves.push({ - type: 'subdigest', - digest: digest as `0x${string}`, - }) + Hex.assert(digest) + leaves.push({ type: 'subdigest', digest }) remainingElements = remainingElements.slice(firstElement!.length + 1) } else if (firstElementType === 'any-address-subdigest') { const [_, digest] = firstElement!.split(':') - leaves.push({ - type: 'any-address-subdigest', - digest: digest as `0x${string}`, - }) + Hex.assert(digest) + leaves.push({ type: 'any-address-subdigest', digest }) remainingElements = remainingElements.slice(firstElement!.length + 1) } else if (firstElementType === 'sapient') { const [_, imageHash, address, weight] = firstElement!.split(':') - if (!imageHash || !imageHash.startsWith('0x') || imageHash.length !== 66) { + Hex.assert(imageHash) + if (imageHash.length !== 66) { throw new Error(`Invalid image hash: ${imageHash}`) } + if (address === undefined) { + throw new Error('no address for signer') + } + if (weight === undefined) { + throw new Error('no weight for signer') + } leaves.push({ type: 'sapient-signer', - imageHash: imageHash as `0x${string}`, - address: Address.from(address!), - weight: BigInt(weight!), + imageHash, + address: Address.checksum(address), + weight: BigInt(weight), }) remainingElements = remainingElements.slice(firstElement!.length + 1) } else if (firstElementType === 'nested') { @@ -97,7 +106,8 @@ function parseElements(elements: string): Config.Leaf[] { remainingElements = remainingElements.slice(endSubElements + 1).trim() } else if (firstElementType === 'node') { const [_, hash] = firstElement!.split(':') - leaves.push(hash as Hex.Hex) + Hex.assert(hash) + leaves.push(hash) remainingElements = remainingElements.slice(firstElement!.length + 1) } else { throw new Error(`Invalid element: ${firstElement}`) @@ -120,7 +130,7 @@ export async function createConfig(options: { checkpoint: BigInt(options.checkpoint), // Starts with empty topology topology: Config.flatLeavesToTopology(leaves), - checkpointer: options.checkpointer ? Address.from(options.checkpointer) : undefined, + checkpointer: options.checkpointer ? Address.checksum(options.checkpointer) : undefined, } return Config.configToJson(config) diff --git a/packages/wallet/primitives-cli/src/subcommands/devTools.ts b/packages/wallet/primitives-cli/src/subcommands/devTools.ts index aca4cd546..1a6ef1c92 100644 --- a/packages/wallet/primitives-cli/src/subcommands/devTools.ts +++ b/packages/wallet/primitives-cli/src/subcommands/devTools.ts @@ -1,6 +1,6 @@ -import { Permission, SessionConfig, Config } from '@0xsequence/wallet-primitives' +import { Address, Permission, SessionConfig, Config } from '@0xsequence/wallet-primitives' import crypto from 'crypto' -import { Bytes } from 'ox' +import { Bytes, Hex } from 'ox' import type { CommandModule } from 'yargs' export interface RandomOptions { @@ -41,7 +41,7 @@ function randomBytes(length: number, options?: RandomOptions): Uint8Array { return crypto.getRandomValues(bytes) } -function randomHex(length: number, options?: RandomOptions): `0x${string}` { +function randomHex(length: number, options?: RandomOptions): Hex.Hex { return Bytes.toHex(randomBytes(length, options)) } @@ -52,8 +52,8 @@ function randomBigInt(max: bigint, options?: RandomOptions): bigint { return BigInt(Math.floor(Math.random() * Number(max))) } -function randomAddress(options?: RandomOptions): `0x${string}` { - return `0x${Buffer.from(randomBytes(20, options)).toString('hex')}` +function randomAddress(options?: RandomOptions): Address.Checksummed { + return Address.checksum(`0x${Buffer.from(randomBytes(20, options)).toString('hex')}`) } function generateRandomTopology(depth: number, options?: RandomOptions): Config.Topology { diff --git a/packages/wallet/primitives-cli/src/subcommands/passkeys.ts b/packages/wallet/primitives-cli/src/subcommands/passkeys.ts index 5858409be..e5e50a73d 100644 --- a/packages/wallet/primitives-cli/src/subcommands/passkeys.ts +++ b/packages/wallet/primitives-cli/src/subcommands/passkeys.ts @@ -7,14 +7,14 @@ import { Extensions } from '@0xsequence/wallet-primitives' // Reusable function for encoding a signature export async function doEncodeSignature(options: { - x: string - y: string + x: Hex.Hex + y: Hex.Hex requireUserVerification: boolean credentialId?: string - metadataHash?: string - r: string - s: string - authenticatorData: string + metadataHash?: Hex.Hex + r: Hex.Hex + s: Hex.Hex + authenticatorData: Hex.Hex clientDataJson: string | object embedMetadata: boolean }): Promise { @@ -26,21 +26,17 @@ export async function doEncodeSignature(options: { } const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, + x: options.x, + y: options.y, requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, + metadata: options.credentialId ? { credentialId: options.credentialId } : options.metadataHash, } const decodedSignature: Extensions.Passkeys.DecodedSignature = { publicKey, - r: Bytes.fromHex(options.r as Hex.Hex), - s: Bytes.fromHex(options.s as Hex.Hex), - authenticatorData: Bytes.fromHex(options.authenticatorData as Hex.Hex), + r: Bytes.fromHex(options.r), + s: Bytes.fromHex(options.s), + authenticatorData: Bytes.fromHex(options.authenticatorData), clientDataJSON: typeof options.clientDataJson === 'string' ? options.clientDataJson : JSON.stringify(options.clientDataJson), embedMetadata: options.embedMetadata, @@ -51,8 +47,8 @@ export async function doEncodeSignature(options: { } // Reusable function for decoding a signature -export async function doDecodeSignature(encodedSignatureHex: string): Promise { - const encodedBytes = Bytes.fromHex(encodedSignatureHex as Hex.Hex) +export async function doDecodeSignature(encodedSignature: Hex.Hex): Promise { + const encodedBytes = Bytes.fromHex(encodedSignature) const decoded = Extensions.Passkeys.decode(encodedBytes) // Convert bytes back to hex for readability in JSON output @@ -75,25 +71,21 @@ export async function doDecodeSignature(encodedSignatureHex: string): Promise { if (options.credentialId && options.metadataHash) { throw new Error('Cannot provide both credential-id and metadata-hash') } const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, + x: options.x, + y: options.y, requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, + metadata: options.credentialId ? { credentialId: options.credentialId } : options.metadataHash, } const root = Extensions.Passkeys.rootFor(publicKey) @@ -102,15 +94,15 @@ export async function doComputeRoot(options: { // Reusable function for validating a signature export async function doValidateSignature(options: { - challenge: string - x: string - y: string + challenge: Hex.Hex + x: Hex.Hex + y: Hex.Hex requireUserVerification: boolean credentialId?: string - metadataHash?: string - r: string - s: string - authenticatorData: string + metadataHash?: Hex.Hex + r: Hex.Hex + s: Hex.Hex + authenticatorData: Hex.Hex clientDataJson: string }): Promise { if (options.credentialId && options.metadataHash) { @@ -118,26 +110,22 @@ export async function doValidateSignature(options: { } const publicKey: Extensions.Passkeys.PublicKey = { - x: options.x as Hex.Hex, - y: options.y as Hex.Hex, + x: options.x, + y: options.y, requireUserVerification: options.requireUserVerification, - metadata: options.credentialId - ? { credentialId: options.credentialId } - : options.metadataHash - ? (options.metadataHash as Hex.Hex) - : undefined, + metadata: options.credentialId ? { credentialId: options.credentialId } : options.metadataHash, } // Construct DecodedSignature without embedMetadata flag, as validation doesn't need it directly const decodedSignature: Omit = { publicKey, - r: Bytes.fromHex(options.r as Hex.Hex), - s: Bytes.fromHex(options.s as Hex.Hex), - authenticatorData: Bytes.fromHex(options.authenticatorData as Hex.Hex), + r: Bytes.fromHex(options.r), + s: Bytes.fromHex(options.s), + authenticatorData: Bytes.fromHex(options.authenticatorData), clientDataJSON: options.clientDataJson, } - return Extensions.Passkeys.isValidSignature(options.challenge as Hex.Hex, decodedSignature) + return Extensions.Passkeys.isValidSignature(options.challenge, decodedSignature) } const passkeysCommand: CommandModule = { @@ -182,6 +170,13 @@ const passkeysCommand: CommandModule = { .conflicts('credential-id', 'metadata-hash') }, async (argv) => { + Hex.assert(argv.x) + Hex.assert(argv.y) + Hex.assert(argv.metadataHash) + Hex.assert(argv.r) + Hex.assert(argv.s) + Hex.assert(argv.authenticatorData) + const result = await doEncodeSignature({ x: argv.x, y: argv.y, @@ -207,8 +202,10 @@ const passkeysCommand: CommandModule = { }) }, async (argv) => { - const encodedSignatureHex = await fromPosOrStdin(argv, 'encoded-signature') - const result = await doDecodeSignature(encodedSignatureHex) + const encodedSignature = await fromPosOrStdin(argv, 'encoded-signature') + Hex.assert(encodedSignature) + + const result = await doDecodeSignature(encodedSignature) console.log(result) }, ) @@ -232,6 +229,10 @@ const passkeysCommand: CommandModule = { .conflicts('credential-id', 'metadata-hash') }, async (argv) => { + Hex.assert(argv.x) + Hex.assert(argv.y) + Hex.assert(argv.metadataHash) + const result = await doComputeRoot({ x: argv.x, y: argv.y, @@ -275,6 +276,14 @@ const passkeysCommand: CommandModule = { .conflicts('credential-id', 'metadata-hash') }, async (argv) => { + Hex.assert(argv.challenge) + Hex.assert(argv.x) + Hex.assert(argv.y) + Hex.assert(argv.metadataHash) + Hex.assert(argv.r) + Hex.assert(argv.s) + Hex.assert(argv.authenticatorData) + const isValid = await doValidateSignature({ challenge: argv.challenge, x: argv.x, diff --git a/packages/wallet/primitives-cli/src/subcommands/payload.ts b/packages/wallet/primitives-cli/src/subcommands/payload.ts index 51c80aa32..f30e1ab4f 100644 --- a/packages/wallet/primitives-cli/src/subcommands/payload.ts +++ b/packages/wallet/primitives-cli/src/subcommands/payload.ts @@ -1,6 +1,6 @@ -import { AbiParameters, Address, Hex } from 'ox' +import { AbiParameters, Hex } from 'ox' import type { CommandModule } from 'yargs' -import { Payload } from '@0xsequence/wallet-primitives' +import { Address, Payload } from '@0xsequence/wallet-primitives' import { fromPosOrStdin } from '../utils.js' const CallAbi = [ @@ -34,39 +34,38 @@ export async function doConvertToAbi(_payload: string): Promise { throw new Error('Not implemented') } -export async function doConvertToPacked(payload: string, wallet?: string): Promise { +export async function doConvertToPacked(payload: Hex.Hex, wallet?: Address.Checksummed): Promise { const decodedPayload = Payload.fromAbiFormat( AbiParameters.decode( [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, + payload, )[0] as unknown as Payload.SolidityDecoded, ) if (Payload.isCalls(decodedPayload)) { - const packed = Payload.encode(decodedPayload, wallet ? (wallet as `0x${string}`) : undefined) + const packed = Payload.encode(decodedPayload, wallet) return Hex.from(packed) } throw new Error('Not implemented') } -export async function doConvertToJson(payload: string): Promise { +export async function doConvertToJson(payload: Hex.Hex): Promise { const decoded = AbiParameters.decode( [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, + payload, )[0] as unknown as Payload.SolidityDecoded - const json = JSON.stringify(decoded) - return json + return JSON.stringify(decoded) } -export async function doHash(wallet: string, chainId: bigint, payload: string): Promise { +export async function doHash(wallet: Address.Checksummed, chainId: bigint, payload: Hex.Hex): Promise { const decoded = AbiParameters.decode( [{ type: 'tuple', name: 'payload', components: DecodedAbi }], - payload as Hex.Hex, + payload, )[0] as unknown as Payload.SolidityDecoded - return Hex.from(Payload.hash(Address.from(wallet), chainId, Payload.fromAbiFormat(decoded))) + return Hex.from(Payload.hash(wallet, chainId, Payload.fromAbiFormat(decoded))) } const payloadCommand: CommandModule = { @@ -106,7 +105,8 @@ const payloadCommand: CommandModule = { }, async (argv) => { const payload = await fromPosOrStdin(argv, 'payload') - const result = await doConvertToPacked(payload, argv.wallet) + Hex.assert(payload) + const result = await doConvertToPacked(payload, argv.wallet ? Address.checksum(argv.wallet) : undefined) console.log(result) }, ) @@ -121,6 +121,7 @@ const payloadCommand: CommandModule = { }, async (argv) => { const payload = await fromPosOrStdin(argv, 'payload') + Hex.assert(payload) const result = await doConvertToJson(payload) console.log(result) }, @@ -147,7 +148,8 @@ const payloadCommand: CommandModule = { }, async (argv) => { const payload = await fromPosOrStdin(argv, 'payload') - const result = await doHash(argv.wallet, BigInt(argv.chainId), payload) + Hex.assert(payload) + const result = await doHash(Address.checksum(argv.wallet), BigInt(argv.chainId), payload) console.log(result) }, ) diff --git a/packages/wallet/primitives-cli/src/subcommands/recovery.ts b/packages/wallet/primitives-cli/src/subcommands/recovery.ts index fb9a0a03d..b19862019 100644 --- a/packages/wallet/primitives-cli/src/subcommands/recovery.ts +++ b/packages/wallet/primitives-cli/src/subcommands/recovery.ts @@ -1,7 +1,7 @@ import { CommandModule } from 'yargs' import { readStdin } from '../utils.js' -import { Address, Bytes, Hex } from 'ox' -import { Extensions } from '@0xsequence/wallet-primitives' +import { Bytes, Hex } from 'ox' +import { Address, Extensions } from '@0xsequence/wallet-primitives' async function parseLeaves(leavesInput: string | string[]): Promise { if (typeof leavesInput === 'string') { @@ -14,6 +14,9 @@ async function parseLeaves(leavesInput: string | string[]): Promise return Bytes.toHex(encoded) } -export async function doTrim(leavesInput: string | string[], signer: string): Promise { +export async function doTrim(leavesInput: string | string[], signer: Address.Checksummed): Promise { const leaves = await parseLeaves(leavesInput) const topology = Extensions.Recovery.fromRecoveryLeaves(leaves) - const trimmed = Extensions.Recovery.trimTopology(topology, signer as Address.Address) + const trimmed = Extensions.Recovery.trimTopology(topology, signer) const encoded = Extensions.Recovery.encodeTopology(trimmed) return Bytes.toHex(encoded) } @@ -152,9 +155,8 @@ const recoveryCommand: CommandModule = { .map((line) => line.trim()) .filter((line) => line) } - const signer = argv.signer try { - const encoded = await doTrim(leavesInput, signer) + const encoded = await doTrim(leavesInput, Address.checksum(argv.signer)) console.log(encoded) } catch (error) { console.error((error as Error).message) diff --git a/packages/wallet/primitives-cli/src/subcommands/session.ts b/packages/wallet/primitives-cli/src/subcommands/session.ts index 3d343d1ab..56fda9d18 100644 --- a/packages/wallet/primitives-cli/src/subcommands/session.ts +++ b/packages/wallet/primitives-cli/src/subcommands/session.ts @@ -3,9 +3,9 @@ import { CommandModule } from 'yargs' import sessionExplicitCommand from './sessionExplicit.js' import sessionImplicitCommand from './sessionImplicit.js' -import { GenericTree, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' +import { Address, GenericTree, SessionConfig, SessionSignature } from '@0xsequence/wallet-primitives' -export async function doEmptyTopology(identitySigner: `0x${string}`): Promise { +export async function doEmptyTopology(identitySigner: Address.Checksummed): Promise { const topology = SessionConfig.emptySessionsTopology(identitySigner) return SessionConfig.sessionsTopologyToJson(topology) } @@ -19,16 +19,16 @@ export async function doEncodeTopology(sessionTopologyInput: string): Promise { const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) const callSignatures = callSignaturesInput.map((s) => SessionSignature.sessionCallSignatureFromJson(s)) const encoded = SessionSignature.encodeSessionCallSignatures( callSignatures, sessionTopology, - explicitSigners as `0x${string}`[], - implicitSigners as `0x${string}`[], + explicitSigners, + implicitSigners, ) return Hex.from(encoded) } @@ -57,7 +57,7 @@ const sessionCommand: CommandModule = { }) }, async (args) => { - console.log(await doEmptyTopology(args.identitySigner as `0x${string}`)) + console.log(await doEmptyTopology(Address.checksum(args.identitySigner))) }, ) .command( @@ -112,8 +112,8 @@ const sessionCommand: CommandModule = { await doEncodeSessionCallSignatures( args.sessionTopology, args.callSignatures, - args.explicitSigners, - args.implicitSigners, + args.explicitSigners.map(Address.checksum), + args.implicitSigners.map(Address.checksum), ), ) }, diff --git a/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts b/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts index 3f9d775e3..9626836b1 100644 --- a/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts +++ b/packages/wallet/primitives-cli/src/subcommands/sessionExplicit.ts @@ -1,6 +1,6 @@ +import { Address, Permission, SessionConfig } from '@0xsequence/wallet-primitives' import type { CommandModule } from 'yargs' import { fromPosOrStdin } from '../utils.js' -import { Permission, SessionConfig } from '@0xsequence/wallet-primitives' export async function doAddSession(sessionInput: string, topologyInput: string): Promise { const session = Permission.sessionPermissionsFromJson(sessionInput) @@ -20,15 +20,15 @@ export async function doAddSession(sessionInput: string, topologyInput: string): return SessionConfig.sessionsTopologyToJson(topology) } -export async function doRemoveSession(explicitSessionAddress: string, topologyInput: string): Promise { +export async function doRemoveSession( + explicitSessionAddress: Address.Checksummed, + topologyInput: string, +): Promise { const topology = SessionConfig.sessionsTopologyFromJson(topologyInput) if (!SessionConfig.isSessionsTopology(topology)) { throw new Error('Session topology must be a valid session topology') } - if (!explicitSessionAddress || !explicitSessionAddress.startsWith('0x')) { - throw new Error('Explicit session address must be a valid address') - } - const updated = SessionConfig.removeExplicitSession(topology, explicitSessionAddress as `0x${string}`) + const updated = SessionConfig.removeExplicitSession(topology, explicitSessionAddress) if (!updated) { throw new Error('Session topology is empty') } @@ -82,9 +82,9 @@ const sessionExplicitCommand: CommandModule = { }) }, async (argv) => { - const explicitSessionAddress = argv.explicitSessionAddress + const explicitSessionAddress = Address.checksum(argv.explicitSessionAddress) const topologyInput = await fromPosOrStdin(argv, 'session-topology') - console.log(await doRemoveSession(explicitSessionAddress!, topologyInput)) + console.log(await doRemoveSession(explicitSessionAddress, topologyInput)) }, ) .demandCommand(1, 'You must specify a subcommand for session') diff --git a/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts b/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts index 713e419b9..c24188131 100644 --- a/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts +++ b/packages/wallet/primitives-cli/src/subcommands/sessionImplicit.ts @@ -1,20 +1,22 @@ -import { SessionConfig } from '@0xsequence/wallet-primitives' -import { Address } from 'ox' +import { Address, SessionConfig } from '@0xsequence/wallet-primitives' import type { CommandModule } from 'yargs' -import { fromPosOrStdin, requireString } from '../utils.js' +import { fromPosOrStdin } from '../utils.js' -export async function doAddBlacklistAddress(blacklistAddress: string, sessionTopologyInput: string): Promise { +export async function doAddBlacklistAddress( + blacklistAddress: Address.Checksummed, + sessionTopologyInput: string, +): Promise { const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const updated = SessionConfig.addToImplicitBlacklist(sessionTopology, blacklistAddress as Address.Address) + const updated = SessionConfig.addToImplicitBlacklist(sessionTopology, blacklistAddress) return SessionConfig.sessionsTopologyToJson(updated) } export async function doRemoveBlacklistAddress( - blacklistAddress: string, + blacklistAddress: Address.Checksummed, sessionTopologyInput: string, ): Promise { const sessionTopology = SessionConfig.sessionsTopologyFromJson(sessionTopologyInput) - const updated = SessionConfig.removeFromImplicitBlacklist(sessionTopology, blacklistAddress as Address.Address) + const updated = SessionConfig.removeFromImplicitBlacklist(sessionTopology, blacklistAddress) return SessionConfig.sessionsTopologyToJson(updated) } @@ -40,8 +42,7 @@ const sessionImplicitCommand: CommandModule = { }) }, async (argv) => { - const blacklistAddress = argv.blacklistAddress - requireString(blacklistAddress, 'Blacklist address') + const blacklistAddress = Address.checksum(argv.blacklistAddress) const sessionTopologyInput = await fromPosOrStdin(argv, 'session-topology') console.log(await doAddBlacklistAddress(blacklistAddress, sessionTopologyInput)) }, @@ -63,10 +64,7 @@ const sessionImplicitCommand: CommandModule = { }) }, async (argv) => { - const blacklistAddress = argv.blacklistAddress as string - if (!blacklistAddress) { - throw new Error('Blacklist address is required') - } + const blacklistAddress = Address.checksum(argv.blacklistAddress) const sessionTopologyInput = await fromPosOrStdin(argv, 'session-topology') console.log(await doRemoveBlacklistAddress(blacklistAddress, sessionTopologyInput)) }, diff --git a/packages/wallet/primitives-cli/src/subcommands/signature.ts b/packages/wallet/primitives-cli/src/subcommands/signature.ts index 0dacf0da5..b213245cf 100644 --- a/packages/wallet/primitives-cli/src/subcommands/signature.ts +++ b/packages/wallet/primitives-cli/src/subcommands/signature.ts @@ -1,5 +1,5 @@ -import { Config, Signature } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, Signature as OxSignature } from 'ox' +import { Address, Config, Signature } from '@0xsequence/wallet-primitives' +import { Bytes, Hex, Signature as OxSignature } from 'ox' import { type CommandModule } from 'yargs' import { fromPosOrStdin } from '../utils.js' import { PossibleElements } from './config.js' @@ -36,14 +36,17 @@ export async function doEncode( input: string, signatures: string[] = [], noChainId: boolean, - checkpointerData?: string, + checkpointerData?: Hex.Hex, ): Promise { const config = Config.configFromJson(input) const allSignatures = signatures.filter(Boolean).map((s) => { const values = s.split(':') + if (values[0] === undefined) { + throw new Error('no address for signature') + } return { - address: Address.from(values[0] as `0x${string}`), + address: Address.checksum(values[0]), type: values[1], values: values.slice(2), } @@ -59,26 +62,31 @@ export async function doEncode( } if (candidate.type === 'erc1271') { + Hex.assert(candidate.values[0]) return { - address: candidate.address as `0x${string}`, - data: candidate.values[0] as `0x${string}`, + address: candidate.address, + data: candidate.values[0], type: 'erc1271', } } if (candidate.type === 'eth_sign') { + Hex.assert(candidate.values[0]) + Hex.assert(candidate.values[1]) return { - r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0] as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1] as `0x${string}`, { size: 32 })), + r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0], { size: 32 })), + s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1], { size: 32 })), yParity: OxSignature.vToYParity(Number(candidate.values[2])), type: 'eth_sign', } } if (candidate.type === 'hash') { + Hex.assert(candidate.values[0]) + Hex.assert(candidate.values[1]) return { - r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0] as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1] as `0x${string}`, { size: 32 })), + r: Bytes.toBigInt(Bytes.fromHex(candidate.values[0], { size: 32 })), + s: Bytes.toBigInt(Bytes.fromHex(candidate.values[1], { size: 32 })), yParity: OxSignature.vToYParity(Number(candidate.values[2])), type: 'hash', } @@ -98,9 +106,10 @@ export async function doEncode( } if (candidate.type === 'sapient' || candidate.type === 'sapient_compact') { + Hex.assert(candidate.values[0]) return { - address: candidate.address as `0x${string}`, - data: candidate.values[0] as `0x${string}`, + address: candidate.address, + data: candidate.values[0], type: candidate.type, } } @@ -118,18 +127,18 @@ export async function doEncode( const encoded = Signature.encodeSignature({ noChainId, configuration: { ...config, topology: fullTopology }, - checkpointerData: checkpointerData ? Bytes.fromHex(checkpointerData as `0x${string}`) : undefined, + checkpointerData: checkpointerData ? Bytes.fromHex(checkpointerData) : undefined, }) return Hex.fromBytes(encoded) } -export async function doConcat(signatures: string[]): Promise { +export async function doConcat(signatures: Hex.Hex[]): Promise { if (signatures.length === 0) { throw new Error('No signatures provided') } - const decoded = signatures.map((s) => Signature.decodeSignature(Bytes.fromHex(s as `0x${string}`))) + const decoded = signatures.map((s) => Signature.decodeSignature(Bytes.fromHex(s))) const reEncoded = Signature.encodeSignature({ ...decoded[0]!, @@ -139,8 +148,8 @@ export async function doConcat(signatures: string[]): Promise { return Hex.fromBytes(reEncoded) } -export async function doDecode(signature: string): Promise { - const bytes = Bytes.fromHex(signature as `0x${string}`) +export async function doDecode(signature: Hex.Hex): Promise { + const bytes = Bytes.fromHex(signature) const decoded = Signature.decodeSignature(bytes) return Signature.rawSignatureToJson(decoded) } @@ -182,6 +191,7 @@ const signatureCommand: CommandModule = { }, async (argv) => { const input = await fromPosOrStdin(argv, 'input') + Hex.assert(argv.checkpointerData) console.log(await doEncode(input, argv.signature, !argv.chainId, argv.checkpointerData)) }, ) @@ -197,7 +207,14 @@ const signatureCommand: CommandModule = { }) }, async (argv) => { - console.log(await doConcat(argv.signatures)) + console.log( + await doConcat( + argv.signatures.map((signature) => { + Hex.assert(signature) + return signature + }), + ), + ) }, ) .command( @@ -212,6 +229,7 @@ const signatureCommand: CommandModule = { }, async (argv) => { const input = await fromPosOrStdin(argv, 'signature') + Hex.assert(input) console.log(await doDecode(input)) }, ) diff --git a/packages/wallet/primitives-cli/src/utils.ts b/packages/wallet/primitives-cli/src/utils.ts index d4bc27ab5..8899d8527 100644 --- a/packages/wallet/primitives-cli/src/utils.ts +++ b/packages/wallet/primitives-cli/src/utils.ts @@ -29,9 +29,3 @@ export async function fromPosOrStdin(argv: Arguments, arg: keyof T): Promi return await readStdin() } - -export function requireString(arg: string | undefined, name: string): asserts arg is string { - if (!arg) { - throw new Error(`${name} is required`) - } -} diff --git a/packages/wallet/primitives/src/address.ts b/packages/wallet/primitives/src/address.ts index 4de08a39d..2183fa427 100644 --- a/packages/wallet/primitives/src/address.ts +++ b/packages/wallet/primitives/src/address.ts @@ -1,19 +1,42 @@ -import { Address, Bytes, Hash } from 'ox' +import { Address, Bytes, Hash, PublicKey } from 'ox' import { Context } from './context.js' import { Config, hashConfiguration } from './config.js' -export function from(configuration: Bytes.Bytes | Config, context: Omit): Address.Address { +export type Checksummed = Address.Address & { readonly _checksummed: unique symbol } + +export function checksum(address: string): Checksummed { + return Address.checksum(address) as Checksummed +} + +export function isChecksummed(address: any): address is Checksummed { + return typeof address === 'string' && Address.validate(address) && address === Address.checksum(address) +} + +export function isEqual(a: Checksummed, b: Checksummed): boolean { + return a === b +} + +export function fromPublicKey(publicKey: PublicKey.PublicKey, options?: Address.fromPublicKey.Options): Checksummed { + return checksum(Address.fromPublicKey(publicKey, options)) +} + +export function fromDeployConfiguration( + configuration: Bytes.Bytes | Config, + context: Omit, +): Checksummed { const imageHash = configuration instanceof Uint8Array ? configuration : hashConfiguration(configuration) - return Bytes.toHex( - Hash.keccak256( - Bytes.concat( - Bytes.from('0xff'), - Bytes.from(context.factory), - imageHash, - Hash.keccak256(Bytes.concat(Bytes.from(context.creationCode), Bytes.padLeft(Bytes.from(context.stage1), 32))), - ), - { as: 'Bytes' }, - ).subarray(12), + return checksum( + Bytes.toHex( + Hash.keccak256( + Bytes.concat( + Bytes.from('0xff'), + Bytes.from(context.factory), + imageHash, + Hash.keccak256(Bytes.concat(Bytes.from(context.creationCode), Bytes.padLeft(Bytes.from(context.stage1), 32))), + ), + { as: 'Bytes' }, + ).subarray(12), + ), ) } diff --git a/packages/wallet/primitives/src/attestation.ts b/packages/wallet/primitives/src/attestation.ts index 78795862e..9831102b9 100644 --- a/packages/wallet/primitives/src/attestation.ts +++ b/packages/wallet/primitives/src/attestation.ts @@ -1,7 +1,8 @@ -import { Address, Bytes, Hash } from 'ox' +import { Bytes, Hash } from 'ox' +import { checksum, Checksummed } from './address.js' export type Attestation = { - approvedSigner: Address.Address + approvedSigner: Checksummed identityType: Bytes.Bytes // bytes4 issuerHash: Bytes.Bytes // bytes32 audienceHash: Bytes.Bytes // bytes32 @@ -39,7 +40,7 @@ export function encodeAuthData(authData: AuthData): Bytes.Bytes { } export function decode(bytes: Bytes.Bytes): Attestation { - const approvedSigner = Bytes.toHex(bytes.slice(0, 20)) + const approvedSigner = checksum(Bytes.toHex(bytes.slice(0, 20))) const identityType = bytes.slice(20, 24) const issuerHash = bytes.slice(24, 56) const audienceHash = bytes.slice(56, 88) @@ -96,7 +97,7 @@ export function fromJson(json: string): Attestation { export function fromParsed(parsed: any): Attestation { return { - approvedSigner: Address.from(parsed.approvedSigner), + approvedSigner: checksum(parsed.approvedSigner), identityType: Bytes.fromHex(parsed.identityType), issuerHash: Bytes.fromHex(parsed.issuerHash), audienceHash: Bytes.fromHex(parsed.audienceHash), @@ -112,7 +113,7 @@ export function fromParsed(parsed: any): Attestation { export const ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX = Hash.keccak256(Bytes.fromString('acceptImplicitRequest')) -export function generateImplicitRequestMagic(attestation: Attestation, wallet: Address.Address): Bytes.Bytes { +export function generateImplicitRequestMagic(attestation: Attestation, wallet: Checksummed): Bytes.Bytes { return Hash.keccak256( Bytes.concat( ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, diff --git a/packages/wallet/primitives/src/config.ts b/packages/wallet/primitives/src/config.ts index f6274c573..07f6caa2c 100644 --- a/packages/wallet/primitives/src/config.ts +++ b/packages/wallet/primitives/src/config.ts @@ -1,4 +1,5 @@ -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { Checksummed, isEqual } from './address.js' import { isRawConfig, isRawNestedLeaf, @@ -15,7 +16,7 @@ import { Constants } from './index.js' export type SignerLeaf = { type: 'signer' - address: Address.Address + address: Checksummed weight: bigint signed?: boolean signature?: SignatureOfSignerLeaf @@ -23,7 +24,7 @@ export type SignerLeaf = { export type SapientSignerLeaf = { type: 'sapient-signer' - address: Address.Address + address: Checksummed weight: bigint imageHash: Hex.Hex signed?: boolean @@ -59,7 +60,7 @@ export type Config = { threshold: bigint checkpoint: bigint topology: Topology - checkpointer?: Address.Address + checkpointer?: Checksummed } export function isSignerLeaf(cand: any): cand is SignerLeaf { @@ -110,12 +111,12 @@ export function isTopology(cand: any): cand is Topology { } export function getSigners(configuration: Config | Topology): { - signers: Address.Address[] - sapientSigners: { address: Address.Address; imageHash: Hex.Hex }[] + signers: Checksummed[] + sapientSigners: { address: Checksummed; imageHash: Hex.Hex }[] isComplete: boolean } { - const signers = new Set() - const sapientSigners = new Set<{ address: Address.Address; imageHash: Hex.Hex }>() + const signers = new Set() + const sapientSigners = new Set<{ address: Checksummed; imageHash: Hex.Hex }>() let isComplete = true @@ -144,18 +145,18 @@ export function getSigners(configuration: Config | Topology): { export function findSignerLeaf( configuration: Config | Topology, - address: Address.Address, + address: Checksummed, ): SignerLeaf | SapientSignerLeaf | undefined { if (isConfig(configuration)) { return findSignerLeaf(configuration.topology, address) } else if (isNode(configuration)) { return findSignerLeaf(configuration[0], address) || findSignerLeaf(configuration[1], address) } else if (isSignerLeaf(configuration)) { - if (Address.isEqual(configuration.address, address)) { + if (isEqual(configuration.address, address)) { return configuration } } else if (isSapientSignerLeaf(configuration)) { - if (Address.isEqual(configuration.address, address)) { + if (isEqual(configuration.address, address)) { return configuration } } @@ -347,7 +348,8 @@ function decodeTopology(obj: any): Topology { } if (typeof obj === 'string') { - return obj as Hex.Hex + Hex.assert(obj) + return obj } switch (obj.type) { @@ -416,33 +418,33 @@ type CancelCallback = (success: boolean) => void type MaybePromise = T | Promise export function mergeTopology(a: Topology, b: Topology): Topology { - if (isNode(a) && isNode(b)) { - return [mergeTopology(a[0], b[0]), mergeTopology(a[1], b[1])] - } - - if (isNode(a) && !isNode(b)) { - if (!isNodeLeaf(b)) { - throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') - } - const hb = hashConfiguration(b) - if (!Bytes.isEqual(hb, hashConfiguration(a))) { - throw new Error('Topology mismatch: node hash does not match') - } - return a - } - - if (!isNode(a) && isNode(b)) { - if (!isNodeLeaf(a)) { - throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') + if (isNode(a)) { + if (isNode(b)) { + return [mergeTopology(a[0], b[0]), mergeTopology(a[1], b[1])] + } else { + if (!isNodeLeaf(b)) { + throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') + } + const hb = hashConfiguration(b) + if (!Bytes.isEqual(hb, hashConfiguration(a))) { + throw new Error('Topology mismatch: node hash does not match') + } + return a } - const ha = hashConfiguration(a) - if (!Bytes.isEqual(ha, hashConfiguration(b))) { - throw new Error('Topology mismatch: node hash does not match') + } else { + if (isNode(b)) { + if (!isNodeLeaf(a)) { + throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf') + } + const ha = hashConfiguration(a) + if (!Bytes.isEqual(ha, hashConfiguration(b))) { + throw new Error('Topology mismatch: node hash does not match') + } + return b + } else { + return mergeLeaf(a, b) } - return b } - - return mergeLeaf(a as Leaf, b as Leaf) } /** diff --git a/packages/wallet/primitives/src/constants.ts b/packages/wallet/primitives/src/constants.ts index ce969f96c..73546206e 100644 --- a/packages/wallet/primitives/src/constants.ts +++ b/packages/wallet/primitives/src/constants.ts @@ -1,8 +1,9 @@ import { Abi } from 'ox' +import { checksum } from './address.js' -export const ZeroAddress = '0x0000000000000000000000000000000000000000' as const +export const ZeroAddress = checksum('0x0000000000000000000000000000000000000000') -export const DefaultGuestAddress = '0xf3c7175460BeD3340A1c4dc700fD6C8Cd3F56250' as const +export const DefaultGuestAddress = checksum('0xf3c7175460BeD3340A1c4dc700fD6C8Cd3F56250') // ERC1271 export const IS_VALID_SIGNATURE = Abi.from([ diff --git a/packages/wallet/primitives/src/context.ts b/packages/wallet/primitives/src/context.ts index 9adc8f52a..02cfb707b 100644 --- a/packages/wallet/primitives/src/context.ts +++ b/packages/wallet/primitives/src/context.ts @@ -1,41 +1,42 @@ -import { Address, Hex } from 'ox' +import { Hex } from 'ox' +import { checksum, Checksummed } from './address.js' export type Capabilities = { erc4337?: { - entrypoint: Address.Address + entrypoint: Checksummed } } export type Context = { - factory: Address.Address - stage1: Address.Address - stage2: Address.Address + factory: Checksummed + stage1: Checksummed + stage2: Checksummed creationCode: Hex.Hex capabilities?: Capabilities } export const Dev1: Context = { - factory: '0xe828630697817291140D6B7A42a2c3b7277bE45a', - stage1: '0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39', - stage2: '0xe1299E4456b267123F7Aba29B72C2164ff501BDa', + factory: checksum('0xe828630697817291140D6B7A42a2c3b7277bE45a'), + stage1: checksum('0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39'), + stage2: checksum('0xe1299E4456b267123F7Aba29B72C2164ff501BDa'), creationCode: '0x603e600e3d39601e805130553df33d3d34601c57363d3d373d363d30545af43d82803e903d91601c57fd5bf3', } export const Dev2: Context = { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD', - stage2: '0x90cb0a8ccf40bEdA60896e408bdc7801033447C6', + factory: checksum('0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010'), + stage1: checksum('0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD'), + stage2: checksum('0x90cb0a8ccf40bEdA60896e408bdc7801033447C6'), creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', } export const Dev2_4337: Context = { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x8Ae58FCc0Ee9b32994CA52c9854deb969DC8fa2A', - stage2: '0x30f8e3AceAcDEac8a3F28935D87FD58DC5f71ad2', + factory: checksum('0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010'), + stage1: checksum('0x8Ae58FCc0Ee9b32994CA52c9854deb969DC8fa2A'), + stage2: checksum('0x30f8e3AceAcDEac8a3F28935D87FD58DC5f71ad2'), creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', capabilities: { erc4337: { - entrypoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', + entrypoint: checksum('0x0000000071727De22E5E9d8BAf0edAc6f37da032'), }, }, } diff --git a/packages/wallet/primitives/src/erc-6492.ts b/packages/wallet/primitives/src/erc-6492.ts index a07de019c..bfbbd1e7c 100644 --- a/packages/wallet/primitives/src/erc-6492.ts +++ b/packages/wallet/primitives/src/erc-6492.ts @@ -1,15 +1,13 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hex, Provider } from 'ox' +import { AbiFunction, AbiParameters, Bytes, Hex, Provider } from 'ox' import { WrappedSignature } from 'ox/erc6492' +import { checksum, Checksummed } from './address.js' import { DEPLOY } from './constants.js' import { Context } from './context.js' const EIP_6492_OFFCHAIN_DEPLOY_CODE = '0x608060405234801561001057600080fd5b5060405161124a38038061124a83398101604081905261002f91610124565b600060405161003d906100dd565b604051809103906000f080158015610059573d6000803e3d6000fd5b5090506000816001600160a01b0316638f0684308686866040518463ffffffff1660e01b815260040161008e939291906101fb565b6020604051808303816000875af11580156100ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d19190610244565b9050806000526001601ff35b610fdc8061026e83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561011b578181015183820152602001610103565b50506000910152565b60008060006060848603121561013957600080fd5b83516001600160a01b038116811461015057600080fd5b6020850151604086015191945092506001600160401b038082111561017457600080fd5b818601915086601f83011261018857600080fd5b81518181111561019a5761019a6100ea565b604051601f8201601f19908116603f011681019083821181831017156101c2576101c26100ea565b816040528281528960208487010111156101db57600080fd5b6101ec836020830160208801610100565b80955050505050509250925092565b60018060a01b0384168152826020820152606060408201526000825180606084015261022e816080850160208701610100565b601f01601f191691909101608001949350505050565b60006020828403121561025657600080fd5b8151801515811461026657600080fd5b939250505056fe608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033' -export function deploy( - deployHash: T, - context: Context, -): { to: Address.Address; data: T } { +export function deploy(deployHash: T, context: Context): { to: Checksummed; data: T } { const encoded = AbiFunction.encodeData(DEPLOY, [context.stage1, Hex.from(deployHash)]) switch (typeof deployHash) { @@ -22,7 +20,7 @@ export function deploy( export function wrap( signature: T, - { to, data }: { to: Address.Address; data: Bytes.Bytes | Hex.Hex }, + { to, data }: { to: Checksummed; data: Bytes.Bytes | Hex.Hex }, ): T { const encoded = Hex.concat( AbiParameters.encode( @@ -42,7 +40,7 @@ export function wrap( export function decode( signature: T, -): { signature: T; erc6492?: { to: Address.Address; data: T } } { +): { signature: T; erc6492?: { to: Checksummed; data: T } } { switch (typeof signature) { case 'object': if ( @@ -53,7 +51,7 @@ export function decode( [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], signature.subarray(0, -WrappedSignature.magicBytes.slice(2).length / 2), ) - return { signature: Hex.toBytes(decoded) as T, erc6492: { to, data: Hex.toBytes(data) as T } } + return { signature: Hex.toBytes(decoded) as T, erc6492: { to: checksum(to), data: Hex.toBytes(data) as T } } } else { return { signature } } @@ -63,9 +61,9 @@ export function decode( try { const [to, data, decoded] = AbiParameters.decode( [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], - signature.slice(0, -WrappedSignature.magicBytes.slice(2).length) as Hex.Hex, + `0x${signature.slice(2, -WrappedSignature.magicBytes.slice(2).length)}`, ) - return { signature: decoded as T, erc6492: { to, data: data as T } } + return { signature: decoded as T, erc6492: { to: checksum(to), data: data as T } } } catch { return { signature } } @@ -76,7 +74,7 @@ export function decode( } export function isValid( - address: Address.Address, + address: Checksummed, messageHash: Bytes.Bytes | Hex.Hex, encodedSignature: Bytes.Bytes | Hex.Hex, provider: Provider.Provider, diff --git a/packages/wallet/primitives/src/extensions/index.ts b/packages/wallet/primitives/src/extensions/index.ts index 7d59c24bd..14066f531 100644 --- a/packages/wallet/primitives/src/extensions/index.ts +++ b/packages/wallet/primitives/src/extensions/index.ts @@ -1,21 +1,21 @@ -import { Address } from 'ox' +import { checksum, Checksummed } from '../address.js' export type Extensions = { - passkeys: Address.Address - recovery: Address.Address - sessions: Address.Address + passkeys: Checksummed + recovery: Checksummed + sessions: Checksummed } export const Dev1: Extensions = { - passkeys: '0x8f26281dB84C18aAeEa8a53F94c835393229d296', - recovery: '0xd98da48C4FF9c19742eA5856A277424557C863a6', - sessions: '0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29', + passkeys: checksum('0x8f26281dB84C18aAeEa8a53F94c835393229d296'), + recovery: checksum('0xd98da48C4FF9c19742eA5856A277424557C863a6'), + sessions: checksum('0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29'), } export const Dev2: Extensions = { - passkeys: '0x4491845806B757D67BE05BbD877Cab101B9bee5C', - recovery: '0xdED857b9b5142832634129aFfc1D67cD106b927c', - sessions: '0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29', + passkeys: checksum('0x4491845806B757D67BE05BbD877Cab101B9bee5C'), + recovery: checksum('0xdED857b9b5142832634129aFfc1D67cD106b927c'), + sessions: checksum('0x06aa3a8F781F2be39b888Ac8a639c754aEe9dA29'), } export * as Passkeys from './passkeys.js' diff --git a/packages/wallet/primitives/src/extensions/recovery.ts b/packages/wallet/primitives/src/extensions/recovery.ts index 8760aae07..52bb17799 100644 --- a/packages/wallet/primitives/src/extensions/recovery.ts +++ b/packages/wallet/primitives/src/extensions/recovery.ts @@ -1,4 +1,5 @@ -import { Abi, AbiFunction, Address, Bytes, Hex, Provider } from 'ox' +import { Abi, AbiFunction, Bytes, Hex, Provider } from 'ox' +import { checksum, Checksummed } from '../address.js' import * as Payload from '../payload.js' import * as GenericTree from '../generic-tree.js' import { Signature } from '../index.js' @@ -34,7 +35,7 @@ export const TOTAL_QUEUED_PAYLOADS = Abi.from([ */ export type RecoveryLeaf = { type: 'leaf' - signer: Address.Address + signer: Checksummed requiredDeltaTime: bigint minTimestamp: bigint } @@ -152,7 +153,7 @@ export function parseBranch(encoded: Bytes.Bytes): { nodes: Tree[]; leftover: By if (encoded.length < index + 32) { throw new Error('Invalid recovery leaf') } - const signer = Address.from(Hex.fromBytes(encoded.slice(index + 1, index + 21))) + const signer = checksum(Hex.fromBytes(encoded.slice(index + 1, index + 21))) const requiredDeltaTime = Bytes.toBigInt(encoded.slice(index + 21, index + 24)) const minTimestamp = Bytes.toBigInt(encoded.slice(index + 24, index + 32)) nodes.push({ type: 'leaf', signer, requiredDeltaTime, minTimestamp }) @@ -200,7 +201,7 @@ export function parseBranch(encoded: Bytes.Bytes): { nodes: Tree[]; leftover: By * @param signer - The signer address to keep * @returns The trimmed topology */ -export function trimTopology(topology: Tree, signer: Address.Address): Tree { +export function trimTopology(topology: Tree, signer: Checksummed): Tree { if (isRecoveryLeaf(topology)) { if (topology.signer === signer) { return topology @@ -346,7 +347,7 @@ export function fromRecoveryLeaves(leaves: RecoveryLeaf[]): Tree { */ export function hashRecoveryPayload( payload: Payload.MayRecoveryPayload, - wallet: Address.Address, + wallet: Checksummed, chainId: bigint, noChainId: boolean, ): Hex.Hex { @@ -401,7 +402,7 @@ export function fromGenericTree(tree: GenericTree.Tree): Tree { } const offset = RECOVERY_LEAF_PREFIX.length - const signer = Address.from(Hex.fromBytes(bytes.slice(offset, offset + 20))) + const signer = checksum(Hex.fromBytes(bytes.slice(offset, offset + 20))) const requiredDeltaTime = Bytes.toBigInt(bytes.slice(offset + 20, offset + 52)) const minTimestamp = Bytes.toBigInt(bytes.slice(offset + 52, offset + 84)) @@ -435,9 +436,9 @@ export function fromGenericTree(tree: GenericTree.Tree): Tree { * @returns The encoded calldata for the queuePayload function on the recovery extension */ export function encodeCalldata( - wallet: Address.Address, + wallet: Checksummed, payload: Payload.Recovery, - signer: Address.Address, + signer: Checksummed, signature: Signature.SignatureOfSignerLeaf, ) { let signatureBytes: Hex.Hex @@ -463,9 +464,9 @@ export function encodeCalldata( */ export async function totalQueuedPayloads( provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, + extension: Checksummed, + wallet: Checksummed, + signer: Checksummed, ): Promise { const total = await provider.request({ method: 'eth_call', @@ -496,9 +497,9 @@ export async function totalQueuedPayloads( */ export async function queuedPayloadHashOf( provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, + extension: Checksummed, + wallet: Checksummed, + signer: Checksummed, index: bigint, ): Promise { const hash = await provider.request({ @@ -527,9 +528,9 @@ export async function queuedPayloadHashOf( */ export async function timestampForQueuedPayload( provider: Provider.Provider, - extension: Address.Address, - wallet: Address.Address, - signer: Address.Address, + extension: Checksummed, + wallet: Checksummed, + signer: Checksummed, payloadHash: Hex.Hex, ): Promise { const timestamp = await provider.request({ diff --git a/packages/wallet/primitives/src/payload.ts b/packages/wallet/primitives/src/payload.ts index 84030f107..2b88a5c69 100644 --- a/packages/wallet/primitives/src/payload.ts +++ b/packages/wallet/primitives/src/payload.ts @@ -1,5 +1,6 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex } from 'ox' +import { AbiFunction, AbiParameters, Bytes, Hash, Hex } from 'ox' import { getSignPayload } from 'ox/TypedData' +import { checksum, Checksummed, isEqual } from './address.js' import { EXECUTE_USER_OP, RECOVER_SAPIENT_SIGNATURE } from './constants.js' import { Attestation } from './index.js' import { minBytesFor } from './utils.js' @@ -15,7 +16,7 @@ export const BEHAVIOR_REVERT_ON_ERROR = 0x01 export const BEHAVIOR_ABORT_ON_ERROR = 0x02 interface SolidityCall { - to: Address.Address + to: Checksummed value: bigint data: Hex.Hex gasLimit: bigint @@ -33,11 +34,11 @@ export interface SolidityDecoded { message: Hex.Hex imageHash: Hex.Hex digest: Hex.Hex - parentWallets: Address.Address[] + parentWallets: Checksummed[] } export type Call = { - to: Address.Address + to: Checksummed value: bigint data: Hex.Hex gasLimit: bigint @@ -70,30 +71,30 @@ export type Digest = { export type SessionImplicitAuthorize = { type: 'session-implicit-authorize' - sessionAddress: Address.Address + sessionAddress: Checksummed attestation: Attestation.Attestation } export type Parent = { - parentWallets?: Address.Address[] + parentWallets?: Checksummed[] } export type Calls4337_07 = { type: 'call_4337_07' calls: Call[] - entrypoint: Address.Address + entrypoint: Checksummed callGasLimit: bigint maxFeePerGas: bigint maxPriorityFeePerGas: bigint space: bigint nonce: bigint - paymaster?: Address.Address | undefined + paymaster?: Checksummed | undefined paymasterData?: Hex.Hex | undefined paymasterPostOpGasLimit?: bigint | undefined paymasterVerificationGasLimit?: bigint | undefined preVerificationGas: bigint verificationGasLimit: bigint - factory?: Address.Address | undefined + factory?: Checksummed | undefined factoryData?: Hex.Hex | undefined } @@ -119,7 +120,7 @@ export type TypedDataToSign = { name: string version: string chainId: number - verifyingContract: Address.Address + verifyingContract: Checksummed } types: Record> primaryType: string @@ -199,7 +200,7 @@ export function isSessionImplicitAuthorize(payload: Payload): payload is Session return payload.type === 'session-implicit-authorize' } -export function encode(payload: Calls, self?: Address.Address): Bytes.Bytes { +export function encode(payload: Calls, self?: Checksummed): Bytes.Bytes { const callsLen = payload.calls.length const nonceBytesNeeded = minBytesFor(payload.nonce) console.log('TS encode: nonce value:', payload.nonce, 'nonceBytesNeeded:', nonceBytesNeeded) @@ -286,7 +287,7 @@ export function encode(payload: Calls, self?: Address.Address): Bytes.Bytes { */ let flags = 0 - if (self && Address.isEqual(call.to, self)) { + if (self && isEqual(call.to, self)) { flags |= 0x01 } @@ -397,7 +398,7 @@ export function encodeSapient( return encoded } -export function hash(wallet: Address.Address, chainId: bigint, payload: Parented): Bytes.Bytes { +export function hash(wallet: Checksummed, chainId: bigint, payload: Parented): Bytes.Bytes { if (isDigest(payload)) { return Bytes.fromHex(payload.digest) } @@ -410,13 +411,13 @@ export function hash(wallet: Address.Address, chainId: bigint, payload: Parented function domainFor( payload: Payload, - wallet: Address.Address, + wallet: Checksummed, chainId: bigint, ): { name: string version: string chainId: number - verifyingContract: Address.Address + verifyingContract: Checksummed } { if (isRecovery(payload)) { return { @@ -441,7 +442,7 @@ export function encode4337Nonce(key: bigint, seq: bigint): bigint { return (key << 64n) | seq } -export function toTyped(wallet: Address.Address, chainId: bigint, payload: Parented): TypedDataToSign { +export function toTyped(wallet: Checksummed, chainId: bigint, payload: Parented): TypedDataToSign { const domain = domainFor(payload, wallet, chainId) switch (payload.type) { @@ -552,7 +553,7 @@ export function toTyped(wallet: Address.Address, chainId: bigint, payload: Paren export function to4337UserOperation( payload: Calls4337_07, - wallet: Address.Address, + wallet: Checksummed, signature?: Hex.Hex, ): UserOperation.UserOperation<'0.7'> { const callsPayload: Calls = { @@ -583,7 +584,7 @@ export function to4337UserOperation( return operation } -export function to4337Message(payload: Calls4337_07, wallet: Address.Address, chainId: bigint): Hex.Hex { +export function to4337Message(payload: Calls4337_07, wallet: Checksummed, chainId: bigint): Hex.Hex { const operation = to4337UserOperation(payload, wallet) const accountGasLimits = Hex.concat( Hex.padLeft(Hex.fromNumber(operation.verificationGasLimit), 16), @@ -680,7 +681,7 @@ export function hashCall(call: Call): Hex.Hex { ) } -export function decode(packed: Bytes.Bytes, self?: Address.Address): Calls { +export function decode(packed: Bytes.Bytes, self?: Checksummed): Calls { let pointer = 0 if (packed.length < 1) { throw new Error('Invalid packed data: missing globalFlag') @@ -735,7 +736,7 @@ export function decode(packed: Bytes.Bytes, self?: Address.Address): Calls { pointer += 1 // bit 0 => toSelf - let to: Address.Address + let to: Checksummed if ((flags & 0x01) === 0x01) { if (!self) { throw new Error('Missing "self" address for toSelf call') @@ -745,7 +746,7 @@ export function decode(packed: Bytes.Bytes, self?: Address.Address): Calls { if (pointer + 20 > packed.length) { throw new Error('Invalid packed data: not enough bytes for address') } - to = Bytes.toHex(packed.slice(pointer, pointer + 20)) as Address.Address + to = checksum(Bytes.toHex(packed.slice(pointer, pointer + 20))) pointer += 20 } @@ -844,39 +845,34 @@ export function fromAbiFormat(decoded: SolidityDecoded): Parented { nonce: decoded.nonce, space: decoded.space, calls: decoded.calls.map((call) => ({ - to: Address.from(call.to), - value: call.value, - data: call.data as `0x${string}`, - gasLimit: call.gasLimit, - delegateCall: call.delegateCall, - onlyFallback: call.onlyFallback, + ...call, behaviorOnError: parseBehaviorOnError(Number(call.behaviorOnError)), })), - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), + parentWallets: decoded.parentWallets, } } if (decoded.kind === KIND_MESSAGE) { return { type: 'message', - message: decoded.message as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), + message: decoded.message, + parentWallets: decoded.parentWallets, } } if (decoded.kind === KIND_CONFIG_UPDATE) { return { type: 'config-update', - imageHash: decoded.imageHash as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), + imageHash: decoded.imageHash, + parentWallets: decoded.parentWallets, } } if (decoded.kind === KIND_DIGEST) { return { type: 'digest', - digest: decoded.digest as `0x${string}`, - parentWallets: decoded.parentWallets.map((wallet) => Address.from(wallet)), + digest: decoded.digest, + parentWallets: decoded.parentWallets, } } diff --git a/packages/wallet/primitives/src/permission.ts b/packages/wallet/primitives/src/permission.ts index 28f3849ec..d0085ced9 100644 --- a/packages/wallet/primitives/src/permission.ts +++ b/packages/wallet/primitives/src/permission.ts @@ -1,4 +1,5 @@ -import { AbiParameters, Address, Bytes } from 'ox' +import { AbiParameters, Bytes } from 'ox' +import { checksum, Checksummed } from './address.js' export enum ParameterOperation { EQUAL = 0, @@ -16,12 +17,12 @@ export type ParameterRule = { } export type Permission = { - target: Address.Address + target: Checksummed rules: ParameterRule[] } export type SessionPermissions = { - signer: Address.Address + signer: Checksummed chainId: bigint valueLimit: bigint deadline: bigint // uint64 @@ -106,7 +107,7 @@ function encodeParameterRule(rule: ParameterRule): Bytes.Bytes { // Decoding export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions { - const signer = Bytes.toHex(bytes.slice(0, 20)) + const signer = checksum(Bytes.toHex(bytes.slice(0, 20))) const chainId = Bytes.toBigInt(bytes.slice(20, 52)) const valueLimit = Bytes.toBigInt(bytes.slice(52, 84)) const deadline = Bytes.toBigInt(bytes.slice(84, 92)) @@ -133,7 +134,7 @@ export function decodeSessionPermissions(bytes: Bytes.Bytes): SessionPermissions // Returns the permission and the number of bytes consumed in the permission block function decodePermission(bytes: Bytes.Bytes): { permission: Permission; consumed: number } { - const target = Bytes.toHex(bytes.slice(0, 20)) + const target = checksum(Bytes.toHex(bytes.slice(0, 20))) const rulesLength = Number(bytes[20]!) const rules = [] let pointer = 21 @@ -192,7 +193,7 @@ export const permissionStructAbi = { ], }, ], -} as const +} export function abiEncodePermission(permission: Permission): string { return AbiParameters.encode( @@ -259,7 +260,7 @@ export function sessionPermissionsFromJson(json: string): SessionPermissions { export function sessionPermissionsFromParsed(parsed: any): SessionPermissions { return { - signer: Address.from(parsed.signer), + signer: checksum(parsed.signer), chainId: BigInt(parsed.chainId), valueLimit: BigInt(parsed.valueLimit), deadline: BigInt(parsed.deadline), @@ -273,7 +274,7 @@ export function permissionFromJson(json: string): Permission { function permissionFromParsed(parsed: any): Permission { return { - target: Address.from(parsed.target), + target: checksum(parsed.target), rules: parsed.rules.map((decoded: any) => ({ cumulative: decoded.cumulative, operation: decoded.operation, diff --git a/packages/wallet/primitives/src/precondition.ts b/packages/wallet/primitives/src/precondition.ts index 40c98dbb9..eea407d14 100644 --- a/packages/wallet/primitives/src/precondition.ts +++ b/packages/wallet/primitives/src/precondition.ts @@ -104,14 +104,5 @@ export function createIntentPrecondition( throw new Error(`Invalid precondition type: ${type}`) } - const intent: IntentPrecondition = { - type: type, - data: data as Omit, - } - - if (chainId !== undefined) { - intent.chainId = chainId - } - - return intent + return { type, data, ...(chainId !== undefined ? { chainId } : undefined) } } diff --git a/packages/wallet/primitives/src/session-config.ts b/packages/wallet/primitives/src/session-config.ts index 341bd11b0..77480c1d7 100644 --- a/packages/wallet/primitives/src/session-config.ts +++ b/packages/wallet/primitives/src/session-config.ts @@ -1,4 +1,5 @@ -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { checksum, Checksummed, isEqual } from './address.js' import * as GenericTree from './generic-tree.js' import { decodeSessionPermissions, @@ -18,12 +19,12 @@ export const SESSIONS_FLAG_IDENTITY_SIGNER = 4 export type ImplicitBlacklistLeaf = { type: 'implicit-blacklist' - blacklist: Address.Address[] + blacklist: Checksummed[] } export type IdentitySignerLeaf = { type: 'identity-signer' - identitySigner: Address.Address + identitySigner: Checksummed } export type SessionPermissionsLeaf = SessionPermissions & { @@ -106,7 +107,7 @@ function checkIsCompleteSessionsBranch(topology: SessionsTopology): { * @param topology The topology to get the identity signer from * @returns The identity signer or null if it's not present */ -export function getIdentitySigner(topology: SessionsTopology): Address.Address | null { +export function getIdentitySigner(topology: SessionsTopology): Checksummed | null { if (isIdentitySignerLeaf(topology)) { // Got it return topology.identitySigner @@ -131,7 +132,7 @@ export function getIdentitySigner(topology: SessionsTopology): Address.Address | * @param topology The topology to get the implicit blacklist from * @returns The implicit blacklist or null if it's not present */ -export function getImplicitBlacklist(topology: SessionsTopology): Address.Address[] | null { +export function getImplicitBlacklist(topology: SessionsTopology): Checksummed[] | null { const blacklistNode = getImplicitBlacklistLeaf(topology) if (!blacklistNode) { return null @@ -164,9 +165,9 @@ export function getImplicitBlacklistLeaf(topology: SessionsTopology): ImplicitBl return null } -export function getSessionPermissions(topology: SessionsTopology, address: Address.Address): SessionPermissions | null { +export function getSessionPermissions(topology: SessionsTopology, address: Checksummed): SessionPermissions | null { if (isSessionPermissions(topology)) { - if (Address.isEqual(topology.signer, address)) { + if (isEqual(topology.signer, address)) { return topology } } @@ -181,16 +182,16 @@ export function getSessionPermissions(topology: SessionsTopology, address: Addre return null } -export function getExplicitSigners(topology: SessionsTopology): Address.Address[] { +export function getExplicitSigners(topology: SessionsTopology): Checksummed[] { return getExplicitSignersFromBranch(topology, []) } -function getExplicitSignersFromBranch(topology: SessionsTopology, current: Address.Address[]): Address.Address[] { +function getExplicitSignersFromBranch(topology: SessionsTopology, current: Checksummed[]): Checksummed[] { if (isSessionPermissions(topology)) { return [...current, topology.signer] } if (isSessionsBranch(topology)) { - const result: Address.Address[] = [...current] + const result: Checksummed[] = [...current] for (const child of topology) { result.push(...getExplicitSignersFromBranch(child, current)) } @@ -239,14 +240,14 @@ export function encodeLeafToGeneric(leaf: SessionLeaf): GenericTree.Leaf { export function decodeLeafFromBytes(bytes: Bytes.Bytes): SessionLeaf { const flag = bytes[0]! if (flag === SESSIONS_FLAG_BLACKLIST) { - const blacklist: `0x${string}`[] = [] + const blacklist: Checksummed[] = [] for (let i = 1; i < bytes.length; i += 20) { - blacklist.push(Bytes.toHex(bytes.slice(i, i + 20))) + blacklist.push(checksum(Bytes.toHex(bytes.slice(i, i + 20)))) } return { type: 'implicit-blacklist', blacklist } } if (flag === SESSIONS_FLAG_IDENTITY_SIGNER) { - return { type: 'identity-signer', identitySigner: Bytes.toHex(bytes.slice(1, 21)) } + return { type: 'identity-signer', identitySigner: checksum(Bytes.toHex(bytes.slice(1, 21))) } } if (flag === SESSIONS_FLAG_PERMISSIONS) { return { type: 'session-permissions', ...decodeSessionPermissions(bytes.slice(1)) } @@ -401,13 +402,12 @@ function sessionsTopologyFromParsed(parsed: any): SessionsTopology { // Parse identity signer if (typeof parsed === 'object' && parsed !== null && 'identitySigner' in parsed) { - const identitySigner = parsed.identitySigner as `0x${string}` - return { type: 'identity-signer', identitySigner } + return { type: 'identity-signer', identitySigner: checksum(parsed.identitySigner) } } // Parse blacklist if (typeof parsed === 'object' && parsed !== null && 'blacklist' in parsed) { - const blacklist = parsed.blacklist.map((address: any) => Address.from(address)) + const blacklist = parsed.blacklist.map(checksum) return { type: 'implicit-blacklist', blacklist } } @@ -421,12 +421,9 @@ function sessionsTopologyFromParsed(parsed: any): SessionsTopology { * Returns the updated topology or null if it becomes empty (for nesting). * If the signer is not found, the topology is returned unchanged. */ -export function removeExplicitSession( - topology: SessionsTopology, - signerAddress: `0x${string}`, -): SessionsTopology | null { +export function removeExplicitSession(topology: SessionsTopology, signerAddress: Checksummed): SessionsTopology | null { if (isSessionPermissions(topology)) { - if (Address.isEqual(topology.signer, signerAddress)) { + if (isEqual(topology.signer, signerAddress)) { return null } // Return the leaf unchanged @@ -594,8 +591,8 @@ export function cleanSessionsTopology( */ export function minimiseSessionsTopology( topology: SessionsTopology, - explicitSigners: Address.Address[] = [], - implicitSigners: Address.Address[] = [], + explicitSigners: Checksummed[] = [], + implicitSigners: Checksummed[] = [], ): SessionsTopology { if (isSessionsBranch(topology)) { const branches = topology.map((b) => minimiseSessionsTopology(b, explicitSigners, implicitSigners)) @@ -636,13 +633,13 @@ export function minimiseSessionsTopology( * Adds an address to the implicit session's blacklist. * If the address is not already in the blacklist, it is added and the list is sorted. */ -export function addToImplicitBlacklist(topology: SessionsTopology, address: Address.Address): SessionsTopology { +export function addToImplicitBlacklist(topology: SessionsTopology, address: Checksummed): SessionsTopology { const blacklistNode = getImplicitBlacklistLeaf(topology) if (!blacklistNode) { throw new Error('No blacklist found') } const { blacklist } = blacklistNode - if (blacklist.some((addr) => Address.isEqual(addr, address))) { + if (blacklist.includes(address)) { return topology } blacklist.push(address) @@ -653,7 +650,7 @@ export function addToImplicitBlacklist(topology: SessionsTopology, address: Addr /** * Removes an address from the implicit session's blacklist. */ -export function removeFromImplicitBlacklist(topology: SessionsTopology, address: Address.Address): SessionsTopology { +export function removeFromImplicitBlacklist(topology: SessionsTopology, address: Checksummed): SessionsTopology { const blacklistNode = getImplicitBlacklistLeaf(topology) if (!blacklistNode) { throw new Error('No blacklist found') @@ -667,7 +664,7 @@ export function removeFromImplicitBlacklist(topology: SessionsTopology, address: /** * Generate an empty sessions topology with the given identity signer. No session permission and an empty blacklist */ -export function emptySessionsTopology(identitySigner: Address.Address): SessionsTopology { +export function emptySessionsTopology(identitySigner: Checksummed): SessionsTopology { return [ { type: 'implicit-blacklist', diff --git a/packages/wallet/primitives/src/session-signature.ts b/packages/wallet/primitives/src/session-signature.ts index cc5f2323c..693235757 100644 --- a/packages/wallet/primitives/src/session-signature.ts +++ b/packages/wallet/primitives/src/session-signature.ts @@ -1,4 +1,5 @@ -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { Checksummed } from './address.js' import { Attestation, encode, encodeForJson, fromParsed, toJson } from './attestation.js' import { MAX_PERMISSIONS_COUNT } from './permission.js' import { @@ -94,9 +95,11 @@ function rsyFromRsvStr(sigStr: string): RSY { if (!rStr || !sStr || !vStr) { throw new Error('Invalid signature format') } + Hex.assert(rStr) + Hex.assert(sStr) return { - r: Bytes.toBigInt(Bytes.fromHex(rStr as `0x${string}`, { size: 32 })), - s: Bytes.toBigInt(Bytes.fromHex(sStr as `0x${string}`, { size: 32 })), + r: Bytes.toBigInt(Bytes.fromHex(rStr, { size: 32 })), + s: Bytes.toBigInt(Bytes.fromHex(sStr, { size: 32 })), yParity: parseInt(vStr, 10) - 27, } } @@ -106,8 +109,8 @@ function rsyFromRsvStr(sigStr: string): RSY { export function encodeSessionCallSignatures( callSignatures: SessionCallSignature[], topology: SessionsTopology, - explicitSigners: Address.Address[] = [], - implicitSigners: Address.Address[] = [], + explicitSigners: Checksummed[] = [], + implicitSigners: Checksummed[] = [], ): Bytes.Bytes { const parts: Bytes.Bytes[] = [] diff --git a/packages/wallet/primitives/src/signature.ts b/packages/wallet/primitives/src/signature.ts index 9c8f8d39e..46c463595 100644 --- a/packages/wallet/primitives/src/signature.ts +++ b/packages/wallet/primitives/src/signature.ts @@ -1,4 +1,5 @@ -import { AbiFunction, AbiParameters, Address, Bytes, Hash, Hex, Provider, Secp256k1, Signature } from 'ox' +import { AbiFunction, AbiParameters, Bytes, Hash, Hex, Provider, Secp256k1, Signature } from 'ox' +import { checksum, Checksummed, isChecksummed } from './address.js' import { Config, Leaf, @@ -52,7 +53,7 @@ export type SignatureOfSignerLeafHash = { export type SignatureOfSignerLeafErc1271 = { type: 'erc1271' - address: `0x${string}` + address: Checksummed data: Hex.Hex } @@ -62,7 +63,7 @@ export type SignatureOfSignerLeaf = | SignatureOfSignerLeafErc1271 export type SignatureOfSapientSignerLeaf = { - address: `0x${string}` + address: Checksummed data: Hex.Hex type: 'sapient' | 'sapient_compact' } @@ -100,7 +101,7 @@ export type RawConfig = { threshold: bigint checkpoint: bigint topology: RawTopology - checkpointer?: Address.Address + checkpointer?: Checksummed } export type RawSignature = { @@ -108,7 +109,7 @@ export type RawSignature = { checkpointerData?: Bytes.Bytes configuration: RawConfig suffix?: RawSignature[] - erc6492?: { to: Address.Address; data: Bytes.Bytes } + erc6492?: { to: Checksummed; data: Bytes.Bytes } } export function isSignatureOfSapientSignerLeaf(signature: any): signature is SignatureOfSapientSignerLeaf { @@ -143,7 +144,7 @@ export function isRawConfig(configuration: any): configuration is RawConfig { typeof configuration.threshold === 'bigint' && typeof configuration.checkpoint === 'bigint' && isRawTopology(configuration.topology) && - (configuration.checkpointer === undefined || Address.validate(configuration.checkpointer)) + (configuration.checkpointer === undefined || isChecksummed(configuration.checkpointer)) ) } @@ -192,7 +193,7 @@ export function decodeSignature(erc6492Signature: Bytes.Bytes): RawSignature { const noChainId = (flag & 0x02) === 0x02 - let checkpointerAddress: Address.Address | undefined + let checkpointerAddress: Checksummed | undefined let checkpointerData: Bytes.Bytes | undefined // bit [6] => checkpointer address + data @@ -200,7 +201,7 @@ export function decodeSignature(erc6492Signature: Bytes.Bytes): RawSignature { if (index + 20 > signature.length) { throw new Error('Not enough bytes for checkpointer address') } - checkpointerAddress = Bytes.toHex(signature.slice(index, index + 20)) + checkpointerAddress = checksum(Bytes.toHex(signature.slice(index, index + 20))) index += 20 if (index + 3 > signature.length) { @@ -315,7 +316,7 @@ export function parseBranch(signature: Bytes.Bytes): { type: 'hash', ...unpackedRSY, }, - } as RawSignerLeaf) + }) continue } @@ -332,14 +333,14 @@ export function parseBranch(signature: Bytes.Bytes): { if (index + 20 > signature.length) { throw new Error('Not enough bytes for address leaf') } - const addr = Bytes.toHex(signature.slice(index, index + 20)) + const addr = checksum(Bytes.toHex(signature.slice(index, index + 20))) index += 20 nodes.push({ type: 'signer', address: addr, weight: BigInt(weight), - } as SignerLeaf) + }) continue } @@ -356,7 +357,7 @@ export function parseBranch(signature: Bytes.Bytes): { if (index + 20 > signature.length) { throw new Error('Not enough bytes for ERC1271 signer address') } - const signer = Bytes.toHex(signature.slice(index, index + 20)) + const signer = checksum(Bytes.toHex(signature.slice(index, index + 20))) index += 20 const sizeSize = (firstByte & 0x0c) >> 2 @@ -380,7 +381,7 @@ export function parseBranch(signature: Bytes.Bytes): { address: signer, data: Bytes.toHex(subSignature), }, - } as RawSignerLeaf) + }) continue } @@ -430,7 +431,7 @@ export function parseBranch(signature: Bytes.Bytes): { nodes.push({ type: 'subdigest', digest: Bytes.toHex(hardcoded), - } as SubdigestLeaf) + }) continue } @@ -479,7 +480,7 @@ export function parseBranch(signature: Bytes.Bytes): { tree: subTree, weight: BigInt(externalWeight), threshold: BigInt(internalThreshold), - } as RawNestedLeaf) + }) continue } @@ -506,7 +507,7 @@ export function parseBranch(signature: Bytes.Bytes): { type: 'eth_sign', ...unpackedRSY, }, - } as RawSignerLeaf) + }) continue } @@ -520,7 +521,7 @@ export function parseBranch(signature: Bytes.Bytes): { nodes.push({ type: 'any-address-subdigest', digest: Bytes.toHex(anyAddressSubdigest), - } as AnyAddressSubdigestLeaf) + }) continue } @@ -536,7 +537,7 @@ export function parseBranch(signature: Bytes.Bytes): { if (index + 20 > signature.length) { throw new Error('Not enough bytes for sapient signer address') } - const address = Bytes.toHex(signature.slice(index, index + 20)) + const address = checksum(Bytes.toHex(signature.slice(index, index + 20))) index += 20 const sizeSize = (firstByte & 0x0c) >> 2 @@ -560,7 +561,7 @@ export function parseBranch(signature: Bytes.Bytes): { data: Bytes.toHex(subSignature), type: flag === FLAG_SIGNATURE_SAPIENT ? 'sapient' : 'sapient_compact', }, - } as RawSignerLeaf) + }) continue } @@ -577,7 +578,7 @@ export function fillLeaves( ) => SignatureOfSignerLeaf | SignatureOfSapientSignerLeaf | undefined, ): Topology { if (isNode(topology)) { - return [fillLeaves(topology[0]!, signatureFor), fillLeaves(topology[1]!, signatureFor)] as Topology + return [fillLeaves(topology[0]!, signatureFor), fillLeaves(topology[1]!, signatureFor)] } if (isSignerLeaf(topology)) { @@ -605,7 +606,7 @@ export function fillLeaves( } if (isNestedLeaf(topology)) { - return { ...topology, tree: fillLeaves(topology.tree, signatureFor) } as NestedLeaf + return { ...topology, tree: fillLeaves(topology.tree, signatureFor) } } if (isNodeLeaf(topology)) { @@ -896,7 +897,7 @@ function foldNodes(nodes: RawTopology[]): RawTopology { let tree: RawTopology = nodes[0]! for (let i = 1; i < nodes.length; i++) { - tree = [tree, nodes[i]!] as RawNode + tree = [tree, nodes[i]!] } return tree } @@ -1070,7 +1071,8 @@ function rawTopologyFromJson(obj: any): RawTopology { } } if (typeof obj === 'string') { - return obj as Hex.Hex + Hex.assert(obj) + return obj } throw new Error('Invalid raw topology format') } @@ -1105,7 +1107,7 @@ function rawSignatureOfLeafFromJson(obj: any): SignatureOfSignerLeaf | Signature export async function recover( signature: RawSignature, - wallet: Address.Address, + wallet: Checksummed, chainId: bigint, payload: Parented, options?: { @@ -1157,7 +1159,7 @@ export async function recover( async function recoverTopology( topology: RawTopology, - wallet: Address.Address, + wallet: Checksummed, chainId: bigint, payload: Parented, options?: { @@ -1174,18 +1176,20 @@ async function recoverTopology( return { topology: { type: 'signer', - address: Secp256k1.recoverAddress({ - payload: - topology.signature.type === 'eth_sign' - ? Hash.keccak256( - AbiParameters.encodePacked( - ['string', 'bytes32'], - ['\x19Ethereum Signed Message:\n32', Bytes.toHex(digest)], - ), - ) - : digest, - signature: topology.signature, - }), + address: checksum( + Secp256k1.recoverAddress({ + payload: + topology.signature.type === 'eth_sign' + ? Hash.keccak256( + AbiParameters.encodePacked( + ['string', 'bytes32'], + ['\x19Ethereum Signed Message:\n32', Bytes.toHex(digest)], + ), + ) + : digest, + signature: topology.signature, + }), + ), weight: topology.weight, signed: true, signature: topology.signature, diff --git a/packages/wallet/primitives/test/address.test.ts b/packages/wallet/primitives/test/address.test.ts index fdf9fc221..8048a740e 100644 --- a/packages/wallet/primitives/test/address.test.ts +++ b/packages/wallet/primitives/test/address.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' -import { from } from '../src/address.js' +import { checksum, fromDeployConfiguration, isChecksummed } from '../src/address.js' import { Context, Dev1, Dev2 } from '../src/context.js' import { Config, hashConfiguration } from '../src/config.js' describe('Address', () => { const mockContext: Omit = { - factory: '0xe828630697817291140D6B7A42a2c3b7277bE45a', - stage1: '0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39', + factory: checksum('0xe828630697817291140D6B7A42a2c3b7277bE45a'), + stage1: checksum('0x2a4fB19F66F1427A5E363Bf1bB3be27b9A9ACC39'), creationCode: '0x603e600e3d39601e805130553df33d3d34601c57363d3d373d363d30545af43d82803e903d91601c57fd5bf3', } @@ -17,34 +17,34 @@ describe('Address', () => { checkpoint: 0n, topology: { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, } describe('from', () => { it('should generate deterministic address from Config object', () => { - const address = from(sampleConfig, mockContext) + const address = fromDeployConfiguration(sampleConfig, mockContext) // Should return a valid address - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) // Should be deterministic - same inputs should produce same output - const address2 = from(sampleConfig, mockContext) + const address2 = fromDeployConfiguration(sampleConfig, mockContext) expect(address).toBe(address2) }) it('should generate deterministic address from bytes configuration', () => { const configHash = hashConfiguration(sampleConfig) - const address = from(configHash, mockContext) + const address = fromDeployConfiguration(configHash, mockContext) // Should return a valid address - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) // Should produce same address as Config object - const addressFromConfig = from(sampleConfig, mockContext) + const addressFromConfig = fromDeployConfiguration(sampleConfig, mockContext) expect(address).toBe(addressFromConfig) }) @@ -54,7 +54,7 @@ describe('Address', () => { checkpoint: 0n, topology: { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, } @@ -64,22 +64,22 @@ describe('Address', () => { checkpoint: 0n, topology: { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, } - const address1 = from(config1, mockContext) - const address2 = from(config2, mockContext) + const address1 = fromDeployConfiguration(config1, mockContext) + const address2 = fromDeployConfiguration(config2, mockContext) expect(address1).not.toBe(address2) }) it('should generate different addresses for different contexts', () => { - const address1 = from(sampleConfig, mockContext) - const address2 = from(sampleConfig, { - factory: '0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010', - stage1: '0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD', + const address1 = fromDeployConfiguration(sampleConfig, mockContext) + const address2 = fromDeployConfiguration(sampleConfig, { + factory: checksum('0xFE14B91dE3c5Ca74c4D24608EBcD4B2848aA6010'), + stage1: checksum('0x300E98ae5bEA4A7291d62Eb0b9feD535E10095dD'), creationCode: '0x6041600e3d396021805130553df33d3d36153402601f57363d3d373d363d30545af43d82803e903d91601f57fd5bf3', }) @@ -89,22 +89,22 @@ describe('Address', () => { it('should work with Dev1 context', () => { const { stage2, ...dev1Context } = Dev1 - const address = from(sampleConfig, dev1Context) + const address = fromDeployConfiguration(sampleConfig, dev1Context) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) it('should work with Dev2 context', () => { const { stage2, ...dev2Context } = Dev2 - const address = from(sampleConfig, dev2Context) + const address = fromDeployConfiguration(sampleConfig, dev2Context) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) // Should be different from Dev1 const { stage2: _, ...dev1Context } = Dev1 - const dev1Address = from(sampleConfig, dev1Context) + const dev1Address = fromDeployConfiguration(sampleConfig, dev1Context) expect(address).not.toBe(dev1Address) }) @@ -115,20 +115,20 @@ describe('Address', () => { topology: [ { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, { type: 'signer', - address: '0x8ba1f109551bD432803012645aac136c776056C0', + address: checksum('0x8ba1f109551bD432803012645aac136c776056C0'), weight: 1n, }, ], } - const address = from(complexConfig, mockContext) + const address = fromDeployConfiguration(complexConfig, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) @@ -142,15 +142,15 @@ describe('Address', () => { threshold: 1n, tree: { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, }, } - const address = from(nestedConfig, mockContext) + const address = fromDeployConfiguration(nestedConfig, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) @@ -160,15 +160,15 @@ describe('Address', () => { checkpoint: 0n, topology: { type: 'sapient-signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, imageHash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', }, } - const address = from(sapientConfig, mockContext) + const address = fromDeployConfiguration(sapientConfig, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) @@ -176,55 +176,55 @@ describe('Address', () => { const configWithCheckpointer: Config = { threshold: 1n, checkpoint: 100n, - checkpointer: '0x1234567890123456789012345678901234567890', + checkpointer: checksum('0x1234567890123456789012345678901234567890'), topology: { type: 'signer', - address: '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1', + address: checksum('0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1'), weight: 1n, }, } - const address = from(configWithCheckpointer, mockContext) + const address = fromDeployConfiguration(configWithCheckpointer, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) // Should be different from config without checkpointer const configWithoutCheckpointer = { ...configWithCheckpointer } delete configWithoutCheckpointer.checkpointer - const addressWithoutCheckpointer = from(configWithoutCheckpointer, mockContext) + const addressWithoutCheckpointer = fromDeployConfiguration(configWithoutCheckpointer, mockContext) expect(address).not.toBe(addressWithoutCheckpointer) }) it('should handle zero hash input', () => { const zeroHash = new Uint8Array(32).fill(0) - const address = from(zeroHash, mockContext) + const address = fromDeployConfiguration(zeroHash, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) it('should handle maximum hash input', () => { const maxHash = new Uint8Array(32).fill(255) - const address = from(maxHash, mockContext) + const address = fromDeployConfiguration(maxHash, mockContext) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) it('should produce different addresses for different factory addresses', () => { const context1 = { ...mockContext, - factory: '0x1111111111111111111111111111111111111111' as Address.Address, + factory: checksum('0x1111111111111111111111111111111111111111'), } const context2 = { ...mockContext, - factory: '0x2222222222222222222222222222222222222222' as Address.Address, + factory: checksum('0x2222222222222222222222222222222222222222'), } - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) + const address1 = fromDeployConfiguration(sampleConfig, context1) + const address2 = fromDeployConfiguration(sampleConfig, context2) expect(address1).not.toBe(address2) }) @@ -232,33 +232,26 @@ describe('Address', () => { it('should produce different addresses for different stage1 addresses', () => { const context1 = { ...mockContext, - stage1: '0x1111111111111111111111111111111111111111' as Address.Address, + stage1: checksum('0x1111111111111111111111111111111111111111'), } const context2 = { ...mockContext, - stage1: '0x2222222222222222222222222222222222222222' as Address.Address, + stage1: checksum('0x2222222222222222222222222222222222222222'), } - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) + const address1 = fromDeployConfiguration(sampleConfig, context1) + const address2 = fromDeployConfiguration(sampleConfig, context2) expect(address1).not.toBe(address2) }) it('should produce different addresses for different creation code', () => { - const context1 = { - ...mockContext, - creationCode: '0x1111' as Hex.Hex, - } + const context1: typeof mockContext = { ...mockContext, creationCode: '0x1111' } + const context2: typeof mockContext = { ...mockContext, creationCode: '0x2222' } - const context2 = { - ...mockContext, - creationCode: '0x2222' as Hex.Hex, - } - - const address1 = from(sampleConfig, context1) - const address2 = from(sampleConfig, context2) + const address1 = fromDeployConfiguration(sampleConfig, context1) + const address2 = fromDeployConfiguration(sampleConfig, context2) expect(address1).not.toBe(address2) }) @@ -277,26 +270,23 @@ describe('Address', () => { { as: 'Bytes' }, ) - const expectedAddress = Bytes.toHex(addressHash.subarray(12)) - const actualAddress = from(sampleConfig, mockContext) + const expectedAddress = checksum(Bytes.toHex(addressHash.subarray(12))) + const actualAddress = fromDeployConfiguration(sampleConfig, mockContext) expect(actualAddress).toBe(expectedAddress) }) it('should handle empty creation code', () => { - const contextWithEmptyCode = { - ...mockContext, - creationCode: '0x' as Hex.Hex, - } + const contextWithEmptyCode: typeof mockContext = { ...mockContext, creationCode: '0x' } - const address = from(sampleConfig, contextWithEmptyCode) + const address = fromDeployConfiguration(sampleConfig, contextWithEmptyCode) - expect(() => Address.assert(address)).not.toThrow() + expect(isChecksummed(address)).to.be.true expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/) }) it('should be consistent across multiple calls with same inputs', () => { - const addresses = Array.from({ length: 10 }, () => from(sampleConfig, mockContext)) + const addresses = Array.from({ length: 10 }, () => fromDeployConfiguration(sampleConfig, mockContext)) // All addresses should be identical addresses.forEach((address) => { diff --git a/packages/wallet/primitives/test/attestation.test.ts b/packages/wallet/primitives/test/attestation.test.ts index 41a75da07..ddc634c9b 100644 --- a/packages/wallet/primitives/test/attestation.test.ts +++ b/packages/wallet/primitives/test/attestation.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { checksum } from '../src/address.js' import { Attestation, AuthData, @@ -24,7 +25,7 @@ describe('Attestation', () => { } const sampleAttestation: Attestation = { - approvedSigner: '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1', + approvedSigner: checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1'), identityType: Bytes.fromHex('0x12345678'), issuerHash: Bytes.fromHex('0x1111111111111111111111111111111111111111111111111111111111111111'), audienceHash: Bytes.fromHex('0x2222222222222222222222222222222222222222222222222222222222222222'), @@ -177,7 +178,7 @@ describe('Attestation', () => { it('should handle different address formats', () => { const attestationWithDifferentAddress: Attestation = { ...sampleAttestation, - approvedSigner: '0x8ba1f109551bd432803012645aac136c776056c0', + approvedSigner: checksum('0x8ba1f109551bd432803012645aac136c776056c0'), } const encoded = encode(attestationWithDifferentAddress) @@ -199,7 +200,7 @@ describe('Attestation', () => { it('should generate different hashes for different attestations', () => { const differentAttestation: Attestation = { ...sampleAttestation, - approvedSigner: '0x8ba1f109551bd432803012645aac136c776056c0', + approvedSigner: checksum('0x8ba1f109551bd432803012645aac136c776056c0'), } const hash1 = hash(sampleAttestation) @@ -291,7 +292,7 @@ describe('Attestation', () => { }) it('should generate implicit request magic correctly', () => { - const wallet = '0x1234567890123456789012345678901234567890' + const wallet = checksum('0x1234567890123456789012345678901234567890') const magic = generateImplicitRequestMagic(sampleAttestation, wallet) expect(magic.length).toBe(32) // keccak256 produces 32 bytes @@ -302,8 +303,8 @@ describe('Attestation', () => { }) it('should generate different magic for different wallets', () => { - const wallet1 = '0x1111111111111111111111111111111111111111' - const wallet2 = '0x2222222222222222222222222222222222222222' + const wallet1 = checksum('0x1111111111111111111111111111111111111111') + const wallet2 = checksum('0x2222222222222222222222222222222222222222') const magic1 = generateImplicitRequestMagic(sampleAttestation, wallet1) const magic2 = generateImplicitRequestMagic(sampleAttestation, wallet2) @@ -312,7 +313,7 @@ describe('Attestation', () => { }) it('should generate different magic for different attestations', () => { - const wallet = '0x1234567890123456789012345678901234567890' + const wallet = checksum('0x1234567890123456789012345678901234567890') const differentAttestation: Attestation = { ...sampleAttestation, audienceHash: Bytes.fromHex('0x3333333333333333333333333333333333333333333333333333333333333333'), @@ -325,7 +326,7 @@ describe('Attestation', () => { }) it('should generate magic matching manual calculation', () => { - const wallet = '0x1234567890123456789012345678901234567890' + const wallet = checksum('0x1234567890123456789012345678901234567890') const manualMagic = Hash.keccak256( Bytes.concat( @@ -345,7 +346,7 @@ describe('Attestation', () => { describe('Edge cases and error conditions', () => { it('should handle attestation with minimal data', () => { const minimalAttestation: Attestation = { - approvedSigner: '0x0000000000000000000000000000000000000000', + approvedSigner: checksum('0x0000000000000000000000000000000000000000'), identityType: new Uint8Array(4), issuerHash: new Uint8Array(32), audienceHash: new Uint8Array(32), @@ -398,7 +399,7 @@ describe('Attestation', () => { it('should maintain byte precision in round-trip operations', () => { // Test with specific byte patterns const precisionAttestation: Attestation = { - approvedSigner: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', + approvedSigner: checksum('0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef'), identityType: Bytes.fromHex('0xCAFEBABE'), issuerHash: Bytes.fromHex('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'), audienceHash: Bytes.fromHex('0xfedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210'), diff --git a/packages/wallet/primitives/test/config.test.ts b/packages/wallet/primitives/test/config.test.ts index 4abb191f9..998afbbc0 100644 --- a/packages/wallet/primitives/test/config.test.ts +++ b/packages/wallet/primitives/test/config.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { checksum } from '../src/address.js' import { Config, Topology, @@ -36,8 +37,8 @@ import { } from '../src/config.js' describe('Config', () => { - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' + const testAddress1 = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' diff --git a/packages/wallet/primitives/test/erc-6492.test.ts b/packages/wallet/primitives/test/erc-6492.test.ts index dcb7688ee..82d6bd2b7 100644 --- a/packages/wallet/primitives/test/erc-6492.test.ts +++ b/packages/wallet/primitives/test/erc-6492.test.ts @@ -1,19 +1,20 @@ import { describe, expect, it, vi } from 'vitest' -import { Address, Bytes, Hex, Provider } from 'ox' +import { Bytes, Hex, Provider } from 'ox' import { WrappedSignature } from 'ox/erc6492' +import { checksum } from '../src/address.js' import { deploy, wrap, decode, isValid } from '../src/erc-6492.js' import { Context } from '../src/context.js' describe('ERC-6492', () => { const mockContext: Context = { - factory: '0x1234567890123456789012345678901234567890' as Address.Address, - stage1: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' as Address.Address, // Fixed: 40 hex chars - stage2: '0x9876543210987654321098765432109876543210' as Address.Address, + factory: checksum('0x1234567890123456789012345678901234567890'), + stage1: checksum('0xabcdefabcdefabcdefabcdefabcdefabcdefabcd'), // Fixed: 40 hex chars + stage2: checksum('0x9876543210987654321098765432109876543210'), creationCode: '0x608060405234801561001057600080fd5b50', } - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address + const testAddress = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') const testMessageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const testSignature = '0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456789001' @@ -30,7 +31,7 @@ describe('ERC-6492', () => { expect(result.data.startsWith('0x')).toBe(true) // Should contain the encoded function call with stage1 and deployHash - expect(result.data).toContain(mockContext.stage1.slice(2)) // Remove 0x prefix for contains check + expect(result.data).toContain(mockContext.stage1.slice(2).toLowerCase()) // Remove 0x prefix for contains check }) it('should create deploy call data with bytes', () => { @@ -42,7 +43,7 @@ describe('ERC-6492', () => { // Convert to hex to check contents const dataHex = Bytes.toHex(result.data) - expect(dataHex).toContain(mockContext.stage1.slice(2)) + expect(dataHex).toContain(mockContext.stage1.slice(2).toLowerCase()) }) it('should return same type as input for deploy hash', () => { @@ -57,9 +58,9 @@ describe('ERC-6492', () => { it('should work with different contexts', () => { const differentContext: Context = { - factory: '0x9999999999999999999999999999999999999999' as Address.Address, - stage1: '0x1111111111111111111111111111111111111111' as Address.Address, - stage2: '0x2222222222222222222222222222222222222222' as Address.Address, + factory: checksum('0x9999999999999999999999999999999999999999'), + stage1: checksum('0x1111111111111111111111111111111111111111'), + stage2: checksum('0x2222222222222222222222222222222222222222'), creationCode: '0x6080604052', } @@ -132,7 +133,7 @@ describe('ERC-6492', () => { // The wrapped signature should contain encoded: address, bytes (data), bytes (signature) expect(result.length).toBeGreaterThan(testSignature.length + deployData.data.length) - expect(result).toContain(testAddress.slice(2)) // Address without 0x + expect(result).toContain(testAddress.slice(2).toLowerCase()) // Address without 0x expect(result.endsWith(WrappedSignature.magicBytes.slice(2))).toBe(true) }) }) @@ -209,7 +210,7 @@ describe('ERC-6492', () => { it('should handle malformed wrapped signature gracefully', () => { // Create a signature that ends with magic bytes but has invalid encoding - const malformedSig = ('0x1234' + WrappedSignature.magicBytes.slice(2)) as Hex.Hex + const malformedSig = `0x1234${WrappedSignature.magicBytes.slice(2)}` as const const result = decode(malformedSig) // Should return original signature when decoding fails @@ -415,9 +416,9 @@ describe('ERC-6492', () => { it('should handle edge case with minimal data', () => { const minimalContext: Context = { - factory: '0x0000000000000000000000000000000000000000' as Address.Address, - stage1: '0x0000000000000000000000000000000000000000' as Address.Address, - stage2: '0x0000000000000000000000000000000000000000' as Address.Address, + factory: checksum('0x0000000000000000000000000000000000000000'), + stage1: checksum('0x0000000000000000000000000000000000000000'), + stage2: checksum('0x0000000000000000000000000000000000000000'), creationCode: '0x', } @@ -434,7 +435,7 @@ describe('ERC-6492', () => { describe('Error handling and edge cases', () => { it('should handle very long signatures', () => { - const longSignature = ('0x' + '00'.repeat(1000)) as Hex.Hex + const longSignature = `0x${'00'.repeat(1000)}` as const const deployData: DeployData = { to: testAddress, data: '0x1234' } const wrapped = wrap(longSignature, deployData) @@ -457,29 +458,11 @@ describe('ERC-6492', () => { it('should handle signatures that accidentally contain magic bytes', () => { // Create a signature that contains the magic bytes but isn't wrapped - const magicInSignature = (testSignature + WrappedSignature.magicBytes.slice(2) + '1234') as Hex.Hex + const magicInSignature = `${testSignature}${WrappedSignature.magicBytes.slice(2)}1234` as const const result = decode(magicInSignature) // Should try to decode, but if it fails, should return original expect(result.signature).toBeDefined() }) - - it('should handle different address formats', () => { - const checksumAddress = '0x742d35Cc6635C0532925a3b8D563A6b35B7f05f1' as Address.Address - const lowercaseAddress = checksumAddress.toLowerCase() - - const deployData1: DeployData = { to: checksumAddress, data: '0x1234' } - const deployData2: DeployData = { to: lowercaseAddress as Address.Address, data: '0x1234' } - - const wrapped1 = wrap(testSignature, deployData1) - const wrapped2 = wrap(testSignature, deployData2) - - const decoded1 = decode(wrapped1) - const decoded2 = decode(wrapped2) - - // Addresses may be normalized to lowercase in decode - expect(decoded1.erc6492!.to.toLowerCase()).toBe(checksumAddress.toLowerCase()) - expect(decoded2.erc6492!.to).toBe(lowercaseAddress) - }) }) }) diff --git a/packages/wallet/primitives/test/generic-tree.test.ts b/packages/wallet/primitives/test/generic-tree.test.ts index 3e8b24091..bb0bcc602 100644 --- a/packages/wallet/primitives/test/generic-tree.test.ts +++ b/packages/wallet/primitives/test/generic-tree.test.ts @@ -21,7 +21,7 @@ describe('Generic Tree', () => { } const sampleNode: Node = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' - const sampleNode2: Node = ('0x' + 'ab'.repeat(32)) as Hex.Hex // Exactly 32 bytes + const sampleNode2: Node = `0x${'ab'.repeat(32)}` // Exactly 32 bytes const sampleBranch: Branch = [sampleLeaf1, sampleLeaf2] const complexBranch: Branch = [sampleLeaf1, sampleNode, sampleLeaf2] @@ -349,7 +349,7 @@ describe('Generic Tree', () => { value: Bytes.fromHex('0x1234'), } - const specificNode: Node = ('0x' + '00'.repeat(32)) as Hex.Hex + const specificNode: Node = `0x${'00'.repeat(32)}` const tree: Branch = [specificLeaf, specificNode] // Manual calculation @@ -442,7 +442,7 @@ describe('Generic Tree', () => { const largeBranch: Tree = Array(10) .fill(null) .map((_, i) => ({ - type: 'leaf' as const, + type: 'leaf', value: Bytes.fromString(`leaf-${i}`), })) as Branch diff --git a/packages/wallet/primitives/test/passkeys.test.ts b/packages/wallet/primitives/test/passkeys.test.ts index b6aa0990f..937869819 100644 --- a/packages/wallet/primitives/test/passkeys.test.ts +++ b/packages/wallet/primitives/test/passkeys.test.ts @@ -76,16 +76,16 @@ vi.mock('ox', async () => { describe('Passkeys', () => { // Real P-256 curve points that fit within 32 bytes (from ox WebAuthnP256 test data) // These are actual valid secp256r1 coordinates that work with Hex.padLeft(32) - const testPublicKeyX = '0x62a31768d44f5eff222f8d70c4cb61abd5840b27d617a7fe8d11b72dd5e86fc1' as Hex.Hex // 32 bytes - const testPublicKeyY = '0x6611bae3f1e2cd38e405153776a7dcb6995b8254a1416ead102a096c45d80618' as Hex.Hex // 32 bytes + const testPublicKeyX = '0x62a31768d44f5eff222f8d70c4cb61abd5840b27d617a7fe8d11b72dd5e86fc1' // 32 bytes + const testPublicKeyY = '0x6611bae3f1e2cd38e405153776a7dcb6995b8254a1416ead102a096c45d80618' // 32 bytes // Valid secp256r1 signature components from ox test data (32 bytes each) const validR = Bytes.fromHex('0x171c8c7b0c3fbea57a28027bc8cf2bbc8b3a22dc31e69e0e9c6b8b9c6b8b9c6b') const validS = Bytes.fromHex('0x6729577e31f54b21dbf72c2c805e5a9e7d5b9e7e5e5e5e5e5e5e5e5e5e5e5e5e') const testCredentialId = 'test-credential-id-12345' - const testMetadataHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex // 32 bytes - const testChallenge = '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf' as Hex.Hex // From ox tests + const testMetadataHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' // 32 bytes + const testChallenge = '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf' // From ox tests const samplePasskeyMetadata: PasskeyMetadata = { credentialId: testCredentialId, @@ -150,7 +150,7 @@ describe('Passkeys', () => { // Create WebAuthn metadata following ox patterns const createValidMetadata = (overrides: any = {}) => ({ - authenticatorData: '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000' as Hex.Hex, + authenticatorData: '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000', challengeIndex: 23, clientDataJSON: '{"type":"webauthn.get","challenge":"9jEFijuhEWrM4SOW-tChJbUEHEP44VcjcJ-Bqo1fTM8","origin":"https://example.com","crossOrigin":false}', @@ -261,11 +261,7 @@ describe('Passkeys', () => { }) it('should properly pad coordinates', () => { - const shortCoordinateKey = createValidPublicKey({ - x: '0x1234' as Hex.Hex, - y: '0x5678' as Hex.Hex, - }) - + const shortCoordinateKey = createValidPublicKey({ x: '0x1234', y: '0x5678' }) const tree = toTree(shortCoordinateKey) expect(GenericTree.isBranch(tree)).toBe(true) if (GenericTree.isBranch(tree)) { @@ -541,11 +537,11 @@ describe('Passkeys', () => { }) it('should handle different challenge formats', () => { - const challenges = [ - '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, - '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, + const challenges: Hex.Hex[] = [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', testChallenge, - '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf' as Hex.Hex, // From ox tests + '0xf631058a3ba1116acce12396fad0a125b5041c43f8e15723709f81aa8d5f4ccf', // From ox tests ] challenges.forEach((challenge) => { @@ -571,8 +567,8 @@ describe('Passkeys', () => { it('should handle invalid public key coordinates gracefully', () => { const invalidPublicKey = createValidPublicKey({ - x: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, - y: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hex.Hex, + x: '0x0000000000000000000000000000000000000000000000000000000000000000', + y: '0x0000000000000000000000000000000000000000000000000000000000000000', }) const signature = createValidSignature({ @@ -609,10 +605,10 @@ describe('Passkeys', () => { describe('WebAuthn Spec Compliance', () => { it('should handle authenticator data flag variations', () => { // Test different authenticator data flags following WebAuthn spec - const flagVariations = [ - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000' as Hex.Hex, // User present - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630100000000' as Hex.Hex, // User verified - '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97631500000000' as Hex.Hex, // Both flags + const flagVariations: Hex.Hex[] = [ + '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000', // User present + '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630100000000', // User verified + '0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97631500000000', // Both flags ] flagVariations.forEach((authenticatorData) => { @@ -687,8 +683,8 @@ describe('Passkeys', () => { it('should handle extreme coordinate values', () => { const extremeKey = createValidPublicKey({ - x: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, - y: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' as Hex.Hex, + x: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + y: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', }) const tree = toTree(extremeKey) diff --git a/packages/wallet/primitives/test/payload.test.ts b/packages/wallet/primitives/test/payload.test.ts index aa76afc36..9d63df519 100644 --- a/packages/wallet/primitives/test/payload.test.ts +++ b/packages/wallet/primitives/test/payload.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' import { UserOperation } from 'ox/erc4337' +import { checksum } from '../src/address.js' import { KIND_TRANSACTIONS, KIND_MESSAGE, @@ -53,12 +54,12 @@ import * as Attestation from '../src/attestation.js' describe('Payload', () => { // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address + const testAddress = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') const testChainId = 1n - const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex - const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex - const testMessage = '0x48656c6c6f20576f726c64' as Hex.Hex // "Hello World" in hex + const testImageHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' + const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' + const testMessage = '0x48656c6c6f20576f726c64' // "Hello World" in hex const sampleCall: Call = { to: testAddress, @@ -938,10 +939,10 @@ describe('Payload', () => { }) it('should throw for data too large', () => { - const largeData = '0x' + '00'.repeat(0x1000000) // 16MB + 1 byte + const largeData = `0x${'00'.repeat(0x1000000)}` as const // 16MB + 1 byte const callWithLargeData: Call = { ...sampleCall, - data: largeData as Hex.Hex, + data: largeData, } const payloadWithLargeData: Calls = { ...sampleCalls, diff --git a/packages/wallet/primitives/test/permission.test.ts b/packages/wallet/primitives/test/permission.test.ts index ca310771a..40d82df1b 100644 --- a/packages/wallet/primitives/test/permission.test.ts +++ b/packages/wallet/primitives/test/permission.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes } from 'ox' +import { Bytes } from 'ox' +import { checksum } from '../src/address.js' import { ParameterOperation, ParameterRule, @@ -25,8 +26,8 @@ import { describe('Permission', () => { // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address + const testAddress = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') const testChainId = 1n const testValueLimit = 1000000000000000000n // 1 ETH const testDeadline = 1893456000n // Jan 1, 2030 @@ -479,8 +480,8 @@ describe('Permission', () => { ] expectedFields.forEach((expected, i) => { - expect(rulesComponent.components[i].name).toBe(expected.name) - expect(rulesComponent.components[i].type).toBe(expected.type) + expect(rulesComponent.components![i].name).toBe(expected.name) + expect(rulesComponent.components![i].type).toBe(expected.type) }) }) }) diff --git a/packages/wallet/primitives/test/recovery.test.ts b/packages/wallet/primitives/test/recovery.test.ts index 3935dc46a..32de7e213 100644 --- a/packages/wallet/primitives/test/recovery.test.ts +++ b/packages/wallet/primitives/test/recovery.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Bytes, Hex } from 'ox' +import { Bytes, Hex } from 'ox' +import { checksum } from '../src/address.js' import { FLAG_RECOVERY_LEAF, FLAG_NODE, @@ -32,15 +33,16 @@ import { queuedPayloadHashOf, timestampForQueuedPayload, } from '../src/extensions/recovery.js' -import * as Payload from '../src/payload.js' import * as GenericTree from '../src/generic-tree.js' +import * as Payload from '../src/payload.js' +import { SignatureOfSignerLeafErc1271, SignatureOfSignerLeafHash } from '../src/signature.js' describe('Recovery', () => { // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testExtensionAddress = '0x1234567890123456789012345678901234567890' as Address.Address - const testNodeHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex + const testAddress = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') + const testExtensionAddress = checksum('0x1234567890123456789012345678901234567890') + const testNodeHash = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' const sampleRecoveryLeaf: RecoveryLeaf = { type: 'leaf', @@ -73,8 +75,8 @@ describe('Recovery', () => { ], } - const sampleSignature = { - type: 'hash' as const, + const sampleSignature: SignatureOfSignerLeafHash = { + type: 'hash', r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, s: 0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321n, yParity: 1, @@ -365,14 +367,14 @@ describe('Recovery', () => { it('should create balanced tree from multiple leaves', () => { const leaf3: RecoveryLeaf = { type: 'leaf', - signer: '0x1111111111111111111111111111111111111111' as Address.Address, + signer: checksum('0x1111111111111111111111111111111111111111'), requiredDeltaTime: 1800n, minTimestamp: 1640995200n, } const leaf4: RecoveryLeaf = { type: 'leaf', - signer: '0x2222222222222222222222222222222222222222' as Address.Address, + signer: checksum('0x2222222222222222222222222222222222222222'), requiredDeltaTime: 3600n, minTimestamp: 1640995200n, } @@ -421,7 +423,7 @@ describe('Recovery', () => { it('should return hash when both branches become hashes', () => { const branch: Branch = [sampleRecoveryLeaf, sampleRecoveryLeaf2] - const thirdAddress = '0x3333333333333333333333333333333333333333' as Address.Address + const thirdAddress = checksum('0x3333333333333333333333333333333333333333') const result = trimTopology(branch, thirdAddress) expect(typeof result).toBe('string') expect(result).toMatch(/^0x[a-fA-F0-9]{64}$/) @@ -649,10 +651,10 @@ describe('Recovery', () => { }) it('should encode calldata for ERC-1271 signature', () => { - const erc1271Signature = { - type: 'erc1271' as const, + const erc1271Signature: SignatureOfSignerLeafErc1271 = { + type: 'erc1271', address: testAddress, - data: '0x1234567890abcdef' as Hex.Hex, + data: '0x1234567890abcdef', } const recoveryPayload = Payload.toRecovery(samplePayload) @@ -742,7 +744,7 @@ describe('Recovery', () => { describe('timestampForQueuedPayload', () => { it('should return timestamp', async () => { mockProvider.request.mockResolvedValue('0x61d2b800') // 1641168000 in hex - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex + const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = await timestampForQueuedPayload( mockProvider, @@ -766,7 +768,7 @@ describe('Recovery', () => { it('should handle zero timestamp', async () => { mockProvider.request.mockResolvedValue('0x0') - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex + const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = await timestampForQueuedPayload( mockProvider, @@ -780,7 +782,7 @@ describe('Recovery', () => { it('should handle large timestamps', async () => { mockProvider.request.mockResolvedValue('0xffffffffffffffff') // Max uint64 - const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex + const validPayloadHash = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = await timestampForQueuedPayload( mockProvider, diff --git a/packages/wallet/primitives/test/session-config.test.ts b/packages/wallet/primitives/test/session-config.test.ts index 5c15ebb32..15ccd183b 100644 --- a/packages/wallet/primitives/test/session-config.test.ts +++ b/packages/wallet/primitives/test/session-config.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hex } from 'ox' +import { Bytes, Hex } from 'ox' +import { checksum } from '../src/address.js' import { SESSIONS_FLAG_PERMISSIONS, SESSIONS_FLAG_NODE, @@ -42,9 +43,9 @@ import { SessionPermissions } from '../src/permission.js' describe('Session Config', () => { // Test data - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testAddress3 = '0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e' as Address.Address + const testAddress1 = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') + const testAddress3 = checksum('0xa0b86a33e6f8b5f56e64c9e1a1b8c6a9cc4b9a9e') const testNode = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as SessionNode const samplePermission = { @@ -733,7 +734,7 @@ describe('Session Config', () => { describe('addToImplicitBlacklist', () => { it('should add address to blacklist', () => { - const newAddress = '0x1111111111111111111111111111111111111111' as Address.Address + const newAddress = checksum('0x1111111111111111111111111111111111111111') const result = addToImplicitBlacklist(sampleCompleteTopology, newAddress) const blacklist = getImplicitBlacklist(result) @@ -771,7 +772,7 @@ describe('Session Config', () => { }) it('should handle non-existent address gracefully', () => { - const nonExistentAddress = '0x1111111111111111111111111111111111111111' as Address.Address + const nonExistentAddress = checksum('0x1111111111111111111111111111111111111111') // Create a copy since removeFromImplicitBlacklist mutates the original const topologyClone = structuredClone(sampleCompleteTopology) const result = removeFromImplicitBlacklist(topologyClone, nonExistentAddress) diff --git a/packages/wallet/primitives/test/session-signature.test.ts b/packages/wallet/primitives/test/session-signature.test.ts index cab08938f..658540a83 100644 --- a/packages/wallet/primitives/test/session-signature.test.ts +++ b/packages/wallet/primitives/test/session-signature.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' +import { checksum } from '../src/address.js' import { ImplicitSessionCallSignature, ExplicitSessionCallSignature, @@ -21,8 +22,8 @@ import * as Payload from '../src/payload.js' describe('Session Signature', () => { // Test data - const testAddress1 = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address + const testAddress1 = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') const testChainId = 1n const testSpace = 0n const testNonce = 1n @@ -537,8 +538,8 @@ describe('Session Signature', () => { const minimalAttestation: Attestation = { approvedSigner: testAddress1, identityType: Bytes.fromHex('0x00'), - issuerHash: Bytes.fromHex(('0x' + '00'.repeat(32)) as Hex.Hex), - audienceHash: Bytes.fromHex(('0x' + '00'.repeat(32)) as Hex.Hex), + issuerHash: Bytes.fromHex(`0x${'00'.repeat(32)}`), + audienceHash: Bytes.fromHex(`0x${'00'.repeat(32)}`), applicationData: Bytes.fromArray([]), authData: { redirectUrl: '', diff --git a/packages/wallet/primitives/test/signature.test.ts b/packages/wallet/primitives/test/signature.test.ts index 66bad4245..523d9ae7a 100644 --- a/packages/wallet/primitives/test/signature.test.ts +++ b/packages/wallet/primitives/test/signature.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' -import { Address, Bytes, Hex } from 'ox' +import { Bytes, Hex } from 'ox' +import { checksum } from '../src/address.js' import { FLAG_SIGNATURE_HASH, FLAG_ADDRESS, @@ -23,6 +24,7 @@ import { RawNode, RawConfig, RawSignature, + RawTopology, isSignatureOfSapientSignerLeaf, isRawSignature, isRawConfig, @@ -42,14 +44,22 @@ import { recover, } from '../src/signature.js' import { packRSY } from '../src/utils.js' -import { Config, SignerLeaf, SapientSignerLeaf } from '../src/config.js' +import { + AnyAddressSubdigestLeaf, + Config, + NestedLeaf, + SignerLeaf, + SapientSignerLeaf, + SubdigestLeaf, + Topology, +} from '../src/config.js' import * as Payload from '../src/payload.js' describe('Signature', () => { // Test data - const testAddress = '0x742d35cc6635c0532925a3b8d563a6b35b7f05f1' as Address.Address - const testAddress2 = '0x8ba1f109551bd432803012645aac136c776056c0' as Address.Address - const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex + const testAddress = checksum('0x742d35cc6635c0532925a3b8d563a6b35b7f05f1') + const testAddress2 = checksum('0x8ba1f109551bd432803012645aac136c776056c0') + const testDigest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' const sampleRSY: RSY = { r: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdefn, @@ -363,11 +373,11 @@ describe('Signature', () => { it.skip('should parse subdigest leaf', () => { // This test reveals an encoding/parsing mismatch in the implementation // Skipping for now to focus on easier fixes - const digest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as `0x${string}` // 32 bytes + const digest = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' // 32 bytes // Use encodeTopology to create the correct bytes, just like the encoding test - const subdigestLeaf = { - type: 'subdigest' as const, + const subdigestLeaf: SubdigestLeaf = { + type: 'subdigest', digest: digest, } const signatureBytes = encodeTopology(subdigestLeaf) @@ -452,11 +462,11 @@ describe('Signature', () => { }) it('should encode hash signature', () => { - const signedLeaf = { - type: 'signer' as const, + const signedLeaf: SignerLeaf = { + type: 'signer', address: testAddress, weight: 2n, - signed: true as const, + signed: true, signature: sampleHashSignature, } @@ -466,8 +476,8 @@ describe('Signature', () => { }) it('should encode subdigest leaf', () => { - const subdigestLeaf = { - type: 'subdigest' as const, + const subdigestLeaf: SubdigestLeaf = { + type: 'subdigest', digest: testDigest, } @@ -499,10 +509,10 @@ describe('Signature', () => { }) it('should encode nested topology', () => { - const nestedLeaf = { - type: 'nested' as const, + const nestedLeaf: NestedLeaf = { + type: 'nested', tree: { - type: 'signer' as const, + type: 'signer', address: testAddress, weight: 1n, }, @@ -682,10 +692,10 @@ describe('Signature', () => { }) it('should handle nested topology', () => { - const nestedTopology = { - type: 'nested' as const, + const nestedTopology: NestedLeaf = { + type: 'nested', tree: { - type: 'signer' as const, + type: 'signer', address: testAddress, weight: 1n, }, @@ -714,8 +724,8 @@ describe('Signature', () => { }) it('should handle subdigest leaves', () => { - const subdigestLeaf = { - type: 'subdigest' as const, + const subdigestLeaf: SubdigestLeaf = { + type: 'subdigest', digest: testDigest, } @@ -724,8 +734,8 @@ describe('Signature', () => { }) it('should handle any-address-subdigest leaves', () => { - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, + const anyAddressSubdigestLeaf: AnyAddressSubdigestLeaf = { + type: 'any-address-subdigest', digest: testDigest, } @@ -734,7 +744,7 @@ describe('Signature', () => { }) it('should handle node leaves', () => { - const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex + const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const result = fillLeaves(nodeLeaf, () => undefined) expect(result).toBe(nodeLeaf) @@ -789,8 +799,8 @@ describe('Signature', () => { }) it('should handle different signature types', () => { - const erc1271Signer = { - type: 'unrecovered-signer' as const, + const erc1271Signer: RawTopology = { + type: 'unrecovered-signer', weight: 1n, signature: sampleErc1271Signature, } @@ -850,8 +860,8 @@ describe('Signature', () => { }) it('should handle sapient signatures', () => { - const sapientSigner = { - type: 'unrecovered-signer' as const, + const sapientSigner: RawTopology = { + type: 'unrecovered-signer', weight: 1n, signature: sampleSapientSignature, } @@ -890,14 +900,14 @@ describe('Signature', () => { }) it('should handle different topology types', () => { - const signatures = [ + const signatures: Array<{ name: string; topology: RawTopology }> = [ { topology: sampleRawSignerLeaf, name: 'unrecovered-signer', }, { topology: { - type: 'signer' as const, + type: 'signer', address: testAddress, weight: 1n, }, @@ -905,18 +915,18 @@ describe('Signature', () => { }, { topology: { - type: 'subdigest' as const, + type: 'subdigest', digest: testDigest, }, name: 'subdigest', }, { - topology: testDigest as `0x${string}`, + topology: testDigest, name: 'node', }, ] - signatures.forEach(({ topology, name }) => { + signatures.forEach(({ topology }) => { const signature = { noChainId: false, configuration: { @@ -996,19 +1006,19 @@ describe('Signature', () => { it('should recover simple hash signature', async () => { // Use working RFC 6979 test vectors instead of fake sampleRSY data - const workingHashSignature = { + const workingHashSignature: RawSignature = { noChainId: false, configuration: { threshold: 1n, checkpoint: 0n, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: { - type: 'hash' as const, + type: 'hash', r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, }, }, @@ -1022,19 +1032,19 @@ describe('Signature', () => { it('should handle chained signatures', async () => { // Use working RFC 6979 test vectors for chained signatures - const workingChainedSignature = { + const workingChainedSignature: RawSignature = { noChainId: false, configuration: { threshold: 1n, checkpoint: 0n, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: { - type: 'hash' as const, + type: 'hash', r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, }, }, @@ -1045,13 +1055,13 @@ describe('Signature', () => { threshold: 1n, checkpoint: 1n, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: { - type: 'hash' as const, + type: 'hash', r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, }, }, @@ -1066,12 +1076,12 @@ describe('Signature', () => { // These work because they don't use hash/eth_sign signatures it('should handle ERC-1271 signatures with assume-valid provider', async () => { - const erc1271Signature = { + const erc1271Signature: RawSignature = { ...sampleRawSignature, configuration: { ...sampleRawConfig, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: sampleErc1271Signature, }, @@ -1084,12 +1094,12 @@ describe('Signature', () => { }) it('should handle ERC-1271 signatures with assume-invalid provider', async () => { - const erc1271Signature = { + const erc1271Signature: RawSignature = { ...sampleRawSignature, configuration: { ...sampleRawConfig, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: sampleErc1271Signature, }, @@ -1102,12 +1112,12 @@ describe('Signature', () => { }) it('should handle sapient signatures', async () => { - const sapientSignature = { + const sapientSignature: RawSignature = { ...sampleRawSignature, configuration: { ...sampleRawConfig, topology: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: sampleSapientSignature, }, @@ -1122,21 +1132,21 @@ describe('Signature', () => { it.skip('should handle nested topology', async () => { // This test has crypto issues with the fake signature data // We already test nested topology recovery in our Real Cryptographic Recovery Tests - const workingNestedSignature = { + const workingNestedSignature: RawSignature = { noChainId: false, configuration: { threshold: 1n, checkpoint: 0n, topology: { - type: 'nested' as const, + type: 'nested', tree: { - type: 'unrecovered-signer' as const, + type: 'unrecovered-signer', weight: 1n, signature: { - type: 'hash' as const, + type: 'hash', r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, }, weight: 2n, @@ -1151,12 +1161,12 @@ describe('Signature', () => { }) it('should handle subdigest leaves', async () => { - const subdigestSignature = { + const subdigestSignature: RawSignature = { ...sampleRawSignature, configuration: { ...sampleRawConfig, topology: { - type: 'subdigest' as const, + type: 'subdigest', digest: testDigest, }, }, @@ -1223,14 +1233,14 @@ describe('Signature', () => { const largeDataSignature: SignatureOfSignerLeafErc1271 = { type: 'erc1271', address: testAddress, - data: ('0x' + '12'.repeat(1000)) as Hex.Hex, // Large data + data: `0x${'12'.repeat(1000)}`, // Large data } - const signedLeaf = { - type: 'signer' as const, + const signedLeaf: SignerLeaf = { + type: 'signer', address: testAddress, weight: 1n, - signed: true as const, + signed: true, signature: largeDataSignature, } @@ -1242,14 +1252,14 @@ describe('Signature', () => { const extremeDataSignature: SignatureOfSignerLeafErc1271 = { type: 'erc1271', address: testAddress, - data: ('0x' + '12'.repeat(50000)) as Hex.Hex, // Extremely large data + data: `0x${'12'.repeat(50000)}`, // Extremely large data } - const signedLeaf = { - type: 'signer' as const, + const signedLeaf: SignerLeaf = { + type: 'signer', address: testAddress, weight: 1n, - signed: true as const, + signed: true, signature: extremeDataSignature, } @@ -1347,7 +1357,7 @@ describe('Signature', () => { signature: { r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, } @@ -1362,7 +1372,7 @@ describe('Signature', () => { signature: { r: 0xf1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367n, s: 0x019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083n, - yParity: 1 as const, + yParity: 1, }, } @@ -1503,7 +1513,7 @@ describe('Signature', () => { } // Test with message payload - const messagePayload = Payload.fromMessage('0x48656c6c6f576f726c64' as Hex.Hex) + const messagePayload = Payload.fromMessage('0x48656c6c6f576f726c64') const result = await recover(hashSignature, testAddress, 1n, messagePayload) @@ -1736,20 +1746,18 @@ describe('Signature', () => { }, { name: 'message payload', - payload: Payload.fromMessage('0x48656c6c6f20576f726c64' as Hex.Hex), + payload: Payload.fromMessage('0x48656c6c6f20576f726c64'), }, // Temporarily skip config-update to isolate the bytes33 issue // { // name: 'config-update payload', // payload: Payload.fromConfigUpdate( - // '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex, + // '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef', // ), // }, { name: 'digest payload', - payload: Payload.fromDigest( - '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex, - ), + payload: Payload.fromDigest('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'), }, ] @@ -1837,7 +1845,7 @@ describe('Signature', () => { type: 'hash', r: 0xefd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716n, s: 0xf7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8n, - yParity: 0 as const, + yParity: 0, }, }, weight: 2n, @@ -1894,7 +1902,7 @@ describe('Signature', () => { type: 'sapient-signer', address: testAddress, weight: 1n, - imageHash: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef' as Hex.Hex, + imageHash: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef', } const signature: RawSignature = { @@ -1925,9 +1933,9 @@ describe('Signature', () => { // Create a payload and calculate its digest to match const digest = hash(testAddress, 1n, samplePayload) - const subdigestLeaf = { - type: 'subdigest' as const, - digest: Bytes.toHex(digest) as `0x${string}`, + const subdigestLeaf: SubdigestLeaf = { + type: 'subdigest', + digest: Bytes.toHex(digest), } const signature: RawSignature = { @@ -1952,9 +1960,9 @@ describe('Signature', () => { }) it('should handle SubdigestLeaf topology with non-matching digest', async () => { - const subdigestLeaf = { - type: 'subdigest' as const, - digest: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as `0x${string}`, + const subdigestLeaf: SubdigestLeaf = { + type: 'subdigest', + digest: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', } const signature: RawSignature = { @@ -1977,15 +1985,11 @@ describe('Signature', () => { const { hash } = await import('../src/payload.js') // Create a payload and calculate its any-address digest - const anyAddressOpHash = hash( - '0x0000000000000000000000000000000000000000' as Address.Address, - 1n, - samplePayload, - ) + const anyAddressOpHash = hash(checksum('0x0000000000000000000000000000000000000000'), 1n, samplePayload) - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, - digest: Bytes.toHex(anyAddressOpHash) as `0x${string}`, + const anyAddressSubdigestLeaf: AnyAddressSubdigestLeaf = { + type: 'any-address-subdigest', + digest: Bytes.toHex(anyAddressOpHash), } const signature: RawSignature = { @@ -2010,9 +2014,9 @@ describe('Signature', () => { }) it('should handle AnyAddressSubdigestLeaf with non-matching digest', async () => { - const anyAddressSubdigestLeaf = { - type: 'any-address-subdigest' as const, - digest: '0x9999999999999999999999999999999999999999999999999999999999999999' as `0x${string}`, + const anyAddressSubdigestLeaf: AnyAddressSubdigestLeaf = { + type: 'any-address-subdigest', + digest: '0x9999999999999999999999999999999999999999999999999999999999999999', } const signature: RawSignature = { @@ -2031,7 +2035,7 @@ describe('Signature', () => { }) it('should handle NodeLeaf topology (line 1325)', async () => { - const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' as Hex.Hex + const nodeLeaf = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' const signature: RawSignature = { noChainId: false, diff --git a/packages/wallet/wdk/src/dbs/auth-keys.ts b/packages/wallet/wdk/src/dbs/auth-keys.ts index a4aa63a5a..3253d4cde 100644 --- a/packages/wallet/wdk/src/dbs/auth-keys.ts +++ b/packages/wallet/wdk/src/dbs/auth-keys.ts @@ -1,3 +1,4 @@ +import { Address } from '@0xsequence/wallet-primitives' import { Generic, Migration } from './generic.js' import { IDBPDatabase, IDBPTransaction } from 'idb' @@ -6,7 +7,7 @@ const TABLE_NAME = 'auth-keys' export type AuthKey = { address: string privateKey: CryptoKey - identitySigner: string + identitySigner?: Address.Checksummed expiresAt: Date } @@ -37,11 +38,7 @@ export class AuthKeys extends Generic { } async set(item: AuthKey): Promise { - const result = await super.set({ - ...item, - address: item.address.toLowerCase(), - identitySigner: item.identitySigner.toLowerCase(), - }) + const result = await super.set({ ...item, address: item.address.toLowerCase() }) this.scheduleExpiration(item) return result } @@ -52,15 +49,14 @@ export class AuthKeys extends Generic { return result } - async getBySigner(signer: string, attempt: number = 1): Promise { - const normalizedSigner = signer.toLowerCase() + async getBySigner(signer: Address.Checksummed | undefined, attempt: number = 1): Promise { const store = await this.getStore('readonly') const index = store.index('identitySigner') // Below code has a workaround where get does not work as expected // and we fall back to getAll to find the key by identitySigner. try { - const result = await index.get(normalizedSigner) + const result = await index.get(signer ?? '') if (result !== undefined) { return result } else if (attempt < 2) { @@ -70,30 +66,25 @@ export class AuthKeys extends Generic { try { const allKeys = await store.getAll() if (allKeys && allKeys.length > 0) { - const foundKey = allKeys.find((key) => key.identitySigner.toLowerCase() === normalizedSigner) - return foundKey + return allKeys.find((key) => + signer === undefined ? key.identitySigner === undefined : Address.isEqual(key.identitySigner, signer), + ) } return undefined } catch (getAllError) { - console.error( - `[AuthKeys.getBySigner] Fallback: Error during getAll() for signer ${normalizedSigner}:`, - getAllError, - ) + console.error(`[AuthKeys.getBySigner] Fallback: Error during getAll() for signer ${signer}:`, getAllError) throw getAllError } } } catch (error) { - console.error( - `[AuthKeys.getBySigner attempt #${attempt}] Index query error for signer ${normalizedSigner}:`, - error, - ) + console.error(`[AuthKeys.getBySigner attempt #${attempt}] Index query error for signer ${signer}:`, error) throw error } } - async delBySigner(signer: string): Promise { - const authKey = await this.getBySigner(signer.toLowerCase()) + async delBySigner(signer: Address.Checksummed | undefined): Promise { + const authKey = await this.getBySigner(signer) if (authKey) { await this.del(authKey.address.toLowerCase()) } diff --git a/packages/wallet/wdk/src/identity/signer.ts b/packages/wallet/wdk/src/identity/signer.ts index 3da8d1301..af85ad28f 100644 --- a/packages/wallet/wdk/src/identity/signer.ts +++ b/packages/wallet/wdk/src/identity/signer.ts @@ -1,15 +1,15 @@ -import { Address, Signature, Hex, Bytes, PersonalMessage } from 'ox' +import { Signature, Hex, Bytes, PersonalMessage } from 'ox' import { Signers, State } from '@0xsequence/wallet-core' import { IdentityInstrument, KeyType } from '@0xsequence/identity-instrument' import { AuthKey } from '../dbs/auth-keys.js' -import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives' +import { Address, Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives' import * as Identity from '@0xsequence/identity-instrument' export function toIdentityAuthKey(authKey: AuthKey): Identity.AuthKey { return { address: authKey.address, keyType: Identity.KeyType.Secp256r1, - signer: authKey.identitySigner, + signer: authKey.identitySigner ?? '', async sign(digest: Bytes.Bytes) { const authKeySignature = await window.crypto.subtle.sign( { @@ -30,15 +30,15 @@ export class IdentitySigner implements Signers.Signer { readonly authKey: AuthKey, ) {} - get address(): Address.Address { - if (!Address.validate(this.authKey.identitySigner)) { + get address(): Address.Checksummed { + if (!this.authKey.identitySigner) { throw new Error('No signer address found') } - return Address.checksum(this.authKey.identitySigner) + return this.authKey.identitySigner } async sign( - wallet: Address.Address, + wallet: Address.Checksummed, chainId: bigint, payload: Payload.Parented, ): Promise { @@ -55,7 +55,7 @@ export class IdentitySigner implements Signers.Signer { } } - async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise { + async witness(stateWriter: State.Writer, wallet: Address.Checksummed, extra?: Object): Promise { const payload = Payload.fromMessage( Hex.fromString( JSON.stringify({ diff --git a/packages/wallet/wdk/src/sequence/devices.ts b/packages/wallet/wdk/src/sequence/devices.ts index 94d3f3ff2..200842a31 100644 --- a/packages/wallet/wdk/src/sequence/devices.ts +++ b/packages/wallet/wdk/src/sequence/devices.ts @@ -1,5 +1,5 @@ import { Signers } from '@0xsequence/wallet-core' -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' import { Kinds, WitnessExtraSignerKind } from './types/signer.js' import { Shared } from './manager.js' @@ -10,7 +10,7 @@ export class Devices { return this.shared.databases.encryptedPks.listAddresses() } - async has(address: Address.Address) { + async has(address: Address.Checksummed) { const entry = await this.shared.databases.encryptedPks.getEncryptedEntry(address) return entry !== undefined } @@ -27,7 +27,7 @@ export class Devices { return new Signers.Pk.Pk(s) } - async get(address: Address.Address) { + async get(address: Address.Checksummed) { const s = await this.shared.databases.encryptedPks.getEncryptedPkStore(address) if (!s) { return undefined @@ -36,7 +36,7 @@ export class Devices { return new Signers.Pk.Pk(s) } - async witness(address: Address.Address, wallet: Address.Address) { + async witness(address: Address.Checksummed, wallet: Address.Checksummed) { const signer = await this.get(address) if (!signer) { throw new Error('Signer not found') @@ -47,7 +47,7 @@ export class Devices { } as WitnessExtraSignerKind) } - async remove(address: Address.Address) { + async remove(address: Address.Checksummed) { await this.shared.databases.encryptedPks.remove(address) } } diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts index 1208b3077..f7a02852e 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode-pkce.ts @@ -1,4 +1,4 @@ -import { Hex, Address, Bytes } from 'ox' +import { Hex, Bytes } from 'ox' import { Handler } from './handler.js' import * as Db from '../../dbs/index.js' import { Signatures } from '../signatures.js' diff --git a/packages/wallet/wdk/src/sequence/handlers/authcode.ts b/packages/wallet/wdk/src/sequence/handlers/authcode.ts index 87919d364..2e09a723c 100644 --- a/packages/wallet/wdk/src/sequence/handlers/authcode.ts +++ b/packages/wallet/wdk/src/sequence/handlers/authcode.ts @@ -1,4 +1,5 @@ -import { Hex, Address, Bytes } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex, Bytes } from 'ox' import { Handler } from './handler.js' import * as Db from '../../dbs/index.js' import { Signatures } from '../signatures.js' @@ -71,7 +72,7 @@ export class AuthCodeHandler extends IdentityHandler implements Handler { } async status( - address: Address.Address, + address: Address.Checksummed, _imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/handlers/devices.ts b/packages/wallet/wdk/src/sequence/handlers/devices.ts index 9da0f8971..08794a6f4 100644 --- a/packages/wallet/wdk/src/sequence/handlers/devices.ts +++ b/packages/wallet/wdk/src/sequence/handlers/devices.ts @@ -1,6 +1,7 @@ +import { Address } from '@0xsequence/wallet-primitives' import { Kinds } from '../types/signer.js' import { Signatures } from '../signatures.js' -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { Devices } from '../devices.js' import { Handler } from './handler.js' import { SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' @@ -18,7 +19,7 @@ export class DevicesHandler implements Handler { } async status( - address: Address.Address, + address: Address.Checksummed, _imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/handlers/handler.ts b/packages/wallet/wdk/src/sequence/handlers/handler.ts index 8cd4b72f5..cbeada1ad 100644 --- a/packages/wallet/wdk/src/sequence/handlers/handler.ts +++ b/packages/wallet/wdk/src/sequence/handlers/handler.ts @@ -1,4 +1,5 @@ -import { Address, Hex } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { SignerActionable, SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' export interface Handler { @@ -7,7 +8,7 @@ export interface Handler { onStatusChange(cb: () => void): () => void status( - address: Address.Address, + address: Address.Checksummed, imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise diff --git a/packages/wallet/wdk/src/sequence/handlers/identity.ts b/packages/wallet/wdk/src/sequence/handlers/identity.ts index cbe09963f..b69a43984 100644 --- a/packages/wallet/wdk/src/sequence/handlers/identity.ts +++ b/packages/wallet/wdk/src/sequence/handlers/identity.ts @@ -1,3 +1,4 @@ +import { Address } from '@0xsequence/wallet-primitives' import { Hex, Bytes } from 'ox' import * as Db from '../../dbs/index.js' import * as Identity from '@0xsequence/identity-instrument' @@ -33,8 +34,8 @@ export class IdentityHandler { } protected async nitroCommitVerifier(challenge: Identity.Challenge) { - await this.authKeys.delBySigner('') - const authKey = await this.getAuthKey('') + await this.authKeys.delBySigner(undefined) + const authKey = await this.getAuthKey(undefined) if (!authKey) { throw new Error('no-auth-key') } @@ -44,16 +45,16 @@ export class IdentityHandler { } protected async nitroCompleteAuth(challenge: Identity.Challenge) { - const authKey = await this.getAuthKey('') + const authKey = await this.getAuthKey(undefined) if (!authKey) { throw new Error('no-auth-key') } const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey), challenge) - authKey.identitySigner = res.signer.address + authKey.identitySigner = Address.checksum(res.signer.address) authKey.expiresAt = new Date(Date.now() + 1000 * 60 * 3) // 3 minutes - await this.authKeys.delBySigner('') + await this.authKeys.delBySigner(undefined) await this.authKeys.delBySigner(authKey.identitySigner) await this.authKeys.set(authKey) @@ -69,7 +70,7 @@ export class IdentityHandler { }) } - protected async getAuthKeySigner(address: string): Promise { + protected async getAuthKeySigner(address: Address.Checksummed): Promise { const authKey = await this.getAuthKey(address) if (!authKey) { return undefined @@ -77,7 +78,7 @@ export class IdentityHandler { return new IdentitySigner(this.nitro, authKey) } - private async getAuthKey(signer: string): Promise { + private async getAuthKey(signer: Address.Checksummed | undefined): Promise { let authKey = await this.authKeys.getBySigner(signer) if (!signer && !authKey) { const keyPair = await window.crypto.subtle.generateKey( @@ -91,7 +92,6 @@ export class IdentityHandler { const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey) authKey = { address: Hex.fromBytes(new Uint8Array(publicKey)), - identitySigner: '', expiresAt: new Date(Date.now() + 1000 * 60 * 60), // 1 hour privateKey: keyPair.privateKey, } diff --git a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts index 536de8a14..2c7df0e2e 100644 --- a/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts +++ b/packages/wallet/wdk/src/sequence/handlers/mnemonic.ts @@ -1,5 +1,6 @@ import { Signers } from '@0xsequence/wallet-core' -import { Address, Hex, Mnemonic } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex, Mnemonic } from 'ox' import { Handler } from './handler.js' import { Signatures } from '../signatures.js' import { Kinds } from '../types/signer.js' @@ -39,7 +40,7 @@ export class MnemonicHandler implements Handler { } async status( - address: Address.Address, + address: Address.Checksummed, _imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/handlers/otp.ts b/packages/wallet/wdk/src/sequence/handlers/otp.ts index 6364ffba4..c4026c1a2 100644 --- a/packages/wallet/wdk/src/sequence/handlers/otp.ts +++ b/packages/wallet/wdk/src/sequence/handlers/otp.ts @@ -1,4 +1,5 @@ -import { Hex, Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { Signers } from '@0xsequence/wallet-core' import * as Identity from '@0xsequence/identity-instrument' import { Handler } from './handler.js' @@ -55,7 +56,7 @@ export class OtpHandler extends IdentityHandler implements Handler { } async status( - address: Address.Address, + address: Address.Checksummed, _imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts b/packages/wallet/wdk/src/sequence/handlers/passkeys.ts index 226ba2032..ef2b59b6f 100644 --- a/packages/wallet/wdk/src/sequence/handlers/passkeys.ts +++ b/packages/wallet/wdk/src/sequence/handlers/passkeys.ts @@ -1,8 +1,8 @@ import { Signers, State } from '@0xsequence/wallet-core' -import { Address, Hex } from 'ox' +import { Hex } from 'ox' import { Kinds } from '../types/signer.js' import { Signatures } from '../signatures.js' -import { Extensions } from '@0xsequence/wallet-primitives' +import { Address, Extensions } from '@0xsequence/wallet-primitives' import { Handler } from './handler.js' import { SignerActionable, SignerUnavailable, BaseSignatureRequest } from '../types/index.js' @@ -19,7 +19,10 @@ export class PasskeysHandler implements Handler { return () => {} } - private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise { + private async loadPasskey( + wallet: Address.Checksummed, + imageHash: Hex.Hex, + ): Promise { try { return await Signers.Passkey.Passkey.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash) } catch (e) { @@ -29,7 +32,7 @@ export class PasskeysHandler implements Handler { } async status( - address: Address.Address, + address: Address.Checksummed, imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/handlers/recovery.ts b/packages/wallet/wdk/src/sequence/handlers/recovery.ts index 6fb30e24c..f5abb2199 100644 --- a/packages/wallet/wdk/src/sequence/handlers/recovery.ts +++ b/packages/wallet/wdk/src/sequence/handlers/recovery.ts @@ -1,8 +1,7 @@ -import { Address } from 'ox/Address' import { BaseSignatureRequest, SignerUnavailable, SignerReady, SignerActionable, Kinds } from '../types/index.js' import { Handler } from './handler.js' import { Recovery } from '../recovery.js' -import { Payload } from '@0xsequence/wallet-primitives' +import { Address, Payload } from '@0xsequence/wallet-primitives' import { Hex } from 'ox' import { Signatures } from '../signatures.js' @@ -19,7 +18,7 @@ export class RecoveryHandler implements Handler { } async status( - address: Address, + address: Address.Checksummed, imageHash: Hex.Hex | undefined, request: BaseSignatureRequest, ): Promise { diff --git a/packages/wallet/wdk/src/sequence/manager.ts b/packages/wallet/wdk/src/sequence/manager.ts index 38146a69c..9ece18757 100644 --- a/packages/wallet/wdk/src/sequence/manager.ts +++ b/packages/wallet/wdk/src/sequence/manager.ts @@ -2,8 +2,7 @@ import { Signers as CoreSigners, Relayer, State } from '@0xsequence/wallet-core' import { IdentityInstrument } from '@0xsequence/identity-instrument' import { createAttestationVerifyingFetch } from '@0xsequence/tee-verifier' -import { Config, Constants, Context, Extensions, Network } from '@0xsequence/wallet-primitives' -import { Address } from 'ox' +import { Address, Config, Constants, Context, Extensions, Network } from '@0xsequence/wallet-primitives' import * as Db from '../dbs/index.js' import { Cron } from './cron.js' import { Devices } from './devices.js' @@ -32,7 +31,7 @@ export type ManagerOptions = { extensions?: Extensions.Extensions context?: Context.Context - guest?: Address.Address + guest?: Address.Checksummed encryptedPksDb?: CoreSigners.Pk.Encrypted.EncryptedPksDb managerDb?: Db.Wallets @@ -102,7 +101,7 @@ export const ManagerOptionsDefaults = { defaultGuardTopology: { // TODO: Move this somewhere else type: 'signer', - address: '0xf71eC72C8C03a0857DD7601ACeF1e42b85983e99', + address: Address.checksum('0xf71eC72C8C03a0857DD7601ACeF1e42b85983e99'), weight: 1n, } as Config.SignerLeaf, @@ -172,7 +171,7 @@ export type Sequence = { readonly context: Context.Context readonly context4337: Context.Context readonly extensions: Extensions.Extensions - readonly guest: Address.Address + readonly guest: Address.Checksummed readonly stateProvider: State.Provider diff --git a/packages/wallet/wdk/src/sequence/messages.ts b/packages/wallet/wdk/src/sequence/messages.ts index afd34912b..ee5ec4971 100644 --- a/packages/wallet/wdk/src/sequence/messages.ts +++ b/packages/wallet/wdk/src/sequence/messages.ts @@ -1,6 +1,6 @@ import { Envelope, Wallet } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hex, Provider, RpcTransport } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Bytes, Hex, Provider, RpcTransport } from 'ox' import { v7 as uuidv7 } from 'uuid' import { Shared } from './manager.js' import { Message, MessageRequest, MessageRequested, MessageSigned } from './types/message-request.js' @@ -49,7 +49,7 @@ export interface MessagesInterface { * @see {complete} to finalize the signature after it has been signed. */ request( - wallet: Address.Address, + wallet: Address.Checksummed, message: MessageRequest, chainId?: bigint, options?: { source?: string }, @@ -129,7 +129,7 @@ export class Messages implements MessagesInterface { } async request( - from: Address.Address, + from: Address.Checksummed, message: MessageRequest, chainId?: bigint, options?: { diff --git a/packages/wallet/wdk/src/sequence/recovery.ts b/packages/wallet/wdk/src/sequence/recovery.ts index 46f00ebac..7e556157b 100644 --- a/packages/wallet/wdk/src/sequence/recovery.ts +++ b/packages/wallet/wdk/src/sequence/recovery.ts @@ -1,6 +1,6 @@ -import { Config, Constants, Extensions, GenericTree, Payload } from '@0xsequence/wallet-primitives' +import { Address, Config, Constants, Extensions, GenericTree, Payload } from '@0xsequence/wallet-primitives' import { Shared } from './manager.js' -import { Address, Hex, Provider, RpcTransport } from 'ox' +import { Hex, Provider, RpcTransport } from 'ox' import { Kinds, RecoverySigner } from './types/signer.js' import { Envelope } from '@0xsequence/wallet-core' import { QueuedRecoveryPayload } from './types/recovery.js' @@ -20,7 +20,7 @@ export interface RecoveryInterface { * the recovery module enabled, it returns `undefined`. * @see {RecoverySigner} for details on the returned object structure. */ - getSigners(wallet: Address.Address): Promise + getSigners(wallet: Address.Checksummed): Promise /** * Initiates the process of queuing a recovery payload for future execution. This is the first of a two-part @@ -36,7 +36,7 @@ export interface RecoveryInterface { * with the signing UI and `completePayload`. * @see {completePayload} for the next step. */ - queuePayload(wallet: Address.Address, chainId: bigint, payload: Payload.Calls): Promise + queuePayload(wallet: Address.Checksummed, chainId: bigint, payload: Payload.Calls): Promise /** * Finalizes a queued recovery payload request and returns the transaction data needed to start the timelock on-chain. @@ -58,7 +58,7 @@ export interface RecoveryInterface { * (the encoded calldata) for the on-chain queuing transaction. * @throws An error if the `requestId` is invalid, not for a recovery action, or not fully signed. */ - completePayload(requestId: string): Promise<{ to: Address.Address; data: Hex.Hex }> + completePayload(requestId: string): Promise<{ to: Address.Checksummed; data: Hex.Hex }> /** * Initiates a configuration update to add a new mnemonic as a recovery signer for a wallet. @@ -72,7 +72,7 @@ export interface RecoveryInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {completeUpdate} to finalize this change after it has been signed. */ - addMnemonic(wallet: Address.Address, mnemonic: string): Promise + addMnemonic(wallet: Address.Checksummed, mnemonic: string): Promise /** * Initiates a configuration update to add any generic address as a recovery signer. @@ -88,7 +88,7 @@ export interface RecoveryInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {completeUpdate} to finalize this change after it has been signed. */ - addSigner(wallet: Address.Address, address: Address.Address): Promise + addSigner(wallet: Address.Checksummed, address: Address.Checksummed): Promise /** * Initiates a configuration update to remove a recovery signer from a wallet. @@ -100,7 +100,7 @@ export interface RecoveryInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {completeUpdate} to finalize this change after it has been signed. */ - removeSigner(wallet: Address.Address, address: Address.Address): Promise + removeSigner(wallet: Address.Checksummed, address: Address.Checksummed): Promise /** * Finalizes and saves a pending recovery configuration update. @@ -144,7 +144,7 @@ export interface RecoveryInterface { * @returns A function that, when called, unsubscribes the listener. */ onQueuedPayloadsUpdate( - wallet: Address.Address | undefined, + wallet: Address.Checksummed | undefined, cb: (payloads: QueuedRecoveryPayload[]) => void, trigger?: boolean, ): () => void @@ -201,7 +201,7 @@ export class Recovery implements RecoveryInterface { modules[idx].imageHash = GenericTree.hash(nextGeneric) } - public async initRecoveryModule(modules: Config.SapientSignerLeaf[], address: Address.Address) { + public async initRecoveryModule(modules: Config.SapientSignerLeaf[], address: Address.Checksummed) { if (this.hasRecoveryModule(modules)) { throw new Error('recovery-module-already-initialized') } @@ -232,7 +232,7 @@ export class Recovery implements RecoveryInterface { return modules.some((m) => Address.isEqual(m.address, this.shared.sequence.extensions.recovery)) } - async addRecoverySignerToModules(modules: Config.SapientSignerLeaf[], address: Address.Address) { + async addRecoverySignerToModules(modules: Config.SapientSignerLeaf[], address: Address.Checksummed) { if (!this.hasRecoveryModule(modules)) { throw new Error('recovery-module-not-enabled') } @@ -256,7 +256,7 @@ export class Recovery implements RecoveryInterface { }) } - async removeRecoverySignerFromModules(modules: Config.SapientSignerLeaf[], address: Address.Address) { + async removeRecoverySignerFromModules(modules: Config.SapientSignerLeaf[], address: Address.Checksummed) { if (!this.hasRecoveryModule(modules)) { throw new Error('recovery-module-not-enabled') } @@ -278,7 +278,7 @@ export class Recovery implements RecoveryInterface { }) } - async addMnemonic(wallet: Address.Address, mnemonic: string) { + async addMnemonic(wallet: Address.Checksummed, mnemonic: string) { const signer = MnemonicHandler.toSigner(mnemonic) if (!signer) { throw new Error('invalid-mnemonic') @@ -292,7 +292,7 @@ export class Recovery implements RecoveryInterface { return this.addSigner(wallet, signer.address) } - async addSigner(wallet: Address.Address, address: Address.Address) { + async addSigner(wallet: Address.Checksummed, address: Address.Checksummed) { const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet) await this.addRecoverySignerToModules(modules, address) return this.shared.modules.wallets.requestConfigurationUpdate( @@ -305,7 +305,7 @@ export class Recovery implements RecoveryInterface { ) } - async removeSigner(wallet: Address.Address, address: Address.Address) { + async removeSigner(wallet: Address.Checksummed, address: Address.Checksummed) { const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet) await this.removeRecoverySignerFromModules(modules, address) return this.shared.modules.wallets.requestConfigurationUpdate( @@ -325,7 +325,7 @@ export class Recovery implements RecoveryInterface { return this.shared.modules.wallets.completeConfigurationUpdate(requestId) } - async getSigners(address: Address.Address): Promise { + async getSigners(address: Address.Checksummed): Promise { const { raw } = await this.shared.modules.wallets.getConfiguration(address) const recoveryLeaf = raw.modules.find((m) => Address.isEqual(m.address, this.shared.sequence.extensions.recovery)) if (!recoveryLeaf) { @@ -359,7 +359,7 @@ export class Recovery implements RecoveryInterface { })) } - async queuePayload(wallet: Address.Address, chainId: bigint, payload: Payload.Calls) { + async queuePayload(wallet: Address.Checksummed, chainId: bigint, payload: Payload.Calls) { const signers = await this.getSigners(wallet) if (!signers) { throw new Error('recovery-signers-not-found') @@ -398,7 +398,7 @@ export class Recovery implements RecoveryInterface { } // TODO: Handle this transaction instead of just returning the to and data - async completePayload(requestId: string): Promise<{ to: Address.Address; data: Hex.Hex }> { + async completePayload(requestId: string): Promise<{ to: Address.Checksummed; data: Hex.Hex }> { const signature = await this.shared.modules.signatures.get(requestId) if (signature.action !== 'recovery' || !Payload.isRecovery(signature.envelope.payload)) { throw new Error('invalid-recovery-payload') @@ -436,7 +436,7 @@ export class Recovery implements RecoveryInterface { } } - async getQueuedRecoveryPayloads(wallet?: Address.Address): Promise { + async getQueuedRecoveryPayloads(wallet?: Address.Checksummed): Promise { const all = await this.shared.databases.recovery.list() if (wallet) { return all.filter((p) => Address.isEqual(p.wallet, wallet)) @@ -446,7 +446,7 @@ export class Recovery implements RecoveryInterface { } onQueuedPayloadsUpdate( - wallet: Address.Address | undefined, + wallet: Address.Checksummed | undefined, cb: (payloads: QueuedRecoveryPayload[]) => void, trigger?: boolean, ) { @@ -553,7 +553,7 @@ export class Recovery implements RecoveryInterface { } } - async encodeRecoverySignature(imageHash: Hex.Hex, signer: Address.Address) { + async encodeRecoverySignature(imageHash: Hex.Hex, signer: Address.Checksummed) { const genericTree = await this.shared.sequence.stateProvider.getTree(imageHash) if (!genericTree) { throw new Error('recovery-module-tree-not-found') diff --git a/packages/wallet/wdk/src/sequence/sessions.ts b/packages/wallet/wdk/src/sequence/sessions.ts index 6bef32888..e16caf933 100644 --- a/packages/wallet/wdk/src/sequence/sessions.ts +++ b/packages/wallet/wdk/src/sequence/sessions.ts @@ -1,5 +1,6 @@ import { Signers as CoreSigners, Envelope } from '@0xsequence/wallet-core' import { + Address, Attestation, Config, Constants, @@ -8,7 +9,7 @@ import { Signature as SequenceSignature, SessionConfig, } from '@0xsequence/wallet-primitives' -import { Address, Bytes, Hash, Hex } from 'ox' +import { Bytes, Hash, Hex } from 'ox' import { IdentityType } from '@0xsequence/identity-instrument' import { AuthCodePkceHandler } from './handlers/authcode-pkce.js' import { IdentityHandler, identityTypeToHex } from './handlers/identity.js' @@ -34,7 +35,7 @@ export interface SessionsInterface { * @returns A promise that resolves to the wallet's `SessionsTopology` object. * @throws An error if the wallet is not configured with a session manager or if the topology cannot be found. */ - getTopology(walletAddress: Address.Address): Promise + getTopology(walletAddress: Address.Checksummed): Promise /** * Initiates the authorization of an "implicit session". @@ -56,8 +57,8 @@ export interface SessionsInterface { * @see {completeAuthorizeImplicitSession} to finalize the process after signing. */ prepareAuthorizeImplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, args: AuthorizeImplicitSessionArgs, ): Promise @@ -97,8 +98,8 @@ export interface SessionsInterface { * @see {complete} to finalize the update after it has been signed. */ addExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, permissions: CoreSigners.Session.ExplicitParams, ): Promise @@ -119,8 +120,8 @@ export interface SessionsInterface { * @see {complete} to finalize the update after it has been signed. */ modifyExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, permissions: CoreSigners.Session.ExplicitParams, origin?: string, ): Promise @@ -136,7 +137,7 @@ export interface SessionsInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {complete} to finalize the update after it has been signed. */ - removeExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address): Promise + removeExplicitSession(walletAddress: Address.Checksummed, sessionAddress: Address.Checksummed): Promise /** * Initiates an on-chain configuration update to add a contract address to the implicit session blacklist. @@ -148,7 +149,7 @@ export interface SessionsInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {complete} to finalize the update after it has been signed. */ - addBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise + addBlacklistAddress(walletAddress: Address.Checksummed, address: Address.Checksummed): Promise /** * Initiates an on-chain configuration update to remove a contract address from the implicit session blacklist. @@ -158,7 +159,7 @@ export interface SessionsInterface { * @returns A promise that resolves to a `requestId` for the configuration update signature request. * @see {complete} to finalize the update after it has been signed. */ - removeBlacklistAddress(walletAddress: Address.Address, address: Address.Address): Promise + removeBlacklistAddress(walletAddress: Address.Checksummed, address: Address.Checksummed): Promise /** * Finalizes and saves a pending session configuration update. @@ -185,7 +186,7 @@ export interface SessionsInterface { export class Sessions implements SessionsInterface { constructor(private readonly shared: Shared) {} - async getTopology(walletAddress: Address.Address, fixMissing = false): Promise { + async getTopology(walletAddress: Address.Checksummed, fixMissing = false): Promise { const { loginTopology, modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress) const managerLeaf = modules.find((leaf) => Address.isEqual(leaf.address, this.shared.sequence.extensions.sessions)) if (!managerLeaf) { @@ -217,8 +218,8 @@ export class Sessions implements SessionsInterface { } async prepareAuthorizeImplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, args: AuthorizeImplicitSessionArgs, ): Promise { const topology = await this.getTopology(walletAddress) @@ -320,8 +321,8 @@ export class Sessions implements SessionsInterface { } async addExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, permissions: CoreSigners.Session.ExplicitParams, origin?: string, ): Promise { @@ -334,8 +335,8 @@ export class Sessions implements SessionsInterface { } async modifyExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, permissions: CoreSigners.Session.ExplicitParams, origin?: string, ): Promise { @@ -353,8 +354,8 @@ export class Sessions implements SessionsInterface { } async removeExplicitSession( - walletAddress: Address.Address, - sessionAddress: Address.Address, + walletAddress: Address.Checksummed, + sessionAddress: Address.Checksummed, origin?: string, ): Promise { const topology = await this.getTopology(walletAddress) @@ -366,8 +367,8 @@ export class Sessions implements SessionsInterface { } async addBlacklistAddress( - walletAddress: Address.Address, - address: Address.Address, + walletAddress: Address.Checksummed, + address: Address.Checksummed, origin?: string, ): Promise { const topology = await this.getTopology(walletAddress, true) @@ -376,8 +377,8 @@ export class Sessions implements SessionsInterface { } async removeBlacklistAddress( - walletAddress: Address.Address, - address: Address.Address, + walletAddress: Address.Checksummed, + address: Address.Checksummed, origin?: string, ): Promise { const topology = await this.getTopology(walletAddress) @@ -386,7 +387,7 @@ export class Sessions implements SessionsInterface { } private async prepareSessionUpdate( - walletAddress: Address.Address, + walletAddress: Address.Checksummed, topology: SessionConfig.SessionsTopology, origin: string = 'wallet-webapp', ): Promise { diff --git a/packages/wallet/wdk/src/sequence/signatures.ts b/packages/wallet/wdk/src/sequence/signatures.ts index 3369d4039..6593b5047 100644 --- a/packages/wallet/wdk/src/sequence/signatures.ts +++ b/packages/wallet/wdk/src/sequence/signatures.ts @@ -1,6 +1,6 @@ import { Envelope } from '@0xsequence/wallet-core' -import { Config, Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Config, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { v7 as uuidv7 } from 'uuid' import { Shared } from './manager.js' import { diff --git a/packages/wallet/wdk/src/sequence/signers.ts b/packages/wallet/wdk/src/sequence/signers.ts index 495f305f7..588244b9d 100644 --- a/packages/wallet/wdk/src/sequence/signers.ts +++ b/packages/wallet/wdk/src/sequence/signers.ts @@ -1,5 +1,5 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { Shared } from './manager.js' import { Kind, Kinds, SignerWithKind, WitnessExtraSignerKind } from './types/signer.js' @@ -23,7 +23,11 @@ function toKnownKind(kind: string): Kind { export class Signers { constructor(private readonly shared: Shared) {} - async kindOf(wallet: Address.Address, address: Address.Address, imageHash?: Hex.Hex): Promise { + async kindOf( + wallet: Address.Checksummed, + address: Address.Checksummed, + imageHash?: Hex.Hex, + ): Promise { // // The device may be among the local devices, in that case it is a local device // // TODO: Maybe signers shouldn't be getting in the way of devices, it feels like a // // different concern @@ -63,8 +67,8 @@ export class Signers { } async resolveKinds( - wallet: Address.Address, - signers: (Address.Address | { address: Address.Address; imageHash: Hex.Hex })[], + wallet: Address.Checksummed, + signers: (Address.Checksummed | { address: Address.Checksummed; imageHash: Hex.Hex })[], ): Promise { return Promise.all( signers.map(async (signer) => { diff --git a/packages/wallet/wdk/src/sequence/transactions.ts b/packages/wallet/wdk/src/sequence/transactions.ts index 26d7f0977..2d39475fe 100644 --- a/packages/wallet/wdk/src/sequence/transactions.ts +++ b/packages/wallet/wdk/src/sequence/transactions.ts @@ -1,6 +1,6 @@ -import { Constants, Payload } from '@0xsequence/wallet-primitives' +import { Address, Constants, Payload } from '@0xsequence/wallet-primitives' import { Envelope, Relayer, Wallet } from '@0xsequence/wallet-core' -import { Abi, AbiFunction, Address, Hex, Provider, RpcTransport } from 'ox' +import { Abi, AbiFunction, Hex, Provider, RpcTransport } from 'ox' import { v7 as uuidv7 } from 'uuid' import { Shared } from './manager.js' import { @@ -54,7 +54,7 @@ export interface TransactionsInterface { * @returns A promise that resolves to the unique `transactionId` for this new request. */ request( - from: Address.Address, + from: Address.Checksummed, chainId: bigint, txs: TransactionRequest[], options?: { source?: string; noConfigUpdate?: boolean; unsafe?: boolean; space?: bigint }, @@ -243,7 +243,7 @@ export class Transactions implements TransactionsInterface { } async request( - from: Address.Address, + from: Address.Checksummed, chainId: bigint, txs: TransactionRequest[], options?: { @@ -432,9 +432,7 @@ export class Transactions implements TransactionsInterface { // then we need to prepend the transaction payload with the fee const { token, to, value, gasLimit } = selection.feeOption - Address.assert(to) - - if (token.contractAddress === Constants.ZeroAddress) { + if (!token.contractAddress || token.contractAddress === Constants.ZeroAddress) { tx.envelope.payload.calls.unshift({ to, value: BigInt(value), @@ -446,9 +444,8 @@ export class Transactions implements TransactionsInterface { }) } else { const [transfer] = Abi.from(['function transfer(address to, uint256 amount) returns (bool)']) - tx.envelope.payload.calls.unshift({ - to: token.contractAddress as Address.Address, + to: Address.checksum(token.contractAddress), value: 0n, data: AbiFunction.encodeData(transfer, [to, BigInt(value)]), gasLimit: BigInt(gasLimit), diff --git a/packages/wallet/wdk/src/sequence/types/device.ts b/packages/wallet/wdk/src/sequence/types/device.ts index a7ca13080..03ab352e9 100644 --- a/packages/wallet/wdk/src/sequence/types/device.ts +++ b/packages/wallet/wdk/src/sequence/types/device.ts @@ -1,4 +1,4 @@ -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' /** * Represents a device key that is authorized to sign for a wallet. @@ -7,7 +7,7 @@ export interface Device { /** * The on-chain address of the device key. */ - address: Address.Address + address: Address.Checksummed /** * True if this is the key for the current local session. diff --git a/packages/wallet/wdk/src/sequence/types/message-request.ts b/packages/wallet/wdk/src/sequence/types/message-request.ts index 8c1d9e1cc..9a958d3df 100644 --- a/packages/wallet/wdk/src/sequence/types/message-request.ts +++ b/packages/wallet/wdk/src/sequence/types/message-request.ts @@ -1,12 +1,12 @@ import { Envelope } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' export type MessageRequest = string | Hex.Hex | Payload.TypedDataToSign type MessageBase = { id: string - wallet: Address.Address + wallet: Address.Checksummed message: MessageRequest source: string signatureId: string diff --git a/packages/wallet/wdk/src/sequence/types/recovery.ts b/packages/wallet/wdk/src/sequence/types/recovery.ts index f27247af7..a08606daa 100644 --- a/packages/wallet/wdk/src/sequence/types/recovery.ts +++ b/packages/wallet/wdk/src/sequence/types/recovery.ts @@ -1,12 +1,12 @@ -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' export type QueuedRecoveryPayload = { id: string index: bigint - recoveryModule: Address.Address - wallet: Address.Address - signer: Address.Address + recoveryModule: Address.Checksummed + wallet: Address.Checksummed + signer: Address.Checksummed chainId: bigint startTimestamp: bigint endTimestamp: bigint diff --git a/packages/wallet/wdk/src/sequence/types/signature-request.ts b/packages/wallet/wdk/src/sequence/types/signature-request.ts index cbce933da..5aa97c77a 100644 --- a/packages/wallet/wdk/src/sequence/types/signature-request.ts +++ b/packages/wallet/wdk/src/sequence/types/signature-request.ts @@ -1,6 +1,6 @@ import { Envelope } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' import { Handler } from '../handlers/handler.js' export type ActionToPayload = { @@ -42,7 +42,7 @@ export type BaseSignatureRequest = /** A unique identifier for the signature request (UUID v7). */ id: string /** The address of the wallet this request is for. */ - wallet: Address.Address + wallet: Address.Checksummed /** A string indicating the origin of the request (e.g., a dapp URL or 'wallet-webapp'). */ origin: string /** The ISO 8601 timestamp of when the request was created. */ @@ -62,7 +62,7 @@ export type BaseSignatureRequest = /** A unique identifier for the signature request (UUID v7). */ id: string /** The address of the wallet this request is for. */ - wallet: Address.Address + wallet: Address.Checksummed /** A string indicating the origin of the request (e.g., a dapp URL or 'wallet-webapp'). */ origin: string /** The ISO 8601 timestamp of when the request was created. */ @@ -89,7 +89,7 @@ export type BaseSignatureRequest = */ export type SignerBase = { /** The address of the signer. */ - address: Address.Address + address: Address.Checksummed /** * For sapient signers (e.g., passkeys, recovery modules), this is the hash of the * configuration tree that defines the signer's behavior, acting as a unique identifier. diff --git a/packages/wallet/wdk/src/sequence/types/signer.ts b/packages/wallet/wdk/src/sequence/types/signer.ts index 5fe72affc..ff1e7fb6b 100644 --- a/packages/wallet/wdk/src/sequence/types/signer.ts +++ b/packages/wallet/wdk/src/sequence/types/signer.ts @@ -1,4 +1,5 @@ -import { Address, Hex } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' export const Kinds = { LocalDevice: 'local-device', @@ -18,7 +19,7 @@ export type WitnessExtraSignerKind = { } export type SignerWithKind = { - address: Address.Address + address: Address.Checksummed kind?: Kind imageHash?: Hex.Hex } @@ -26,7 +27,7 @@ export type SignerWithKind = { export type RecoverySigner = { kind: Kind isRecovery: true - address: Address.Address + address: Address.Checksummed minTimestamp: bigint requiredDeltaTime: bigint } diff --git a/packages/wallet/wdk/src/sequence/types/transaction-request.ts b/packages/wallet/wdk/src/sequence/types/transaction-request.ts index 289bf5b64..d2dc0e71e 100644 --- a/packages/wallet/wdk/src/sequence/types/transaction-request.ts +++ b/packages/wallet/wdk/src/sequence/types/transaction-request.ts @@ -1,9 +1,9 @@ import { Envelope, Relayer } from '@0xsequence/wallet-core' -import { Payload } from '@0xsequence/wallet-primitives' -import { Address, Hex } from 'ox' +import { Address, Payload } from '@0xsequence/wallet-primitives' +import { Hex } from 'ox' export type TransactionRequest = { - to: Address.Address + to: Address.Checksummed value?: bigint data?: Hex.Hex gasLimit?: bigint @@ -41,7 +41,7 @@ export function isERC4337RelayerOption(relayerOption: RelayerOption): relayerOpt type TransactionBase = { id: string - wallet: Address.Address + wallet: Address.Checksummed requests: TransactionRequest[] source: string envelope: Envelope.Envelope diff --git a/packages/wallet/wdk/src/sequence/types/wallet.ts b/packages/wallet/wdk/src/sequence/types/wallet.ts index dd754a05b..1adaea4a7 100644 --- a/packages/wallet/wdk/src/sequence/types/wallet.ts +++ b/packages/wallet/wdk/src/sequence/types/wallet.ts @@ -1,4 +1,4 @@ -import { Address } from 'ox' +import { Address } from '@0xsequence/wallet-primitives' /** * Represents the local state of a managed wallet session within the SDK. @@ -9,7 +9,7 @@ export interface Wallet { * The unique, on-chain address of the wallet. * @property */ - address: Address.Address + address: Address.Checksummed /** * The current status of the wallet's session in the manager. @@ -32,7 +32,7 @@ export interface Wallet { * most signing operations, avoiding the need to use the primary login credential repeatedly. * @property */ - device: Address.Address + device: Address.Checksummed /** * A string identifier for the authentication method used for this session. @@ -91,13 +91,13 @@ export type WalletSelectionOptions = { * The UI should present these as login options. * @property */ - existingWallets: Address.Address[] + existingWallets: Address.Checksummed[] /** * The address of the signer/credential that triggered this selection flow (e.g., a passkey's public key address). * @property */ - signerAddress: Address.Address + signerAddress: Address.Checksummed /** * Additional context about how the selection handler was invoked. diff --git a/packages/wallet/wdk/src/sequence/wallets.ts b/packages/wallet/wdk/src/sequence/wallets.ts index d73923a25..290fd558f 100644 --- a/packages/wallet/wdk/src/sequence/wallets.ts +++ b/packages/wallet/wdk/src/sequence/wallets.ts @@ -1,6 +1,6 @@ import { Wallet as CoreWallet, Envelope, Signers, State } from '@0xsequence/wallet-core' -import { Config, Constants, GenericTree, Payload, SessionConfig } from '@0xsequence/wallet-primitives' -import { Address, Hex, Provider, RpcTransport } from 'ox' +import { Address, Config, Constants, GenericTree, Payload, SessionConfig } from '@0xsequence/wallet-primitives' +import { Hex, Provider, RpcTransport } from 'ox' import { AuthCommitment } from '../dbs/auth-commitments.js' import { MnemonicHandler } from './handlers/mnemonic.js' import { OtpHandler } from './handlers/otp.js' @@ -54,18 +54,18 @@ export type AuthCodeSignupArgs = CommonSignupArgs & { export type SignupArgs = PasskeySignupArgs | MnemonicSignupArgs | EmailOtpSignupArgs | AuthCodeSignupArgs export type LoginToWalletArgs = { - wallet: Address.Address + wallet: Address.Checksummed } export type LoginToMnemonicArgs = { kind: 'mnemonic' mnemonic: string - selectWallet: (wallets: Address.Address[]) => Promise + selectWallet: (wallets: Address.Checksummed[]) => Promise } export type LoginToPasskeyArgs = { kind: 'passkey' - selectWallet: (wallets: Address.Address[]) => Promise + selectWallet: (wallets: Address.Checksummed[]) => Promise } export type LoginArgs = LoginToWalletArgs | LoginToMnemonicArgs | LoginToPasskeyArgs @@ -81,7 +81,7 @@ export interface WalletsInterface { * @param wallet The address of the wallet to check. * @returns A promise that resolves to `true` if the wallet is managed, `false` otherwise. */ - has(wallet: Address.Address): Promise + has(wallet: Address.Checksummed): Promise /** * Retrieves the details of a managed wallet. @@ -94,7 +94,7 @@ export interface WalletsInterface { * @returns A promise that resolves to the `Wallet` object if found, or `undefined` if the wallet is not managed. * @see {Wallet} for details on the returned object structure. */ - get(walletAddress: Address.Address): Promise + get(walletAddress: Address.Checksummed): Promise /** * Lists all wallets that are currently managed and logged in by this manager instance. @@ -113,7 +113,7 @@ export interface WalletsInterface { * @param wallet The address of the wallet to query. * @returns A promise that resolves to an array of `Device` objects. */ - listDevices(wallet: Address.Address): Promise + listDevices(wallet: Address.Checksummed): Promise /** * Registers a UI handler for wallet selection. @@ -173,7 +173,7 @@ export interface WalletsInterface { * @returns A promise that resolves to the address of the newly created wallet, or `undefined` if the sign-up was aborted. * @see {SignupArgs} */ - signUp(args: SignupArgs): Promise + signUp(args: SignupArgs): Promise /** * Initiates a sign-up or login process that involves an OAuth redirect. @@ -248,7 +248,7 @@ export interface WalletsInterface { * containing the `requestId` for the on-chain logout transaction. */ logout( - wallet: Address.Address, + wallet: Address.Checksummed, options?: T, ): Promise @@ -261,7 +261,7 @@ export interface WalletsInterface { * @param deviceAddress The address of the device to log out. * @returns A promise that resolves to a `requestId` for the on-chain logout transaction. */ - remoteLogout(wallet: Address.Address, deviceAddress: Address.Address): Promise + remoteLogout(wallet: Address.Checksummed, deviceAddress: Address.Checksummed): Promise /** * Completes the "hard logout" process. @@ -299,7 +299,7 @@ export interface WalletsInterface { * @param wallet The address of the wallet. * @returns A promise that resolves to an object containing the resolved `devices`, `login` signers, and the `raw` configuration. */ - getConfiguration(wallet: Address.Address): Promise<{ + getConfiguration(wallet: Address.Checksummed): Promise<{ devices: SignerWithKind[] login: SignerWithKind[] raw: any @@ -316,7 +316,7 @@ export interface WalletsInterface { * @param space A unique identifier for a transaction category or flow, typically a large random number. * @returns A promise that resolves to the `bigint` nonce for the given space. */ - getNonce(chainId: bigint, address: Address.Address, space: bigint): Promise + getNonce(chainId: bigint, address: Address.Checksummed, space: bigint): Promise /** * Checks if the wallet's on-chain configuration is up to date for a given chain. @@ -329,7 +329,7 @@ export interface WalletsInterface { * @param chainId The chain ID of the network to check against. * @returns A promise that resolves to `true` if the wallet is up to date on the given chain, or `false` otherwise. */ - isUpdatedOnchain(wallet: Address.Address, chainId: bigint): Promise + isUpdatedOnchain(wallet: Address.Checksummed, chainId: bigint): Promise } export function isLoginToWalletArgs(args: LoginArgs): args is LoginToWalletArgs { @@ -348,7 +348,7 @@ export function isAuthCodeArgs(args: SignupArgs): args is AuthCodeSignupArgs { return 'kind' in args && (args.kind === 'google-pkce' || args.kind === 'apple') } -function buildCappedTree(members: { address: Address.Address; imageHash?: Hex.Hex }[]): Config.Topology { +function buildCappedTree(members: { address: Address.Checksummed; imageHash?: Hex.Hex }[]): Config.Topology { const loginMemberWeight = 1n if (members.length === 0) { @@ -505,11 +505,11 @@ export class Wallets implements WalletsInterface { constructor(private readonly shared: Shared) {} - public async has(wallet: Address.Address): Promise { + public async has(wallet: Address.Checksummed): Promise { return this.shared.databases.manager.get(wallet).then((r) => r !== undefined) } - public async get(walletAddress: Address.Address): Promise { + public async get(walletAddress: Address.Checksummed): Promise { return await this.shared.databases.manager.get(walletAddress) } @@ -517,7 +517,7 @@ export class Wallets implements WalletsInterface { return this.shared.databases.manager.list() } - public async listDevices(wallet: Address.Address): Promise { + public async listDevices(wallet: Address.Checksummed): Promise { const walletEntry = await this.get(wallet) if (!walletEntry) { throw new Error('wallet-not-found') @@ -681,7 +681,7 @@ export class Wallets implements WalletsInterface { return commitment.target } - async signUp(args: SignupArgs): Promise { + async signUp(args: SignupArgs): Promise { const loginSigner = await this.prepareSignUp(args) // If there is an existing wallet callback, we check if any wallet already exist for this login signer @@ -800,7 +800,7 @@ export class Wallets implements WalletsInterface { return wallet.address } - public async getConfigurationParts(address: Address.Address) { + public async getConfigurationParts(address: Address.Checksummed) { const wallet = new CoreWallet(address, { stateProvider: this.shared.sequence.stateProvider, guest: this.shared.sequence.guest, @@ -811,7 +811,7 @@ export class Wallets implements WalletsInterface { } public async requestConfigurationUpdate( - address: Address.Address, + address: Address.Checksummed, changes: Partial>, action: Action, origin?: string, @@ -985,7 +985,7 @@ export class Wallets implements WalletsInterface { } async logout( - wallet: Address.Address, + wallet: Address.Checksummed, options?: T, ): Promise { const walletEntry = await this.shared.databases.manager.get(wallet) @@ -1019,7 +1019,7 @@ export class Wallets implements WalletsInterface { return requestId as any } - public async remoteLogout(wallet: Address.Address, deviceAddress: Address.Address): Promise { + public async remoteLogout(wallet: Address.Checksummed, deviceAddress: Address.Checksummed): Promise { const walletEntry = await this.get(wallet) if (!walletEntry) { throw new Error('wallet-not-found') @@ -1053,7 +1053,7 @@ export class Wallets implements WalletsInterface { await this.shared.modules.devices.remove(walletEntry.device) } - async getConfiguration(wallet: Address.Address) { + async getConfiguration(wallet: Address.Checksummed) { const walletObject = new CoreWallet(wallet, { stateProvider: this.shared.sequence.stateProvider, guest: this.shared.sequence.guest, @@ -1078,7 +1078,7 @@ export class Wallets implements WalletsInterface { } } - async getNonce(chainId: bigint, address: Address.Address, space: bigint) { + async getNonce(chainId: bigint, address: Address.Checksummed, space: bigint) { const wallet = new CoreWallet(address, { stateProvider: this.shared.sequence.stateProvider, guest: this.shared.sequence.guest, @@ -1093,7 +1093,7 @@ export class Wallets implements WalletsInterface { return wallet.getNonce(provider, space) } - async getOnchainConfiguration(wallet: Address.Address, chainId: bigint) { + async getOnchainConfiguration(wallet: Address.Checksummed, chainId: bigint) { const walletObject = new CoreWallet(wallet, { stateProvider: this.shared.sequence.stateProvider, guest: this.shared.sequence.guest, @@ -1130,7 +1130,7 @@ export class Wallets implements WalletsInterface { } } - async isUpdatedOnchain(wallet: Address.Address, chainId: bigint) { + async isUpdatedOnchain(wallet: Address.Checksummed, chainId: bigint) { const walletObject = new CoreWallet(wallet, { stateProvider: this.shared.sequence.stateProvider, guest: this.shared.sequence.guest, @@ -1147,8 +1147,8 @@ export class Wallets implements WalletsInterface { } private async _prepareDeviceRemovalUpdate( - wallet: Address.Address, - deviceToRemove: Address.Address, + wallet: Address.Checksummed, + deviceToRemove: Address.Checksummed, action: 'logout' | 'remote-logout', ): Promise { const { devicesTopology, modules } = await this.getConfigurationParts(wallet) diff --git a/packages/wallet/wdk/test/constants.ts b/packages/wallet/wdk/test/constants.ts index 2c3754d48..ab03323ce 100644 --- a/packages/wallet/wdk/test/constants.ts +++ b/packages/wallet/wdk/test/constants.ts @@ -1,5 +1,6 @@ +import { Address } from '@0xsequence/wallet-primitives' import { config as dotenvConfig } from 'dotenv' -import { Abi, Address, Provider, RpcTransport } from 'ox' +import { Abi, Hex, Provider, RpcTransport } from 'ox' import { Manager, ManagerOptions, ManagerOptionsDefaults } from '../src/sequence' import { mockEthereum } from './setup' import { Signers as CoreSigners, State, Relayer } from '@0xsequence/wallet-core' @@ -8,7 +9,7 @@ import * as Db from '../src/dbs' const envFile = process.env.CI ? '.env.test' : '.env.test.local' dotenvConfig({ path: envFile }) -export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a3395aEf228F44' +export const EMITTER_ADDRESS: Address.Checksummed = '0xb7bE532959236170064cf099e1a3395aEf228F44' export const EMITTER_ABI = Abi.from(['function explicitEmit()', 'function implicitEmit()']) // Environment variables @@ -65,7 +66,7 @@ export function newManager(options?: ManagerOptions, noEthereumMock?: boolean, t export function newRemoteManager( remoteManagerOptions: { network: { - relayerPk: string + relayerPk: Hex.Hex bundlerUrl: string rpcUrl: string chainId: bigint @@ -84,7 +85,7 @@ export function newRemoteManager( if (remoteManagerOptions.network.relayerPk) { const provider = Provider.from(RpcTransport.fromHttp(remoteManagerOptions.network.rpcUrl)) - relayers.push(new Relayer.Standard.PkRelayer(remoteManagerOptions.network.relayerPk as `0x${string}`, provider)) + relayers.push(new Relayer.Standard.PkRelayer(remoteManagerOptions.network.relayerPk, provider)) } if (remoteManagerOptions.network.bundlerUrl) { diff --git a/packages/wallet/wdk/test/sessions.test.ts b/packages/wallet/wdk/test/sessions.test.ts index cd92517dd..ca59763e7 100644 --- a/packages/wallet/wdk/test/sessions.test.ts +++ b/packages/wallet/wdk/test/sessions.test.ts @@ -1,7 +1,7 @@ -import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox' +import { AbiFunction, Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox' import { beforeEach, describe, expect, it, vi } from 'vitest' import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, Relayer, State } from '../../core/src/index.js' -import { Attestation, Constants, Extensions, Payload, Permission } from '../../primitives/src/index.js' +import { Address, Attestation, Constants, Extensions, Payload, Permission } from '../../primitives/src/index.js' import { Sequence } from '../src/index.js' import { CAN_RUN_LIVE, EMITTER_ABI, EMITTER_ADDRESS, PRIVATE_KEY, RPC_URL } from './constants' @@ -13,7 +13,7 @@ describe('Sessions (via Manager)', () => { // Wallet webapp components let wdk: { - identitySignerAddress: Address.Address + identitySignerAddress: Address.Checksummed manager: Sequence.Manager } @@ -25,7 +25,7 @@ describe('Sessions (via Manager)', () => { } const setupExplicitSession = async ( - sessionAddress: Address.Address, + sessionAddress: Address.Checksummed, permissions: Permission.SessionPermissions, isModify = false, ) => { @@ -170,8 +170,8 @@ describe('Sessions (via Manager)', () => { // Send the transaction if (CAN_RUN_LIVE && PRIVATE_KEY) { // Load the sender - const senderPk = Hex.from(PRIVATE_KEY as `0x${string}`) - const pkRelayer = new Relayer.Standard.PkRelayer(senderPk, provider) + Hex.assert(PRIVATE_KEY) + const pkRelayer = new Relayer.Standard.PkRelayer(PRIVATE_KEY, provider) const tx = await pkRelayer.relay(transaction.to, transaction.data, chainId, undefined) console.log('Transaction sent', tx) await new Promise((resolve) => setTimeout(resolve, 3000)) diff --git a/packages/wallet/wdk/test/transactions.test.ts b/packages/wallet/wdk/test/transactions.test.ts index b04364cda..867f7ebc7 100644 --- a/packages/wallet/wdk/test/transactions.test.ts +++ b/packages/wallet/wdk/test/transactions.test.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, it } from 'vitest' import { Manager, SignerActionable, Transaction, TransactionDefined, TransactionRelayed } from '../src/sequence' -import { Address, Hex, Mnemonic, Provider, RpcTransport } from 'ox' +import { Hex, Mnemonic, Provider, RpcTransport } from 'ox' import { LOCAL_RPC_URL, newManager } from './constants' -import { Payload } from '@0xsequence/wallet-primitives' +import { Address, Payload } from '@0xsequence/wallet-primitives' describe('Transactions', () => { let manager: Manager | undefined @@ -28,7 +28,7 @@ describe('Transactions', () => { params: [wallet!, '0xa'], }) - const recipient = Address.from(Hex.random(20)) + const recipient = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to: recipient, @@ -141,7 +141,7 @@ describe('Transactions', () => { }) // Send a transaction - const recipient = Address.from(Hex.random(20)) + const recipient = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to: recipient, @@ -217,7 +217,7 @@ describe('Transactions', () => { calledTimes++ }) - const to = Address.from(Hex.random(20)) + const to = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to, @@ -247,7 +247,7 @@ describe('Transactions', () => { expect(wallet).toBeDefined() await expect(manager.wallets.has(wallet!)).resolves.toBeTruthy() - const to = Address.from(Hex.random(20)) + const to = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to, @@ -315,7 +315,7 @@ describe('Transactions', () => { noGuard: true, }) - const to = Address.from(Hex.random(20)) + const to = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to, @@ -336,7 +336,7 @@ describe('Transactions', () => { noGuard: true, }) - const to = Address.from(Hex.random(20)) + const to = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to, @@ -359,7 +359,7 @@ describe('Transactions', () => { noGuard: true, }) - const to = Address.from(Hex.random(20)) + const to = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to, @@ -395,7 +395,7 @@ describe('Transactions', () => { }) // Add a recovery signer, just to change the configuration - const rSigId = await manager.recovery.addSigner(wallet!, Address.from(Hex.random(20))) + const rSigId = await manager.recovery.addSigner(wallet!, Address.checksum(Hex.random(20))) expect(rSigId).toBeDefined() // Sign using the device signer @@ -414,7 +414,7 @@ describe('Transactions', () => { // It should no longer be updated onchain await expect(manager.wallets.isUpdatedOnchain(wallet!, 42161n)).resolves.toBeFalsy() - const randomAddress = Address.from(Hex.random(20)) + const randomAddress = Address.checksum(Hex.random(20)) const txId = await manager.transactions.request(wallet!, 42161n, [ { to: randomAddress,