Encoded Transaction Execution
Execute pre-encoded UserOperations with the MSA API for advanced transaction control.
Execute pre-encoded UserOperations for advanced control over transaction execution. This is useful for custom transaction encoding, passkey signatures, and manual UserOp construction.
Overview
The encoded execution endpoint allows you to provide pre-encoded UserOperations with signatures. This gives you full control over the UserOperation structure and is essential for:
- Passkey-based transactions
- Custom signature schemes
- Pre-encoded transaction batching
- Advanced gas optimization
Basic Encoded Execution
// Using API HTTP (fetch)
const response = await fetch('https://api.msa.omnes.tech/executeEncoded', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
encodedUserOp: '0x0000000000000000000000000000000000000000...',
userOpHash: '0x3cf5cc53a628b6c0526ede4ab8fa72c217cf3789...',
walletCustody: 1 // ECDSA_VALIDATOR
})
});
const result = await response.json();
console.log('Transaction hash:', result.txHash);import requests
def execute_encoded():
url = "https://api.msa.omnes.tech/executeEncoded"
payload = {
"encodedUserOp": "0x0000000000000000000000000000000000000000...",
"userOpHash": "0x3cf5cc53a628b6c0526ede4ab8fa72c217cf3789...",
"walletCustody": 1
}
response = requests.post(url, json=payload)
return response.json()
execute_encoded()curl -X POST "https://api.msa.omnes.tech/executeEncoded" \
-H "Content-Type: application/json" \
-d '{
"encodedUserOp": "0x0000000000000000000000000000000000000000...",
"userOpHash": "0x3cf5cc53a628b6c0526ede4ab8fa72c217cf3789...",
"walletCustody": 1
}'Building UserOperations with SmartWallet SDK
Use the SmartWallet SDK to build UserOperations:
import { SmartWallet, PKSigner, AccountOperations, Operation } from '@omnes/smartwallet-ts-sdk';
import { toBytes, toHex } from 'viem';
const signer = await PKSigner.create(privateKey as `0x${string}`);
const smartWallet = await SmartWallet.create(
signer.signMessage,
null, // Don't auto-sign UserOp hash
signer.getEVMAddress(),
rpcURL,
apiKey
);
// Build operations
const operations: Operation = {
to: tokenAddress,
value: BigInt(0),
funcSignature: 'transfer(address,uint256)',
params: [recipient, amount]
};
const accountOperations: AccountOperations[] = [{
account: {
walletCustody: 1,
salt: 'user@example.com',
publicKeys: []
},
operations: [operations],
settings: {}
}];
// Build UserOperations (not signed yet)
const result = await smartWallet.buildUserOperations(
accountOperations,
[],
[]
);
// Sign UserOp hashes manually
const signatures: `0x${string}`[] = [];
for (const userOp of result.userOps) {
const signature = await signer.signMessage(toBytes(userOp.userOpHash));
signatures.push(toHex(signature) as `0x${string}`);
}
// Execute with signatures
const response = await smartWallet.provideSignaturesAndRequestSendUserOperations(
result.userOps,
result.accessList,
signatures,
[] // Operation settings
);Passkey Execution Flow
For passkey-based execution, you need to:
- Build UserOperations
- Extract UserOp hashes
- Sign with passkey (frontend)
- Encode passkey signatures
- Execute with encoded signatures
// 1. Build UserOperations
const result = await smartWallet.buildUserOperations(accountOperations, [], []);
// 2. Extract UserOp hashes
const userOpHashes = result.userOps.map(op => op.userOpHash);
// 3. Sign with passkey (frontend - see passkey-execution.mdx)
const passkeySignatures = await signWithPasskey(userOpHashes);
// 4. Encode passkey signatures
import { encodeAbiParameters } from 'viem';
const encodedSignatures = passkeySignatures.map(sig => {
return encodeAbiParameters(
[{
type: 'tuple', components: [
{ type: 'bytes' }, // authenticatorData
{ type: 'string' }, // clientDataJSON
{ type: 'uint256' }, // challenge (23)
{ type: 'uint256' }, // type (1)
{ type: 'uint256' }, // r
{ type: 'uint256' } // s
]
}],
[[
sig.authenticatorData,
sig.clientDataJSON,
BigInt(23),
BigInt(1),
BigInt(sig.r),
BigInt(sig.s)
]]
);
});
// 5. Execute
const response = await smartWallet.provideSignaturesAndRequestSendUserOperations(
result.userOps,
result.accessList,
encodedSignatures
);UserOperation Structure
A PackedUserOperation contains:
interface PackedUserOperation {
sender: Address; // Wallet address
nonce: bigint; // Nonce
initCode: `0x${string}`; // Initialization code
callData: `0x${string}`; // Function call data
accountGasLimits: `0x${string}`; // Gas limits
preVerificationGas: bigint; // Pre-verification gas
gasFees: `0x${string}`; // Gas fees
paymasterAndData: `0x${string}`; // Paymaster data
signature: `0x${string}`; // Signature
userOpHash: `0x${string}`; // UserOp hash
dependsOn: number; // Dependency index
}Response Format
Same as basic execution:
interface ExecutionResult {
status: number;
txHash: string;
gasUsed: number;
effectiveGasPrice: string;
logs: any[];
errors: any[];
returnData: any[];
}Use Cases
1. Custom Signature Schemes
// Build UserOp
const result = await smartWallet.buildUserOperations(accountOperations, [], []);
// Apply custom signature logic
const customSignatures = result.userOps.map(userOp => {
// Your custom signing logic
return customSign(userOp.userOpHash);
});
// Execute with custom signatures
await smartWallet.provideSignaturesAndRequestSendUserOperations(
result.userOps,
result.accessList,
customSignatures
);2. Pre-encoding for Batch Operations
// Build multiple UserOps
const userOps = await Promise.all(
accounts.map(account =>
smartWallet.buildUserOperations([{
account: { ...account },
operations: [operation],
settings: {}
}], [], [])
)
);
// Encode all UserOps
const encodedUserOps = userOps.map(result => encodeUserOp(result.userOps[0]));
// Execute all at once
await executeBatch(encodedUserOps);3. Gas Optimization
// Build UserOp
const result = await smartWallet.buildUserOperations(accountOperations, [], []);
// Analyze and optimize gas
const optimizedUserOps = optimizeGas(result.userOps);
// Sign optimized UserOps
const signatures = await signUserOps(optimizedUserOps);
// Execute optimized
await smartWallet.provideSignaturesAndRequestSendUserOperations(
optimizedUserOps,
result.accessList,
signatures
);Next Steps
- Passkey Execution - Detailed passkey execution guide
- Gas Estimation - Optimize transaction costs
- Basic Execution - Standard execution patterns
💡 Advanced Control: Encoded execution gives you full control over UserOperations. Use it for custom signature schemes, passkey integration, and advanced optimization.