Creating Wallets
Learn how to create Account Abstraction wallets using the MSA API with various custody types and configurations.
Creating wallets with the MSA API is straightforward. This guide covers everything you need to know about deploying smart contract wallets with different custody configurations.
Overview
The MSA API allows you to create Account Abstraction wallets on any EVM-compatible network. Wallets are created using a deterministic address generation algorithm (CREATE3), ensuring you can predict the wallet address before deployment.
Basic Wallet Creation
Single Wallet Creation
// Using API HTTP (fetch)
const response = await fetch('https://api.msa.omnes.tech/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
accounts: [{
walletCustody: 1, // ECDSA_VALIDATOR
salt: 'user@example.com'
}],
settings: {
rpc: 'https://rpc-amoy.polygon.technology/',
factory: '0xb09Fd1134553a43A3E02182a6B04F4dEBa7476F4',
validator: '0x9bD18Da66990F80d598dE02d5143dC9A4422eC3a',
entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
beaconAdminAddress: '0x99Ee7725D6a8f691d8B375e0aD33d1Aff2236618',
signer: {
clientId: process.env.MSA_CLIENT_ID!,
versionId: process.env.MSA_VERSION_ID!
}
}
})
});
const result = await response.json();
console.log('Wallet address:', result.returnData[0]['0'].wallet);
console.log('Transaction hash:', result.txHash);
console.log('Status:', result.status === 1 ? 'Success' : 'Failed');import requests
def create_wallet():
url = "https://api.msa.omnes.tech/create"
payload = {
"accounts": [{
"walletCustody": 1, # ECDSA_VALIDATOR
"salt": "user@example.com"
}],
"settings": {
"rpc": "https://rpc-amoy.polygon.technology/",
"factory": "0xb09Fd1134553a43A3E02182a6B04F4dEBa7476F4",
"validator": "0x9bD18Da66990F80d598dE02d5143dC9A4422eC3a",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"beaconAdminAddress": "0x99Ee7725D6a8f691d8B375e0aD33d1Aff2236618",
"beaconAdminCreationCode": "0x60a060405260405161059d...", # Full bytecode
"signer": {
"clientId": "your-client-id",
"versionId": "1"
}
}
}
response = requests.post(url, json=payload)
result = response.json()
print(f'Wallet address: {result["returnData"][0]["0"]["wallet"]}')
print(f'Transaction hash: {result["txHash"]}')
return result
create_wallet()curl -X POST "https://api.msa.omnes.tech/create" \
-H "Content-Type: application/json" \
-d '{
"accounts": [{
"walletCustody": 1,
"salt": "user@example.com"
}],
"settings": {
"rpc": "https://rpc-amoy.polygon.technology/",
"factory": "0xb09Fd1134553a43A3E02182a6B04F4dEBa7476F4",
"validator": "0x9bD18Da66990F80d598dE02d5143dC9A4422eC3a",
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"beaconAdminAddress": "0x99Ee7725D6a8f691d8B375e0aD33d1Aff2236618",
"beaconAdminCreationCode": "0x60a060405260405161059d...",
"signer": {
"clientId": "your-client-id",
"versionId": "1"
}
}
}'Expected Response:
{
"status": 1,
"txHash": "0x066a0269a55aa42498da9993f01b9bfc82517330669c240807ed1b94b2acd938",
"gasUsed": 549731,
"effectiveGasPrice": "1136849215",
"returnData": [{
"0": {
"salt": "user@example.com",
"wallet": "0x28097eF2268B553783D9c32A33ECb1bB78B209F3"
}
}]
}Wallet Creation with Different Custody Types
ECDSA Validator (Type 1)
Standard wallet with ECDSA signature validation:
const ecdsaWallet = await client.createWallet({
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'ecdsa-user@example.com'
});Passkey Validator (Type 3)
Pure passkey-based wallet requiring a public key:
const passkeyWallet = await client.createWallet({
walletCustody: CustodyType.PASSKEY_VALIDATOR,
salt: 'passkey-user@example.com',
passkeyPubKey: ['base64-url-encoded-public-key'] // Keep in DER format!
});Important: The passkey public key must be:
- Base64URL encoded
- In DER format (do NOT convert)
- Generated with P-256 curve (ES256)
- With UP and UV flags enabled
ECDSA + Passkey Validator (Type 2)
Hybrid wallet supporting both ECDSA and passkey:
const hybridWallet = await client.createWallet({
walletCustody: CustodyType.ECDSA_PASSKEY_VALIDATOR,
salt: 'hybrid-user@example.com',
passkeyPubKey: ['base64-url-encoded-public-key']
});Multisig Validator (Type 4)
Multi-signature wallet requiring multiple signers:
const multisigWallet = await client.createWallet({
walletCustody: CustodyType.MULTISIG_VALIDATOR,
salt: 'multisig-org@example.com',
signers: [
'0x742d35Cc6676C461fAb7E06dcA6e1A1BB2b7d0Fd',
'0x8ba1f109551bD432803012645Hac136c0bf1E1A5',
'0x9D86b1BEE4F24C5F5e4B15c2C3d2F0f6Ff2F5f5E'
],
threshold: '2' // Require 2 of 3 signatures
});Multisig + Passkey Validator (Type 5)
Enterprise-grade wallet combining multisig with passkey:
const enterpriseWallet = await client.createWallet({
walletCustody: CustodyType.MULTISIG_PASSKEY_VALIDATOR,
salt: 'enterprise-org@example.com',
signers: ['0x742d35Cc6676C461fAb7E06dcA6e1A1BB2b7d0Fd'],
passkeyPubKey: ['base64-url-encoded-public-key'],
threshold: '2' // Require 2 total (1 signer + 1 passkey)
});Batch Wallet Creation
Create multiple wallets in a single transaction:
const batchResult = await client.createWallets([
{
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'user1@example.com'
},
{
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'user2@example.com'
},
{
walletCustody: CustodyType.PASSKEY_VALIDATOR,
salt: 'user3@example.com',
passkeyPubKey: ['base64-key-here']
}
]);
console.log(`Created ${batchResult.returnData.length} wallets`);Pre-requisites
Before creating a wallet, ensure:
- Sufficient Gas: The predicted wallet address must have enough native tokens for gas
- Fireblocks Credentials: Valid
clientIdandversionId(HSM) orassetId(MPC) - Network Access: Correct RPC endpoint for your target blockchain
- Contract Addresses: Factory and validator addresses configured
Funding the Wallet
Before creation, fund the predicted wallet address:
// 1. Predict the wallet address
const prediction = await client.predictWallet({
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'user@example.com'
});
console.log('Predicted address:', prediction.wallet);
// 2. Fund the wallet (send native tokens)
// Use your preferred method:
// - Transfer from another wallet
// - Use a faucet (testnet)
// - Fund via payment gateway
// 3. Create the wallet
const result = await client.createWallet({
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'user@example.com'
});Wallet Salt
The salt parameter is critical:
- Deterministic: Same salt always generates the same address
- Unique: Use different salts for different wallets
- Format: Can be any string (email, UUID, user ID, etc.)
- Examples:
user@example.comuser-123450xabc123def456unique-identifier-v1
Important: Once a wallet is created with a salt, you cannot reuse that salt to create a new wallet.
Response Status Codes
The status field in the response indicates the transaction result:
| Status | Code | Description |
|---|---|---|
| Success | 1 | Wallet created successfully |
| Failed | 0 | Transaction failed |
| Already Exists | 2 | Wallet already deployed |
| Partially Created | 3 | Some wallets in batch created |
Error Handling
try {
const result = await client.createWallet({
walletCustody: CustodyType.ECDSA_VALIDATOR,
salt: 'user@example.com'
});
if (result.status === 2) {
console.warn('Wallet already exists');
} else if (result.status === 1) {
console.log('Wallet created successfully');
}
} catch (error) {
if (error.message.includes('insufficient funds')) {
console.error('Fund the predicted wallet address first');
} else if (error.message.includes('Invalid client ID')) {
console.error('Check your Fireblocks credentials');
} else {
console.error('Error:', error.message);
}
}Using the SmartWallet SDK
If you're using the TypeScript SmartWallet SDK, wallet creation is handled automatically:
import { SmartWallet, PKSigner } from '@omnes/smartwallet-ts-sdk';
// Create signer
const signer = await PKSigner.create(privateKey as `0x${string}`);
// Create SmartWallet instance
const smartWallet = await SmartWallet.create(
signer.signMessage,
signer.signMessage,
signer.getEVMAddress(),
rpcURL,
apiKey
);
// Build operations
const accountOperations = [{
account: {
walletCustody: 1, // ECDSA_VALIDATOR
salt: 'user@example.com',
publicKeys: []
},
operations: [], // No operations needed for wallet creation
settings: {}
}];
// Build and send
const result = await smartWallet.buildAndRequestSendUserOperations(
accountOperations,
[],
[]
);
console.log('Transaction hash:', result.response.txHash);Best Practices
- Always Predict First: Predict wallet addresses before funding
- Use Meaningful Salts: Choose salts that are unique and meaningful
- Handle Existing Wallets: Check if wallet exists before creation
- Monitor Gas Costs: Track gas usage for optimization
- Test on Testnet: Always test wallet creation on testnet first
Next Steps
- Predicting Addresses - Learn how to predict wallet addresses
- Checking Wallets - Verify wallet existence
- Custody Types - Understand different wallet configurations
- Transaction Execution - Start using your wallets
✅ Wallet Created! Your wallet is now ready to use. You can start executing transactions and managing your Account Abstraction wallet.