Accounts
Account Modules
Developers can enhance the functionality of ZKsync SSO accounts by implementing compatible modules via smart contracts. The module interface is based on ERC-7579 to allow maximum flexibility while using ZKsync's powerful native account abstraction to developer overhead.
The existing passkey and session features are already implemented as modules, and installed by default for every new account deployment. Modules can be added or removed from the account, without having to migrate or upgrade the whole account.
You can contribute to the smart account modules available to ZKsync SSO users by
submitting a PR to the zksync-sso-clave-contracts
repository.
Modules must implement the
IModuleValidator
interface.
The current implemented modules can be found in the validators
folder.
Looking for inspiration? Take a look at the modules already created for ERC-7579
Deploying an Account From a Registered Passkey
You can deploy a modular account from a registered passkey
using the deployModularAccount
method:
import { createWalletClient, http, type Address } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { zksyncSepoliaTestnet } from 'viem/zksync';
import { deployModularAccount } from 'zksync-sso/client';
import type { RegisterNewPasskeyReturnType } from 'zksync-sso/client/passkey';
export async function deployAccountFromPasskey(publicPassKey: RegisterNewPasskeyReturnType) {
const deployerKey = '0x_YOUR_PRIVATE_KEY'; // Replace with your deployer's private key
const deployerClient = createWalletClient({
account: privateKeyToAccount(deployerKey),
chain: zksyncSepoliaTestnet,
transport: http(),
});
const ssoContracts = {
accountFactory: '0xd122999B15081d90b175C81B8a4a9bE3327C0c2a' as Address,
passkey: '0x006ecc2D79242F1986b7cb5F636d6E3f499f1026' as Address,
session: '0x64Fa4b6fCF655024e6d540E0dFcA4142107D4fBC' as Address,
recovery: '0x6AA83E35439D71F28273Df396BC7768dbaA9849D' as Address,
};
const { address, transactionReceipt } = await deployModularAccount(deployerClient, {
passkeyModule: {
location: ssoContracts.passkey,
credentialPublicKey: publicPassKey.credentialPublicKey,
credentialId: publicPassKey.credentialId,
},
accountFactory: ssoContracts.accountFactory,
installNoDataModules: [],
// defines the deployer account as an account owner
owners: [deployerClient.account.address],
});
console.log('Account deployed at:', address);
console.log('Transaction receipt:', transactionReceipt);
}
ECDSA Signer
You can authorize transactions for an account using an ECDSA signer. The example below assumes an SSO account has already been deployed as shown in the example above. The private key here must match one of the owner accounts defined when the account was deployed.
The ecdsaClient
here can be used like a viem
wallet client to send transactions.
import type { Address } from 'viem';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { zksyncSepoliaTestnet } from 'viem/chains';
import { createZksyncEcdsaClient } from 'zksync-sso/client/ecdsa';
const privateKey = '0x_YOUR_PRIVATE_KEY'; // Replace with an owner account's private key
const ssoContracts = {
accountFactory: '0xd122999B15081d90b175C81B8a4a9bE3327C0c2a' as Address,
passkey: '0x006ecc2D79242F1986b7cb5F636d6E3f499f1026' as Address,
session: '0x64Fa4b6fCF655024e6d540E0dFcA4142107D4fBC' as Address,
recovery: '0x6AA83E35439D71F28273Df396BC7768dbaA9849D' as Address,
};
export async function getECDSAClient(accountAddress: Address) {
const eoaClient = createWalletClient({
account: privateKeyToAccount(privateKey as Address),
chain: zksyncSepoliaTestnet,
transport: http(),
});
const ecdsaClient = await createZksyncEcdsaClient({
address: accountAddress,
owner: eoaClient.account,
contracts: ssoContracts,
chain: zksyncSepoliaTestnet,
transport: http(),
});
console.log(`ECDSA client: ${ecdsaClient}`);
return ecdsaClient;
}
Deploying Accounts in React Native
There are two methods available to create a new account and passkey pair: registerAccount
and registerAccountWithUniqueId
.
registerAccount
creates a random wallet address.registerAccountWithUniqueId
creates a deterministic wallet address based on the deploy wallet private key and the user ID.
Both of these methods accept the same input argument types and return the same Account
type:
interface RPInfo {
name: string;
id: RpId;
}
interface AccountInfo {
name: string;
userID: string;
rp: RPInfo;
}
const registerAccountWithUniqueId = async (
accountInfo: AccountInfo,
challenge: string,
config: Config
): Promise<Account>
See the Getting Started page for an example for how to use these methods.
For web3 native applications, generally registerAccount
is recommended.
In this case, only the account address must be stored somewhere.
The account address cannot be derived in any way.
For web2 native applications that already have a username or email used to login,
registerAccountWithUniqueId
can be used to create an address based on that account info.
In this case, the address can be derived from the userID using the getAccountByUserId
method.
import { type Account, getAccountByUserId } from "react-native-zksync-sso";
const account: Account = await getAccountByUserId(uniqueAccountId, config);