diff --git a/.changeset/ninety-taxis-destroy.md b/.changeset/ninety-taxis-destroy.md new file mode 100644 index 0000000000..b7b29396bb --- /dev/null +++ b/.changeset/ninety-taxis-destroy.md @@ -0,0 +1,5 @@ +--- +"viem": patch +--- + +Added ZKsync `toSmartAccount`. diff --git a/site/pages/account-abstraction/accounts/smart/toSmartAccount.md b/site/pages/account-abstraction/accounts/smart/toSmartAccount.md index 03ccda1ab0..d1cdab9fee 100644 --- a/site/pages/account-abstraction/accounts/smart/toSmartAccount.md +++ b/site/pages/account-abstraction/accounts/smart/toSmartAccount.md @@ -2,7 +2,7 @@ description: Creates a Smart Account with a provided Account Implementation. --- -# Custom +# toSmartAccount The `toSmartAccount` function allows you to create a Smart Account with a custom Account Implementation. diff --git a/site/pages/zksync/accounts/toMultisigSmartAccount.md b/site/pages/zksync/accounts/toMultisigSmartAccount.md new file mode 100644 index 0000000000..93e92ea678 --- /dev/null +++ b/site/pages/zksync/accounts/toMultisigSmartAccount.md @@ -0,0 +1,46 @@ +--- +description: Creates a multi-signature ZKsync Smart Account +--- + +# toMultisigSmartAccount (ZKsync) + +Creates a multi-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner. + +## Usage + +```ts twoslash +import { toMultisigSmartAccount } from 'viem/zksync' + +const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', + privateKeys: ['0x...', '0x...'] +}) +``` + +## Parameters + +### address + +- **Type:** `Hex` + +Address of the deployed Account's Contract implementation. + +```ts +const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] + privateKeys: ['0x...', '0x...'] +}) +``` + +### privateKeys + +- **Type:** `Hex[]` + +Private Keys of the owners. + +```ts +const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: ['0x...', '0x...'] // [!code focus] +}) +``` \ No newline at end of file diff --git a/site/pages/zksync/accounts/toSinglesigSmartAccount.md b/site/pages/zksync/accounts/toSinglesigSmartAccount.md new file mode 100644 index 0000000000..ff94b3e80b --- /dev/null +++ b/site/pages/zksync/accounts/toSinglesigSmartAccount.md @@ -0,0 +1,46 @@ +--- +description: Creates a single-signature ZKsync Smart Account +--- + +# toSinglesigSmartAccount (ZKsync) + +Creates a single-signature [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and the Private Key of the owner. + +## Usage + +```ts twoslash +import { toSinglesigSmartAccount } from 'viem/zksync' + +const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', + privateKey: '0x...' +}) +``` + +## Parameters + +### address + +- **Type:** `Hex` + +Address of the deployed Account's Contract implementation. + +```ts +const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] + privateKey: '0x...' +}) +``` + +### privateKey + +- **Type:** `Hex` + +Private Key of the owner. + +```ts +const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: '0x...' // [!code focus] +}) +``` \ No newline at end of file diff --git a/site/pages/zksync/accounts/toSmartAccount.md b/site/pages/zksync/accounts/toSmartAccount.md new file mode 100644 index 0000000000..dc9513920b --- /dev/null +++ b/site/pages/zksync/accounts/toSmartAccount.md @@ -0,0 +1,53 @@ +--- +description: Creates a ZKsync Smart Account +--- + +# toSmartAccount (ZKsync) + +Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) from a Contract Address and a custom sign function. + +## Usage + +```ts twoslash +import { toSmartAccount } from 'viem/zksync' + +const account = toSmartAccount({ + address: '0xf39Fd6e51aad8F6F4ce6aB8827279cffFb92266', + async sign({ hash }) { + // ... signing logic + return '0x...' + } +}) +``` + +## Parameters + +### address + +- **Type:** `Hex` + +Address of the deployed Account's Contract implementation. + +```ts +const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // [!code focus] + async sign({ hash }) { + // ... + } +}) +``` + +### sign + +- **Type:** `({ hash: Hex }) => Hex` + +Custom sign function for the Smart Account. + +```ts +const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + async sign({ hash }) { // [!code focus] + // ... // [!code focus] + } // [!code focus] +}) +``` \ No newline at end of file diff --git a/site/sidebar.ts b/site/sidebar.ts index 6907a65a78..1e9cfcddc1 100644 --- a/site/sidebar.ts +++ b/site/sidebar.ts @@ -1593,6 +1593,23 @@ export const sidebar = { { text: 'Chains', link: '/zksync/chains' }, ], }, + { + text: 'Smart Accounts', + items: [ + { + text: 'Singlesig', + link: '/zksync/accounts/toSinglesigSmartAccount', + }, + { + text: 'Multisig', + link: '/zksync/accounts/toMultisigSmartAccount', + }, + { + text: 'Custom', + link: '/zksync/accounts/toSmartAccount', + }, + ], + }, { text: 'EIP-712 Actions', items: [ diff --git a/site/vocs.config.tsx b/site/vocs.config.tsx index 89236486dd..485a7483f5 100644 --- a/site/vocs.config.tsx +++ b/site/vocs.config.tsx @@ -45,7 +45,9 @@ export default defineConfig({ rootDir: '.', search: { boostDocument(documentId) { - if (documentId.startsWith('pages/docs')) return 2 + if (documentId.startsWith('pages/docs')) return 3 + if (documentId.startsWith('pages/account-abstraction')) return 2 + if (documentId.startsWith('pages/experimental')) return 2 return 1 }, }, diff --git a/src/accounts/privateKeyToAccount.ts b/src/accounts/privateKeyToAccount.ts index 70fa49066f..77182df937 100644 --- a/src/accounts/privateKeyToAccount.ts +++ b/src/accounts/privateKeyToAccount.ts @@ -66,7 +66,7 @@ export function privateKeyToAccount( return signTransaction({ privateKey, transaction, serializer }) }, async signTypedData(typedData) { - return signTypedData({ ...typedData, privateKey }) + return signTypedData({ ...typedData, privateKey } as any) }, }) diff --git a/src/zksync/accounts/toMultisigSmartAccount.test.ts b/src/zksync/accounts/toMultisigSmartAccount.test.ts new file mode 100644 index 0000000000..873f6595d5 --- /dev/null +++ b/src/zksync/accounts/toMultisigSmartAccount.test.ts @@ -0,0 +1,83 @@ +import { expect, test } from 'vitest' + +import { accounts, typedData } from '~test/src/constants.js' + +import { parseEther, parseGwei } from '../../utils/index.js' +import { toMultisigSmartAccount } from './toMultisigSmartAccount.js' + +test('default', () => { + expect( + toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: [accounts[0].privateKey, accounts[1].privateKey], + }), + ).toMatchInlineSnapshot(` + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "experimental_signAuthorization": undefined, + "nonceManager": undefined, + "sign": [Function], + "signMessage": [Function], + "signTransaction": [Function], + "signTypedData": [Function], + "source": "smartAccountZksync", + "type": "local", + } + `) +}) + +test('sign', async () => { + const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: [accounts[0].privateKey, accounts[1].privateKey], + }) + expect( + await account.sign({ + hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68', + }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`, + ) +}) + +test('sign message', async () => { + const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: [accounts[0].privateKey, accounts[1].privateKey], + }) + expect( + await account.signMessage({ message: 'hello world' }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`, + ) +}) + +test('sign transaction', async () => { + const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: [accounts[0].privateKey, accounts[1].privateKey], + }) + expect( + await account.signTransaction({ + chainId: 1, + maxFeePerGas: parseGwei('20'), + gas: 21000n, + to: accounts[1].address, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot( + `"0x71f8cc80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b882f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311c4e5f2cafb92b15ff828d4f8fb34cd3058355428586539495fd0075919cb3cd0d0d33b1e617948f2938f0be2895e4eb4a58a4bcd1a57874ed2c2d235424cf03271bc0"`, + ) +}) + +test('sign typed data', async () => { + const account = toMultisigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKeys: [accounts[0].privateKey, accounts[1].privateKey], + }) + expect( + await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }), + ).toMatchInlineSnapshot( + `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b7f65cd2428f35d93b843da90d299ccabe834c0068b0d4035112ad1345abf962b579854cad5911d725fc1000e6e4ebfb4c41e233f66153857b5c017cb3115879a1c"`, + ) +}) diff --git a/src/zksync/accounts/toMultisigSmartAccount.ts b/src/zksync/accounts/toMultisigSmartAccount.ts new file mode 100644 index 0000000000..4a1d329497 --- /dev/null +++ b/src/zksync/accounts/toMultisigSmartAccount.ts @@ -0,0 +1,37 @@ +import type { Address } from 'abitype' + +import { sign } from '../../accounts/utils/sign.js' +import type { Hex } from '../../types/misc.js' +import { concatHex } from '../../utils/index.js' +import type { ZksyncSmartAccount } from '../types/account.js' +import { toSmartAccount } from './toSmartAccount.js' + +export type ToMultisigSmartAccountParameters = { + /** Address of the deployed Account's Contract implementation. */ + address: Address + /** Array of Private Keys belonging to the owners. */ + privateKeys: readonly Hex[] +} + +/** + * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) + * from a Contract Address and an array of Private Keys belonging to the owners. + */ +export function toMultisigSmartAccount( + parameters: ToMultisigSmartAccountParameters, +): ZksyncSmartAccount { + const { address, privateKeys } = parameters + + return toSmartAccount({ + address, + async sign({ hash }) { + return concatHex( + await Promise.all( + privateKeys.map((privateKey) => + sign({ hash, privateKey, to: 'hex' }), + ), + ), + ) + }, + }) +} diff --git a/src/zksync/accounts/toSinglesigSmartAccount.test.ts b/src/zksync/accounts/toSinglesigSmartAccount.test.ts new file mode 100644 index 0000000000..ff020eb48a --- /dev/null +++ b/src/zksync/accounts/toSinglesigSmartAccount.test.ts @@ -0,0 +1,83 @@ +import { expect, test } from 'vitest' + +import { accounts, typedData } from '~test/src/constants.js' + +import { parseEther, parseGwei } from '../../utils/index.js' +import { toSinglesigSmartAccount } from './toSinglesigSmartAccount.js' + +test('default', () => { + expect( + toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: accounts[0].privateKey, + }), + ).toMatchInlineSnapshot(` + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "experimental_signAuthorization": undefined, + "nonceManager": undefined, + "sign": [Function], + "signMessage": [Function], + "signTransaction": [Function], + "signTypedData": [Function], + "source": "smartAccountZksync", + "type": "local", + } + `) +}) + +test('sign', async () => { + const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: accounts[0].privateKey, + }) + expect( + await account.sign({ + hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68', + }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`, + ) +}) + +test('sign message', async () => { + const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: accounts[0].privateKey, + }) + expect( + await account.signMessage({ message: 'hello world' }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b"`, + ) +}) + +test('sign transaction', async () => { + const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: accounts[0].privateKey, + }) + expect( + await account.signTransaction({ + chainId: 1, + maxFeePerGas: parseGwei('20'), + gas: 21000n, + to: accounts[1].address, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot( + `"0x71f88b80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b841f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311cc0"`, + ) +}) + +test('sign typed data', async () => { + const account = toSinglesigSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + privateKey: accounts[0].privateKey, + }) + expect( + await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }), + ).toMatchInlineSnapshot( + `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b"`, + ) +}) diff --git a/src/zksync/accounts/toSinglesigSmartAccount.ts b/src/zksync/accounts/toSinglesigSmartAccount.ts new file mode 100644 index 0000000000..9c053727ee --- /dev/null +++ b/src/zksync/accounts/toSinglesigSmartAccount.ts @@ -0,0 +1,30 @@ +import type { Address } from 'abitype' + +import { sign } from '../../accounts/utils/sign.js' +import type { Hex } from '../../types/misc.js' +import type { ZksyncSmartAccount } from '../types/account.js' +import { toSmartAccount } from './toSmartAccount.js' + +export type ToSinglesigSmartAccountParameters = { + /** Address of the deployed Account's Contract implementation. */ + address: Address + /** Private Key of the owner. */ + privateKey: Hex +} + +/** + * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) + * from a Contract Address and a Private Key belonging to the owner. + */ +export function toSinglesigSmartAccount( + parameters: ToSinglesigSmartAccountParameters, +): ZksyncSmartAccount { + const { address, privateKey } = parameters + + return toSmartAccount({ + address, + async sign({ hash }) { + return sign({ hash, privateKey, to: 'hex' }) + }, + }) +} diff --git a/src/zksync/accounts/toSmartAccount.test.ts b/src/zksync/accounts/toSmartAccount.test.ts new file mode 100644 index 0000000000..23cc09bc3d --- /dev/null +++ b/src/zksync/accounts/toSmartAccount.test.ts @@ -0,0 +1,94 @@ +import { expect, test } from 'vitest' + +import { accounts, typedData } from '~test/src/constants.js' + +import { sign as sign_ } from '../../accounts/index.js' +import type { Hex } from '../../types/misc.js' +import { concatHex, parseEther, parseGwei } from '../../utils/index.js' +import { toSmartAccount } from './toSmartAccount.js' + +async function sign({ hash }: { hash: Hex }) { + const privateKeys = [accounts[0].privateKey, accounts[1].privateKey] + return concatHex( + await Promise.all( + privateKeys.map((privateKey) => sign_({ hash, privateKey, to: 'hex' })), + ), + ) +} + +test('default', () => { + expect( + toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + sign, + }), + ).toMatchInlineSnapshot(` + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "experimental_signAuthorization": undefined, + "nonceManager": undefined, + "sign": [Function], + "signMessage": [Function], + "signTransaction": [Function], + "signTypedData": [Function], + "source": "smartAccountZksync", + "type": "local", + } + `) +}) + +test('sign', async () => { + const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + sign, + }) + expect( + await account.sign({ + hash: '0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68', + }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`, + ) +}) + +test('sign message', async () => { + const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + sign, + }) + expect( + await account.signMessage({ message: 'hello world' }), + ).toMatchInlineSnapshot( + `"0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b02de21de49d98cf93ef790a262702dcc711b3f2ce0a971e3b50caea43cfc07cb34eabfd4d39eff886015fb2c42ec4265aeaf5ee34a78c75309fdc1165cb659521c"`, + ) +}) + +test('sign transaction', async () => { + const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + sign, + }) + expect( + await account.signTransaction({ + chainId: 1, + maxFeePerGas: parseGwei('20'), + gas: 21000n, + to: accounts[1].address, + value: parseEther('1'), + }), + ).toMatchInlineSnapshot( + `"0x71f8cc80808504a817c8008252089470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000000180800194f39fd6e51aad88f6f4ce6ab8827279cfffb9226682c350c0b882f40a2d2ae9638056cafbe9083c7125edc8555e0e715db0984dd859a5c6dfac5720f36fd0b32bef4d6d75c62f220e59c5fb60c244ca3b361e750985ee5c3a09311c4e5f2cafb92b15ff828d4f8fb34cd3058355428586539495fd0075919cb3cd0d0d33b1e617948f2938f0be2895e4eb4a58a4bcd1a57874ed2c2d235424cf03271bc0"`, + ) +}) + +test('sign typed data', async () => { + const account = toSmartAccount({ + address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + sign, + }) + expect( + await account.signTypedData({ ...typedData.basic, primaryType: 'Mail' }), + ).toMatchInlineSnapshot( + `"0x32f3d5975ba38d6c2fba9b95d5cbed1febaa68003d3d588d51f2de522ad54117760cfc249470a75232552e43991f53953a3d74edf6944553c6bef2469bb9e5921b7f65cd2428f35d93b843da90d299ccabe834c0068b0d4035112ad1345abf962b579854cad5911d725fc1000e6e4ebfb4c41e233f66153857b5c017cb3115879a1c"`, + ) +}) diff --git a/src/zksync/accounts/toSmartAccount.ts b/src/zksync/accounts/toSmartAccount.ts new file mode 100644 index 0000000000..092e42c0f3 --- /dev/null +++ b/src/zksync/accounts/toSmartAccount.ts @@ -0,0 +1,63 @@ +import type { Address } from 'abitype' + +import { toAccount } from '../../accounts/toAccount.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Hash, Hex } from '../../types/misc.js' +import { keccak256 } from '../../utils/index.js' +import { hashMessage } from '../../utils/signature/hashMessage.js' +import { hashTypedData } from '../../utils/signature/hashTypedData.js' +import { serializeTransaction } from '../serializers.js' +import type { ZksyncSmartAccount } from '../types/account.js' +import type { ZksyncTransactionSerializableEIP712 } from '../types/transaction.js' + +export type ToSmartAccountParameters = { + /** Address of the deployed Account's Contract implementation. */ + address: Address + /** Function to sign a hash. */ + sign: (parameters: { hash: Hash }) => Promise +} + +export type ToSmartAccountErrorType = ErrorType + +/** + * Creates a [ZKsync Smart Account](https://docs.zksync.io/build/developer-reference/account-abstraction/building-smart-accounts) + * from a Contract Address and a custom sign function. + */ +export function toSmartAccount( + parameters: ToSmartAccountParameters, +): ZksyncSmartAccount { + const { address, sign } = parameters + + const account = toAccount({ + address, + sign, + async signMessage({ message }) { + return sign({ + hash: hashMessage(message), + }) + }, + async signTransaction(transaction) { + const signableTransaction = { + ...transaction, + from: this.address!, + } as ZksyncTransactionSerializableEIP712 + + return serializeTransaction({ + ...signableTransaction, + customSignature: await sign({ + hash: keccak256(serializeTransaction(signableTransaction)), + }), + }) + }, + async signTypedData(typedData) { + return sign({ + hash: hashTypedData(typedData), + }) + }, + }) + + return { + ...account, + source: 'smartAccountZksync', + } as ZksyncSmartAccount +} diff --git a/src/zksync/index.ts b/src/zksync/index.ts index 1130eb820b..08d9df95cd 100644 --- a/src/zksync/index.ts +++ b/src/zksync/index.ts @@ -1,4 +1,18 @@ // biome-ignore lint/performance/noBarrelFile: entrypoint module +export { + type ToSmartAccountErrorType, + type ToSmartAccountParameters, + toSmartAccount, +} from './accounts/toSmartAccount.js' +export { + type ToMultisigSmartAccountParameters, + toMultisigSmartAccount, +} from './accounts/toMultisigSmartAccount.js' +export { + type ToSinglesigSmartAccountParameters, + toSinglesigSmartAccount, +} from './accounts/toSinglesigSmartAccount.js' + export { type DeployContractErrorType, type DeployContractParameters, @@ -131,6 +145,7 @@ export { export { serializeTransaction } from './serializers.js' +export type { ZksyncSmartAccount } from './types/account.js' export type { /** @deprecated Use `ZksyncBlock` instead */ ZksyncBlock as ZkSyncBlock, diff --git a/src/zksync/types/account.ts b/src/zksync/types/account.ts new file mode 100644 index 0000000000..2c880ffa78 --- /dev/null +++ b/src/zksync/types/account.ts @@ -0,0 +1,5 @@ +import type { CustomSource, LocalAccount } from '../../accounts/types.js' + +export type ZksyncSmartAccount = LocalAccount<'smartAccountZksync'> & { + sign: NonNullable +}