Skip to main content

Authentication

Smart Accounts, much like regular accounts (EOAs), depend on signatures for validating transactions and messages. What sets Smart Accounts apart is their ability to utilize various signature schemes, such as Passkeys and Multisig.

Passkeys

Candide supports on-chain Passkeys, allowing users to sign transactions directly from their devices. Using their default phone/computer auth system, such as PIN, biometrics, or FaceID, signatures are authenticated on-chain through smart contracts. All without reliance on centralized infrastructure.

See a demo on passkeys.candide.dev, and learn more on Passkeys here.

Multisig

Candide supports on-chain multisig (multi-signature) accounts, which enhance security by requiring multiple approvals for transactions. This feature is ideal for implementing two-factor authentication (2FA) and is well-suited for wallets targeting companies or DAOs, where transactions often require multiple key approvals.

Visit dedicated guide on MultiSig

Traditional Signers

Externally Owner Account (EOAs) utilize both a private and public key for security. Similarly, Smart Accounts can employ the same approach for securing funds. This can be achieved through various means such as a locally generated private key, utilizing a hosted MPC solution, or securing it with existing user wallets like MetaMask.

Social / Email

Developers can integrate third-party "Signer services" into their smart accounts, providing the benefits of traditional Web2 onboarding experiences. These services enable user authentication through familiar methods such as email magic links, social logins, or SMS.

  • Candide's AbstractionKit is highly adaptable, supporting any third-party Signer service. The process for assigning the smart account owner and signing a User Operation is consistent across all services. For details on how to integrate as third-parties as main signer, refer to the guide for EOA wallets.

  • However, it is important to note that using third-party services as the primary signer of the account makes the wallet you are offering custodial. Candide recommends using Passkeys as the main signer, while offering third-parties as recovery options via the on-chain Guardian Recovery. This approach ensures progressive self-custody, allowing users to modify recovery options as needed.

Below are some third-party recovery options that you can consider. Candide does not endorse any of these options; the guides are provided for educational purposes only.

ServiceKey Management MethodAuthentication MethodsPlug-n-play Front End?Guide
DynamicMPC (Turnkey under the hood)Email, Social, WalletsYes
FireblocksMPCCustomNo
Lit ProtocolMPCEmail, Social, PhoneNo
Magic.linkAWS KMSEmail, SocialYesUse Magic with AbstractionKit
PrivyMPCEmail, Social, WalletsYes
TurnkeyAWS KMSCustomNoUse Turnkey with AbstractionKit
Web3AuthMPC, key shardingSocial, EmailYeslink

EOA Wallets

You can allow users to use MetaMask, or any other EOA wallet as the signer to the Smart Account. EOAs exposes a JavaScript Ethereum Provider API.

Setup Account Owner

import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";
import { BrowserProvider } from "ethers";

const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const signerAddress = await signer.getAddress();

const smartAccount = SafeAccount.initializeNewAccount([signerAddress]);

Sign UserOperation

Format the signature to a userOperation signature using formatEip712SignaturesToUseroperationSignature.

let userOperation = ... // Use createUserOperation() to help you construct the userOp below

const domain = {
chainId: process.env.CHAIN_ID,
verifyingContract: smartAccount.safe4337ModuleAddress,
};

const types = SafeAccount.EIP712_SAFE_OPERATION_TYPE;

// formate according to EIP712 Safe Operation Type
const { sender, ...userOp } = userOperation;
const safeUserOperation = {
...userOp,
safe: userOperation.sender,
validUntil: BigInt(0),
validAfter: BigInt(0),
entryPoint: smartAccount.entrypointAddress,
};

const signature = await signer.signTypedData(domain, types, safeUserOperation);
const formatedSig = SafeAccount.formatEip712SignaturesToUseroperationSignature([signer.address], [signature]);
userOperation.signature = formatedSig;

Private key Storage

Traditional wallets encrypts the private key in local storage. In this setup, the owner of the smart account is represented by it. Use signUserOperation.

Signup Account Owner

import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";

const ownerPublicAddress = process.env.PUBLIC_KEY;

const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]);

Sign UserOperation

const chainId = BigInt(process.env.CHAIN_ID as string);
const ownerPrivateKey = process.env.PRIVATE_KEY as string;

const userOperation = ... // returned from createUserOperation()

userOperation.signature = smartAccount.signUserOperation(
userOperation,
[privateKey],
chainId,
)