Saltar al contenido principal

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étodoDescripción
accountImplementationDevuelve la implementación compartida de SimpleAccount
getAddressCalcula la dirección proxy contrafactual de un Owner y salt sin desplegar la cuenta
createAccountCrea 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étodoDescripción
ownerOwner ECDSA único actual
entryPointEntryPoint vinculado a la cuenta
getNonceSiguiente nonce de la clave 0 en EntryPoint
initializeInicializa 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étodoQuién puede llamarComportamiento
executeOwner o EntryPoint vinculadoEjecuta una llamada de destino y propaga los datos de reversión
executeBatchOwner o EntryPoint vinculadoEjecuta 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étodoDescripción
getDepositConsulta el depósito de Gas de la cuenta en EntryPoint
addDepositAñade fondos al depósito de EntryPoint de la cuenta
withdrawDepositToPermite 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);
PasoComportamiento de SimpleAccount
Comprobación del llamadorSolo acepta el EntryPoint vinculado
Hash de firmaAplica EIP-191 a userOpHash
Validación de firmaLa recuperación ECDSA debe coincidir con el Owner
Firma no válidaDevuelve SIG_VALIDATION_FAILED, es decir, 1
NonceEntryPoint gestiona la unicidad; la clave predeterminada es 0
PrefundPaga 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ámetroDescripción
userOpPackedUserOperation completa que se validará y ejecutará
userOpHashHash vinculado a EntryPoint y chainId
missingAccountFundsDiferencia 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.sender sea el EntryPoint de confianza de la cuenta.
  • Validar signature, la política de autorización y el estado necesario de la cuenta.
  • Devolver 1 si 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 initCode para la primera UserOperation.
  • Colocar el calldata del Factory en los bytes restantes de initCode.
  • Garantizar que el resultado del despliegue coincida con sender de 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.