Cuentas inteligentes
SimpleAccount es una implementación de referencia ECDSA mínima con un único Owner. Los desarrolladores también pueden implementar cuentas inteligentes ERC-4337 personalizadas.
La implementación de SimpleAccount es una dirección compartida por todos los proxies de cuenta, no la dirección de la cuenta de un usuario. Cada instancia se determina mediante su Owner y su salt.
1. API de SimpleAccountFactory
interface ISimpleAccountFactory {
function accountImplementation()
external view returns (address);
function getAddress(
address owner,
uint256 salt
) external view returns (address);
function createAccount(
address owner,
uint256 salt
) external returns (address);
}
| Método | Descripción |
|---|---|
accountImplementation | Devuelve la implementación compartida de SimpleAccount |
getAddress | Calcula la dirección proxy contrafactual de un Owner y salt sin desplegar la cuenta |
createAccount | Crea el proxy si la dirección no tiene código; de lo contrario devuelve la cuenta existente |
Para la primera UserOperation, los datos del Factory constan de su dirección y del calldata de createAccount(owner, salt) codificado mediante ABI. EntryPoint exige que el Factory devuelva la misma dirección que el sender de la UserOperation y que sender contenga código después del despliegue.
2. API de SimpleAccount
interface ISimpleAccount {
function owner() external view returns (address);
function entryPoint() external view returns (address);
function getNonce() external view returns (uint256);
function initialize(address owner) external;
function execute(
address dest,
uint256 value,
bytes calldata func
) external;
function executeBatch(
address[] calldata dest,
uint256[] calldata value,
bytes[] calldata func
) external;
function getDeposit() external view returns (uint256);
function addDeposit() external payable;
function withdrawDepositTo(
address payable withdrawAddress,
uint256 amount
) external;
}
2.1 Estado de la cuenta
| Método | Descripción |
|---|---|
owner | Owner ECDSA único actual |
entryPoint | EntryPoint vinculado a la cuenta |
getNonce | Siguiente nonce de la clave 0 en EntryPoint |
initialize | Inicializa el Owner del proxy; solo puede ejecutarse una vez |
El Factory inicializa el proxy al crearlo. No expongas un proxy sin inicializar a otros usuarios.
2.2 Ejecución de negocio
| Método | Quién puede llamar | Comportamiento |
|---|---|---|
execute | Owner o EntryPoint vinculado | Ejecuta una llamada de destino y propaga los datos de reversión |
executeBatch | Owner o EntryPoint vinculado | Ejecuta atómicamente varias llamadas de destino |
executeBatch requiere el mismo número de direcciones de destino y elementos de calldata. El array de valores puede estar vacío, lo que indica que ninguna llamada transfiere BOT, o tener la misma longitud que el array de destinos.
Cuando el Owner llama directamente a un método de ejecución, utiliza una transacción on-chain normal y paga su Gas. Cuando llama EntryPoint, utiliza una UserOperation y liquida el Gas desde el depósito de la cuenta o del Paymaster.
2.3 Depósito de EntryPoint
| Método | Descripción |
|---|---|
getDeposit | Consulta el depósito de Gas de la cuenta en EntryPoint |
addDeposit | Añade fondos al depósito de EntryPoint de la cuenta |
withdrawDepositTo | Permite al Owner retirar el depósito de la cuenta |
El saldo BOT nativo de la cuenta financia transferencias y llamadas de negocio. El depósito de EntryPoint paga el Gas de UserOperation. Ambos saldos son independientes.
2.4 Recepción de tokens
SimpleAccount puede recibir BOT nativo, ERC-721 y ERC-1155 e implementa la detección de las interfaces receptoras correspondientes.
3. Comportamiento de validación de SimpleAccount
SimpleAccount hereda BaseAccount.validateUserOp:
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
| Paso | Comportamiento de SimpleAccount |
|---|---|
| Comprobación del llamador | Solo acepta el EntryPoint vinculado |
| Hash de firma | Aplica EIP-191 a userOpHash |
| Validación de firma | La recuperación ECDSA debe coincidir con el Owner |
| Firma no válida | Devuelve SIG_VALIDATION_FAILED, es decir, 1 |
| Nonce | EntryPoint gestiona la unicidad; la clave predeterminada es 0 |
| Prefund | Paga missingAccountFunds a EntryPoint |
SimpleAccount no ofrece de forma nativa multisig, recuperación social, Session Keys, Passkeys ni módulos ERC-7579. Implementa una cuenta personalizada cuando necesites estas funciones; no presupongas que SimpleAccount tiene interfaces no documentadas.
4. Cuentas inteligentes personalizadas
Los desarrolladores pueden implementar IAccount desde cero o heredar de BaseAccount. Ambos enfoques deben ser compatibles con EntryPoint v0.7 y con las reglas de validación del Bundler.
4.1 Interfaz obligatoria
interface IAccount {
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external returns (uint256 validationData);
}
| Parámetro | Descripción |
|---|---|
userOp | PackedUserOperation completa que se validará y ejecutará |
userOpHash | Hash vinculado a EntryPoint y chainId |
missingAccountFunds | Diferencia entre el depósito de la cuenta y el prefund requerido |
validationData utiliza esta disposición:
160 bits inferiores: 0 para firma válida, 1 para firma no válida u otro valor para la dirección de un agregador
48 bits siguientes: validUntil; 0 significa sin vencimiento
48 bits superiores: validAfter
4.2 Comportamiento obligatorio
- Verificar que
msg.sendersea el EntryPoint de confianza de la cuenta. - Validar
signature, la política de autorización y el estado necesario de la cuenta. - Devolver
1si la firma no coincide. Otros errores de formato o política pueden revertir. - Aplicar la política de nonce de la cuenta; EntryPoint continúa gestionando su unicidad.
- Si no se usa Paymaster y el depósito es insuficiente, pagar
missingAccountFunds. - Evitar infringir las restricciones ERC-7562 sobre opcodes, acceso al almacenamiento y llamadas externas.
- Proporcionar una validación determinista y simulable sin efectos secundarios incontrolados durante la estimación.
Al heredar BaseAccount, una subclase debe proporcionar como mínimo:
entryPoint(): devuelve el EntryPoint vinculado._validateSignature(...): devuelve la validación de firma y rango temporal._validateNonce(...)opcional: añade una política de nonce personalizada._payPrefund(...)opcional: personaliza la gestión de prefund.
4.3 Diseño de la interfaz de ejecución
Una cuenta puede definir su propia interfaz callData para ejecución individual, por lotes, mediante módulos o con permisos por niveles. De forma predeterminada, EntryPoint envía userOp.callData directamente a la cuenta.
También puede implementar la interfaz opcional:
interface IAccountExecute {
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external;
}
Cuando callData utiliza el selector executeUserOp, EntryPoint entrega a la cuenta la UserOperation completa y el hash para ejecutarlos. Esta interfaz es opcional para una cuenta ERC-4337.
4.4 Factory y despliegue
Un Factory personalizado debe:
- Derivar una dirección de cuenta estable a partir de parámetros deterministas.
- Devolver la dirección de la cuenta desplegada desde su método de creación.
- Colocar la dirección del Factory en los primeros 20 bytes de
initCodepara la primera UserOperation. - Colocar el calldata del Factory en los bytes restantes de
initCode. - Garantizar que el resultado del despliegue coincida con
senderde la UserOperation. - Omitir los campos Factory después de desplegar la cuenta.
El Bundler simula la validación del Factory y de la cuenta. Una UserOperation se rechaza antes de entrar en el mempool si el comportamiento del Factory o de la cuenta incumple las reglas.