diff --git a/chain-abstraction/add-owner-passkey.ts b/chain-abstraction/add-owner-passkey.ts index 41dc532..8821a8c 100644 --- a/chain-abstraction/add-owner-passkey.ts +++ b/chain-abstraction/add-owner-passkey.ts @@ -16,7 +16,8 @@ */ import * as dotenv from 'dotenv' -import * as ethers from 'ethers' +import { hexToBytes, keccak256, toBytes, numberToBytes } from 'viem' +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" import { SafeMultiChainSigAccount as SafeAccount, AllowAllPaymaster, @@ -64,11 +65,11 @@ async function main(): Promise { id: 'safe.global', }, user: { - id: ethers.getBytes(ethers.id('chain-abstraction-demo')), + id: hexToBytes(keccak256(toBytes('chain-abstraction-demo'))), name: 'demo-user', displayName: 'Demo User', }, - challenge: ethers.toBeArray(Date.now()), + challenge: numberToBytes(Date.now()), pubKeyCredParams: [{ type: 'public-key', alg: -7 }], }, }) @@ -84,7 +85,7 @@ async function main(): Promise { console.log(" Public key X:", publicKey.x.toString().slice(0, 20) + "...") // Generate a new owner address to add (using random address for demo) - const newOwnerAddress = ethers.Wallet.createRandom().address + const newOwnerAddress = privateKeyToAccount(generatePrivateKey()).address console.log("\nPasskey owner (signer):", credential.id.slice(0, 20) + "...") console.log("New owner to add:", newOwnerAddress) @@ -157,7 +158,7 @@ async function main(): Promise { // In a browser, this would trigger device biometrics const assertion = navigator.credentials.get({ publicKey: { - challenge: ethers.getBytes(multiChainHash), + challenge: hexToBytes(multiChainHash as `0x${string}`), rpId: 'safe.global', allowCredentials: [{ type: 'public-key', id: new Uint8Array(credential.rawId) }], userVerification: UserVerificationRequirement.required, diff --git a/eip-7702/upgrade-eoa-to-7702-smart-account-sponsored-gas.ts b/eip-7702/upgrade-eoa-to-7702-smart-account-sponsored-gas.ts index cf3d36e..5ca9187 100644 --- a/eip-7702/upgrade-eoa-to-7702-smart-account-sponsored-gas.ts +++ b/eip-7702/upgrade-eoa-to-7702-smart-account-sponsored-gas.ts @@ -6,7 +6,7 @@ import { createAndSignEip7702DelegationAuthorization, CandidePaymaster, } from "abstractionkit"; -import { Wallet } from 'ethers'; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; async function main(): Promise { //get values from .env @@ -14,10 +14,10 @@ async function main(): Promise { const chainId = BigInt(process.env.CHAIN_ID as string) const bundlerUrl = process.env.BUNDLER_URL as string const nodeUrl = process.env.NODE_URL as string; - - const eoaDelegator = Wallet.createRandom(); + + const eoaDelegatorPrivateKey = generatePrivateKey(); + const eoaDelegator = privateKeyToAccount(eoaDelegatorPrivateKey); const eoaDelegatorPublicAddress = eoaDelegator.address; - const eoaDelegatorPrivateKey = eoaDelegator.privateKey; const paymasterUrl = process.env.PAYMASTER_URL as string; const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID as string; diff --git a/eip-7702/upgrade-eoa-to-7702-smart-account-wallet-signed.ts b/eip-7702/upgrade-eoa-to-7702-smart-account-wallet-signed.ts index df203ab..457c2a5 100644 --- a/eip-7702/upgrade-eoa-to-7702-smart-account-wallet-signed.ts +++ b/eip-7702/upgrade-eoa-to-7702-smart-account-wallet-signed.ts @@ -7,7 +7,8 @@ import { createEip7702DelegationAuthorizationHash, createUserOperationHash, } from "abstractionkit"; -import { JsonRpcProvider, toBeHex, Wallet } from 'ethers'; +import { generatePrivateKey, privateKeyToAccount, sign } from "viem/accounts"; +import { createPublicClient, http, toHex } from "viem"; async function main(): Promise { try { @@ -17,9 +18,10 @@ async function main(): Promise { const bundlerUrl = process.env.BUNDLER_URL as string const nodeUrl = process.env.NODE_URL as string; - const provider = new JsonRpcProvider(nodeUrl); + const client = createPublicClient({ transport: http(nodeUrl) }); - const eoaDelegator = Wallet.createRandom(provider); + const eoaDelegatorPrivateKey = generatePrivateKey(); + const eoaDelegator = privateKeyToAccount(eoaDelegatorPrivateKey); const eoaDelegatorPublicAddress = eoaDelegator.address; const paymasterUrl = process.env.PAYMASTER_URL as string; const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID as string; @@ -58,7 +60,7 @@ async function main(): Promise { } ); - const nonce = await eoaDelegator.getNonce(); + const nonce = await client.getTransactionCount({ address: eoaDelegatorPublicAddress }); const eip7702DelegationAuthorizationHash = createEip7702DelegationAuthorizationHash( chainId, @@ -66,17 +68,15 @@ async function main(): Promise { BigInt(nonce) ); - const signedHash = eoaDelegator.signingKey.sign( - eip7702DelegationAuthorizationHash, - ); + const delegationSig = await sign({ hash: eip7702DelegationAuthorizationHash as `0x${string}`, privateKey: eoaDelegatorPrivateKey }); userOperation.eip7702Auth = { - chainId: toBeHex(chainId), + chainId: toHex(chainId), address: smartAccount.delegateeAddress, - nonce: toBeHex(nonce), - yParity: toBeHex(signedHash.yParity), - r: signedHash.r, - s: signedHash.s + nonce: toHex(nonce), + yParity: toHex(delegationSig.v === 27n ? 0 : 1), + r: delegationSig.r, + s: delegationSig.s }; const paymaster = new CandidePaymaster(paymasterUrl); @@ -91,7 +91,7 @@ async function main(): Promise { chainId, ); - userOperation.signature = eoaDelegator.signingKey.sign(userOperationHash).serialized; + userOperation.signature = await sign({ hash: userOperationHash as `0x${string}`, privateKey: eoaDelegatorPrivateKey, to: 'hex' }); let sendUserOperationResponse = await smartAccount.sendUserOperation( userOperation, bundlerUrl diff --git a/multisig/multisig.ts b/multisig/multisig.ts index 74aad5e..322a412 100644 --- a/multisig/multisig.ts +++ b/multisig/multisig.ts @@ -9,7 +9,7 @@ import { CandidePaymaster, } from "abstractionkit"; -import { Wallet } from "ethers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; async function main(): Promise { //get values from .env @@ -21,8 +21,10 @@ async function main(): Promise { const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID; - const owner1 = Wallet.createRandom(); - const owner2 = Wallet.createRandom(); + const owner1PrivateKey = generatePrivateKey(); + const owner1 = privateKeyToAccount(owner1PrivateKey); + const owner2PrivateKey = generatePrivateKey(); + const owner2 = privateKeyToAccount(owner2PrivateKey); //initializeNewAccount only needed when the smart account //have not been deployed yet for its first useroperation. @@ -98,7 +100,7 @@ async function main(): Promise { //privateKeys userOperation.signature = smartAccount.signUserOperation( userOperation, - [owner1.privateKey, owner2.privateKey], + [owner1PrivateKey, owner2PrivateKey], chainId ) console.log(userOperation) diff --git a/nested-safe-accounts/nested-safe-accounts.ts b/nested-safe-accounts/nested-safe-accounts.ts index d62933f..1a0ad27 100644 --- a/nested-safe-accounts/nested-safe-accounts.ts +++ b/nested-safe-accounts/nested-safe-accounts.ts @@ -1,5 +1,5 @@ import * as dotenv from 'dotenv' -import * as ethers from 'ethers' +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { SafeAccountV0_3_0 as SafeAccount, @@ -20,11 +20,15 @@ async function main(): Promise { const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID as string; /*subaccount1 signers*/ - const signer1Subaccount1 = ethers.Wallet.createRandom(); - const signer2Subaccount1 = ethers.Wallet.createRandom(); + const signer1Subaccount1PrivateKey = generatePrivateKey(); + const signer1Subaccount1 = privateKeyToAccount(signer1Subaccount1PrivateKey); + const signer2Subaccount1PrivateKey = generatePrivateKey(); + const signer2Subaccount1 = privateKeyToAccount(signer2Subaccount1PrivateKey); /*subaccount2 signers*/ - const signer1Subaccount2 = ethers.Wallet.createRandom(); - const signer2Subaccount2 = ethers.Wallet.createRandom(); + const signer1Subaccount2PrivateKey = generatePrivateKey(); + const signer1Subaccount2 = privateKeyToAccount(signer1Subaccount2PrivateKey); + const signer2Subaccount2PrivateKey = generatePrivateKey(); + const signer2Subaccount2 = privateKeyToAccount(signer2Subaccount2PrivateKey); /* initialize subaccounts */ const subAccount1 = SafeAccount.initializeNewAccount( @@ -66,7 +70,7 @@ async function main(): Promise { subAccount1DeployMainAccountUserOperation.signature = subAccount1.signUserOperation( subAccount1DeployMainAccountUserOperation, - [signer1Subaccount1.privateKey, signer2Subaccount1.privateKey], + [signer1Subaccount1PrivateKey, signer2Subaccount1PrivateKey], chainId, ) @@ -176,7 +180,7 @@ async function main(): Promise { subAccount1UserOperation.signature = subAccount1.signUserOperation( subAccount1UserOperation, - [signer1Subaccount1.privateKey, signer2Subaccount1.privateKey], + [signer1Subaccount1PrivateKey, signer2Subaccount1PrivateKey], chainId, ) let subAccount2UserOperation = await subAccount2.createUserOperation( @@ -190,7 +194,7 @@ async function main(): Promise { subAccount2UserOperation.signature = subAccount2.signUserOperation( subAccount2UserOperation, - [signer1Subaccount2.privateKey, signer2Subaccount2.privateKey], + [signer1Subaccount2PrivateKey, signer2Subaccount2PrivateKey], chainId, ) /***********************************/ diff --git a/passkeys/index.ts b/passkeys/index.ts index 1eac5a2..fceeb69 100644 --- a/passkeys/index.ts +++ b/passkeys/index.ts @@ -11,7 +11,7 @@ */ import * as dotenv from 'dotenv' -import * as ethers from 'ethers' +import { hexToBytes, keccak256, toBytes, numberToBytes } from 'viem' import { SafeAccountV0_3_0 as SafeAccount, MetaTransaction, @@ -68,11 +68,11 @@ async function main(): Promise { id: 'safe.global', }, user: { - id: ethers.getBytes(ethers.id('chucknorris')), + id: hexToBytes(keccak256(toBytes('chucknorris'))), name: 'chucknorris', displayName: 'Chuck Norris', }, - challenge: ethers.toBeArray(Date.now()), + challenge: numberToBytes(Date.now()), pubKeyCredParams: [{ type: 'public-key', alg: -7 }], }, }) @@ -150,7 +150,7 @@ async function main(): Promise { // Simulate passkey authentication (biometric prompt in real browser) const assertion = navigator.credentials.get({ publicKey: { - challenge: ethers.getBytes(safeInitOpHash), + challenge: hexToBytes(safeInitOpHash as `0x${string}`), rpId: 'safe.global', allowCredentials: [{ type: 'public-key', id: new Uint8Array(credential.rawId) }], userVerification: UserVerificationRequirement.required, diff --git a/passkeys/passkeys-v0.2.1.ts b/passkeys/passkeys-v0.2.1.ts index d978e2b..c5f17a3 100644 --- a/passkeys/passkeys-v0.2.1.ts +++ b/passkeys/passkeys-v0.2.1.ts @@ -16,7 +16,7 @@ */ import * as dotenv from 'dotenv' -import * as ethers from 'ethers' +import { hexToBytes, keccak256, toBytes, numberToBytes } from 'viem' import { SafeAccountV0_3_0 as SafeAccount, MetaTransaction, @@ -92,11 +92,11 @@ async function main(): Promise { id: 'safe.global', }, user: { - id: ethers.getBytes(ethers.id('chucknorris')), + id: hexToBytes(keccak256(toBytes('chucknorris'))), name: 'chucknorris', displayName: 'Chuck Norris', }, - challenge: ethers.toBeArray(Date.now()), + challenge: numberToBytes(Date.now()), pubKeyCredParams: [{ type: 'public-key', alg: -7 }], }, }) @@ -182,7 +182,7 @@ async function main(): Promise { // Simulate passkey authentication (biometric prompt in real browser) const assertion = navigator.credentials.get({ publicKey: { - challenge: ethers.getBytes(safeInitOpHash), + challenge: hexToBytes(safeInitOpHash as `0x${string}`), rpId: 'safe.global', allowCredentials: [{ type: 'public-key', id: new Uint8Array(credential.rawId) }], userVerification: UserVerificationRequirement.required, diff --git a/passkeys/webauthn.ts b/passkeys/webauthn.ts index 80ac17d..8ae8626 100644 --- a/passkeys/webauthn.ts +++ b/passkeys/webauthn.ts @@ -8,9 +8,9 @@ * [1]: */ -import { p256 } from '@noble/curves/p256' -import { ethers, BytesLike } from 'ethers' -import CBOR from 'cbor' +import * as crypto from 'node:crypto' +import { keccak256, sha256, toHex, hexToBytes, toBytes, maxUint256, type Hex } from 'viem' +import * as CBOR from 'cbor' export interface CredentialCreationOptions { publicKey: PublicKeyCredentialCreationOptions @@ -69,7 +69,7 @@ export interface PublicKeyCredential { } /** - * The authenticator's response to a client’s request for the creation of a new public key credential. + * The authenticator's response to a client's request for the creation of a new public key credential. * See . */ export interface AuthenticatorAttestationResponse { @@ -78,7 +78,7 @@ export interface AuthenticatorAttestationResponse { } /** - * The authenticator's response to a client’s request generation of a new authentication assertion given the WebAuthn Relying Party's challenge. + * The authenticator's response to a client's request generation of a new authentication assertion given the WebAuthn Relying Party's challenge. * See . */ export interface AuthenticatorAssertionResponse { @@ -89,40 +89,66 @@ export interface AuthenticatorAssertionResponse { } class Credential { - public id: string - public pk: bigint + public id: Hex + public privateKey: crypto.KeyObject + private publicKeyUncompressed: Uint8Array // 65 bytes: 0x04 || x || y constructor( public rp: string, public user: Uint8Array, ) { - this.pk = p256.utils.normPrivateKeyToScalar(p256.utils.randomPrivateKey()) - this.id = ethers.dataSlice(ethers.keccak256(ethers.dataSlice(p256.getPublicKey(this.pk, false), 1)), 12) + const keyPair = crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' }) + this.privateKey = keyPair.privateKey + + // Export uncompressed public key (0x04 || x || y) + const pubJwk = keyPair.publicKey.export({ format: 'jwk' }) + const x = Buffer.from(pubJwk.x!, 'base64url') + const y = Buffer.from(pubJwk.y!, 'base64url') + this.publicKeyUncompressed = new Uint8Array(Buffer.concat([Buffer.from([0x04]), x, y])) + + // Credential ID = last 20 bytes of keccak256(pubkey without 0x04 prefix) + const pubKeyHash = keccak256(toHex(this.publicKeyUncompressed.slice(1))) + this.id = `0x${pubKeyHash.slice(26)}` as Hex // skip "0x" + 24 hex chars = 12 bytes } /** * Computes the COSE encoded public key for this credential. * See . - * - * @returns Hex-encoded COSE-encoded public key */ - public cosePublicKey(): string { - const pubk = p256.getPublicKey(this.pk, false) - const x = pubk.subarray(1, 33) - const y = pubk.subarray(33, 65) + public cosePublicKey(): Buffer { + const x = this.publicKeyUncompressed.subarray(1, 33) + const y = this.publicKeyUncompressed.subarray(33, 65) - // const key = new Map() - // key.set(-1, 1) // crv = P-256 key.set(-2, b2ab(x)) key.set(-3, b2ab(y)) - // key.set(1, 2) // kty = EC2 - key.set(3, -7) // alg = ES256 (Elliptic curve signature with SHA-256) + key.set(3, -7) // alg = ES256 + return CBOR.encode(key) + } +} - return ethers.hexlify(CBOR.encode(key)) +/** + * Build authenticator data as a binary buffer. + * See + */ +function buildAuthenticatorData( + rpId: string, + flags: number, + signCount: number, + attestedCredentialData?: Buffer, +): Buffer { + const rpIdHash = Buffer.from(hexToBytes(sha256(toBytes(rpId)))) + const flagsBuf = Buffer.from([flags]) + const signCountBuf = Buffer.alloc(4) + signCountBuf.writeUInt32BE(signCount) + + const parts = [rpIdHash, flagsBuf, signCountBuf] + if (attestedCredentialData) { + parts.push(attestedCredentialData) } + return Buffer.concat(parts) } export class WebAuthnCredentials { @@ -131,9 +157,6 @@ export class WebAuthnCredentials { /** * This is a shim for `navigator.credentials.create` method. * See . - * - * @param options The public key credential creation options. - * @returns A public key credential with an attestation response. */ public create({ publicKey }: CredentialCreationOptions): PublicKeyCredential { if (!publicKey.pubKeyCredParams.some(({ alg }) => alg === -7)) { @@ -143,7 +166,6 @@ export class WebAuthnCredentials { const credential = new Credential(publicKey.rp.id, publicKey.user.id) this.#credentials.push(credential) - // const clientData = { type: 'webauthn.create', challenge: base64UrlEncode(publicKey.challenge).replace(/=*$/, ''), @@ -151,33 +173,29 @@ export class WebAuthnCredentials { } const userVerification = publicKey.userVerification ?? 'preferred' - const userVerificationFlag = userVerification === UserVerificationRequirement.required ? 0x04 : 0x01 - - // - const attestationObject = { - authData: ethers.getBytes( - ethers.solidityPacked( - ['bytes32', 'uint8', 'uint32', 'bytes16', 'uint16', 'bytes', 'bytes'], - [ - ethers.sha256(ethers.toUtf8Bytes(publicKey.rp.id)), - 0x40 + userVerificationFlag, // flags = attested_data + user_present - 0, // signCount - `0x${'42'.repeat(16)}`, // aaguid - ethers.dataLength(credential.id), - credential.id, - credential.cosePublicKey(), - ], - ), - ), - fmt: 'none', - attStmt: {}, - } + const uvFlag = userVerification === UserVerificationRequirement.required ? 0x04 : 0x00 + + // Build attested credential data: aaguid (16) + credIdLen (2) + credId + coseKey + const aaguid = Buffer.alloc(16, 0x42) + const credIdBytes = Buffer.from(hexToBytes(credential.id)) + const credIdLen = Buffer.alloc(2) + credIdLen.writeUInt16BE(credIdBytes.length) + const attestedCredentialData = Buffer.concat([aaguid, credIdLen, credIdBytes, credential.cosePublicKey()]) + + const authData = buildAuthenticatorData( + publicKey.rp.id, + 0x41 | uvFlag, // flags = AT (0x40) + UP (0x01) + optionally UV (0x04) + 0, + attestedCredentialData, + ) + + const attestationObject = { authData, fmt: 'none', attStmt: {} } return { id: base64UrlEncode(credential.id), - rawId: ethers.getBytes(credential.id), + rawId: b2ab(hexToBytes(credential.id)), response: { - clientDataJSON: b2ab(ethers.toUtf8Bytes(JSON.stringify(clientData))), + clientDataJSON: b2ab(Buffer.from(JSON.stringify(clientData))), attestationObject: b2ab(CBOR.encode(attestationObject)), }, type: 'public-key', @@ -187,19 +205,15 @@ export class WebAuthnCredentials { /** * This is a shim for `navigator.credentials.get` method. * See . - * - * @param options The public key credential request options. - * @returns A public key credential with an assertion response. */ get({ publicKey }: CredentialRequestOptions): PublicKeyCredential { const credential = publicKey.allowCredentials - .flatMap(({ id }) => this.#credentials.filter((c) => c.rp === publicKey.rpId && c.id === ethers.hexlify(id))) + .flatMap(({ id }) => this.#credentials.filter((c) => c.rp === publicKey.rpId && c.id === toHex(id))) .at(0) if (credential === undefined) { throw new Error('credential not found') } - // const clientData = { type: 'webauthn.get', challenge: base64UrlEncode(publicKey.challenge).replace(/=*$/, ''), @@ -207,38 +221,22 @@ export class WebAuthnCredentials { } const userVerification = publicKey.userVerification ?? 'preferred' - const userVerificationFlag = userVerification === UserVerificationRequirement.required ? 0x04 : 0x01 - // - // Note that we use a constant 0 value for signCount to simplify things: - // > If the authenticator does not implement a signature counter, let the signature counter - // > value remain constant at zero. - const authenticatorData = ethers.solidityPacked( - ['bytes32', 'uint8', 'uint32'], - [ - ethers.sha256(ethers.toUtf8Bytes(publicKey.rpId)), - userVerificationFlag, // flags = user_present - 0, // signCount - ], - ) + const uvFlag = userVerification === UserVerificationRequirement.required ? 0x04 : 0x00 - // - // - const signature = p256.sign( - ethers.getBytes(ethers.concat([authenticatorData, ethers.sha256(ethers.toUtf8Bytes(JSON.stringify(clientData)))])), - credential.pk, - { - lowS: false, - prehash: true, - }, - ) + const authenticatorData = buildAuthenticatorData(publicKey.rpId, 0x01 | uvFlag, 0) + + // Sign: authenticatorData || sha256(clientDataJSON) + const clientDataHash = Buffer.from(hexToBytes(sha256(toBytes(JSON.stringify(clientData))))) + const dataToSign = Buffer.concat([authenticatorData, clientDataHash]) + const derSignature = crypto.sign('sha256', dataToSign, credential.privateKey) return { id: base64UrlEncode(credential.id), - rawId: ethers.getBytes(credential.id), + rawId: b2ab(hexToBytes(credential.id)), response: { - clientDataJSON: b2ab(ethers.toUtf8Bytes(JSON.stringify(clientData))), - authenticatorData: b2ab(ethers.getBytes(authenticatorData)), - signature: b2ab(signature.toDERRawBytes(false)), + clientDataJSON: b2ab(Buffer.from(JSON.stringify(clientData))), + authenticatorData: b2ab(authenticatorData), + signature: b2ab(derSignature), userHandle: credential.user, }, type: 'public-key', @@ -248,15 +246,16 @@ export class WebAuthnCredentials { /** * Encode bytes using the Base64 URL encoding. - * * See - * - * @param data data to encode to `base64url` - * @returns the `base64url` encoded data as a string. */ -export function base64UrlEncode(data: BytesLike | ArrayBufferLike): string { - const bytes = ethers.isBytesLike(data) ? data : new Uint8Array(data) - return ethers.encodeBase64(bytes).replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, '') +export function base64UrlEncode(data: Hex | Uint8Array | ArrayBufferLike): string { + if (typeof data === 'string') { + return Buffer.from(hexToBytes(data)).toString('base64url') + } + if (data instanceof Uint8Array) { + return Buffer.from(data).toString('base64url') + } + return Buffer.from(new Uint8Array(data)).toString('base64url') } function b2ab(buf: Uint8Array): ArrayBuffer { @@ -269,11 +268,12 @@ function b2ab(buf: Uint8Array): ArrayBuffer { */ export function extractPublicKey(response: AuthenticatorAttestationResponse): { x: bigint; y: bigint } { const attestationObject = CBOR.decode(response.attestationObject) - const authDataView = new DataView(attestationObject.authData.buffer) + const authData: Buffer = attestationObject.authData + const authDataView = new DataView(authData.buffer, authData.byteOffset, authData.byteLength) const credentialIdLength = authDataView.getUint16(53) - const cosePublicKey = attestationObject.authData.slice(55 + credentialIdLength) + const cosePublicKey = authData.subarray(55 + credentialIdLength) const key: Map = CBOR.decode(cosePublicKey) - const bn = (bytes: Uint8Array) => BigInt(ethers.hexlify(bytes)) + const bn = (bytes: Uint8Array) => BigInt(toHex(bytes)) return { x: bn(key.get(-2) as Uint8Array), y: bn(key.get(-3) as Uint8Array), @@ -296,7 +296,7 @@ export function extractClientDataFields(response: AuthenticatorAssertionResponse } const [, fields] = match - return ethers.hexlify(ethers.toUtf8Bytes(fields)) + return toHex(toBytes(fields)) } /** @@ -328,12 +328,12 @@ export function extractSignature(response: AuthenticatorAssertionResponse): [big const len = view.getUint8(offset + 1) const start = offset + 2 const end = start + len - const n = BigInt(ethers.hexlify(new Uint8Array(view.buffer.slice(start, end)))) - check(n < ethers.MaxUint256) + const n = BigInt(toHex(new Uint8Array(view.buffer.slice(start, end)))) + check(n < maxUint256) return [n, end] as const } const [r, sOffset] = readInt(2) const [s] = readInt(sOffset) return [r, s] -} \ No newline at end of file +} diff --git a/recovery/recovery.ts b/recovery/recovery.ts index 5e4ed41..545d92e 100644 --- a/recovery/recovery.ts +++ b/recovery/recovery.ts @@ -7,7 +7,7 @@ import { SocialRecoveryModule } from "abstractionkit"; -import { Wallet } from "ethers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; async function main(): Promise { //get values from .env @@ -18,13 +18,14 @@ async function main(): Promise { const paymasterUrl = process.env.PAYMASTER_URL as string; const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID as string; - const safeOwner = Wallet.createRandom(); + const safeOwnerPrivateKey = generatePrivateKey(); + const safeOwner = privateKeyToAccount(safeOwnerPrivateKey); const ownerPublicAddress = process.env.PUBLIC_ADDRESS || safeOwner.address as string - const ownerPrivateKey = process.env.PRIVATE_KEY || safeOwner.privateKey as string + const ownerPrivateKey = process.env.PRIVATE_KEY || safeOwnerPrivateKey as string - const guardianWallet = Wallet.createRandom(); - const guardianPublicAddress = guardianWallet.address as string; + const guardian = privateKeyToAccount(generatePrivateKey()); + const guardianPublicAddress = guardian.address as string; //initializeNewAccount only needed when the smart account //have not been deployed yet for its first useroperation. diff --git a/simulate-with-tenderly/simulate-with-tenderly.ts b/simulate-with-tenderly/simulate-with-tenderly.ts index 33285c8..a11de41 100644 --- a/simulate-with-tenderly/simulate-with-tenderly.ts +++ b/simulate-with-tenderly/simulate-with-tenderly.ts @@ -1,5 +1,5 @@ import * as dotenv from 'dotenv' -import { Wallet } from "ethers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { SafeAccountV0_3_0, MetaTransaction, @@ -14,9 +14,10 @@ async function main(): Promise { const bundlerUrl = process.env.BUNDLER_URL as string const nodeUrl = process.env.NODE_URL as string - const owner = Wallet.createRandom(); + const ownerPrivateKeyGenerated = generatePrivateKey(); + const owner = privateKeyToAccount(ownerPrivateKeyGenerated); const ownerPublicAddress = process.env.PUBLIC_ADDRESS || owner.address as string - const ownerPrivateKey = process.env.PRIVATE_KEY || owner.privateKey as string + const ownerPrivateKey = process.env.PRIVATE_KEY || ownerPrivateKeyGenerated as string const tenderlyAccountSlug = ''; const tenderlyProjectSlug = ''; diff --git a/spend-permission/spend-permission.ts b/spend-permission/spend-permission.ts index de54776..47bf0f6 100644 --- a/spend-permission/spend-permission.ts +++ b/spend-permission/spend-permission.ts @@ -5,15 +5,10 @@ import { CandidePaymaster, ZeroAddress } from "abstractionkit"; -import { - Wallet, - Contract, - JsonRpcProvider, -} from "ethers"; +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; +import { createPublicClient, http, parseAbi } from "viem"; -const ERC20_ABI = [ - "function balanceOf(address owner) view returns (uint256)", -]; +const ERC20_ABI = parseAbi(["function balanceOf(address owner) view returns (uint256)"]); async function main(): Promise { //get values from .env @@ -30,18 +25,22 @@ async function main(): Promise { const sourceOwnerPrivateKey = process.env.PRIVATE_KEY as string; // delegate account owner - const delegateOwner = Wallet.createRandom(); + const delegateOwnerPrivateKey = generatePrivateKey(); + const delegateOwner = privateKeyToAccount(delegateOwnerPrivateKey); const delegateOwnerPublicAddress = delegateOwner.address; - const delegateOwnerPrivateKey = delegateOwner.privateKey; // source safe account const sourceSafeAccount = SafeAccount.initializeNewAccount( [sourceOwnerPublicAddress], { c2Nonce: 0n } ); - const provider = new JsonRpcProvider(nodeUrl); - const tokenContract = new Contract(allowanceToken, ERC20_ABI, provider); - const sourceSafeAccountBalance = await tokenContract.balanceOf(sourceSafeAccount.accountAddress); + const client = createPublicClient({ transport: http(nodeUrl) }); + const sourceSafeAccountBalance = await client.readContract({ + address: allowanceToken as `0x${string}`, + abi: ERC20_ABI, + functionName: 'balanceOf', + args: [sourceSafeAccount.accountAddress as `0x${string}`], + }); if (sourceSafeAccountBalance <= 2n) { console.log("Please fund the Safe Account with some tokens first"); diff --git a/tsconfig.json b/tsconfig.json index 5eb2d4d..5baea98 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "module": "commonjs" /* Specify what module code is generated. */, "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, "declaration": true,