Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

iip: 9
title: Abstract IOTA Accounts
description: Abstract accounts on IOTA enable smart-contract-based authentication of addresses.
author: Mirko Zichichi (@miker83z) , Valerii Reutov (@valeriyr) , Levente Pap (@lzpap) ,
discussions-to: https://github.com/iotaledger/IIPs/discussions/35
status: Draft
type: Standards Track
layer: Core
created: 2026-02-11
requires: IIP-0010

Abstract

This proposal defines a new account type for the IOTA protocol: the Abstract IOTA (AI) Account. An AI Account features a stable on-chain identifier and programmable authentication logic, enabling smart-contract-based verification in place of traditional private key signatures.

From the perspective of decentralized applications (dApps) and their users, AI Accounts function identically to traditional Externally Owned Accounts (EOAs). The primary objective is to enable flexible authentication mechanisms while ensuring compatibility with existing infrastructure and maintaining consistent behavior at the protocol level.

Motivation

The primary aim of this proposal is to significantly improve user experience throughout the IOTA ecosystem by supporting a diverse range of account authentication methods. This initiative enables extensible user authentication while preserving the integrity of the base protocol and ensuring that backward compatibility is not compromised. Under this model, accounts can operate and authenticate without relying on private keys, instead utilizing external inputs or executable code.

Authentication paradigms enabled by this proposal include, but are not limited to:

  • Arbitrary cryptographic authentication
  • Two-factor authentication (2FA)
  • Dynamic multi-signature schemes
  • Key rotation and recovery options
  • Redundant signing methods
  • DAO governance and treasury management
  • Developer team or admin accounts for secure contract management

Specification

This section presents the technical specification for implementing an Account Abstraction model within the IOTA protocol. The Abstract IOTA (AI) Account is a new account type designed to support flexible, programmable authentication while maintaining full compatibility with existing protocol components.

At a high level, the mechanism works as follows:

  1. A third-party developer publishes a Move package containing a custom AuthenticatorFunction annotated with #[authenticator].
  2. An AI Account object is created on-chain and linked to this AuthenticatorFunction via an AuthenticatorFunctionRef.
  3. When a transaction is submitted with the AI Account as sender, the protocol invokes the linked AuthenticatorFunction — passing the proof data from the MoveAuthenticator signature field — instead of performing traditional signature verification.
  4. If the AuthenticatorFunction executes successfully, the transaction is considered authenticated; if it aborts, the transaction is rejected.

Requirements

The proposed Account Abstraction model must adhere to the following constraints:

  1. Stable Address: Each AI Account must be associated with a stable and persistent address, enabling long-term reference and asset ownership.
  2. Interoperability: AI Accounts must be capable of interacting seamlessly with any on-chain smart contract, without being restricted to specific protocols, types, or interfaces.
  3. True Abstraction: dApps must be able to craft and propose transactions to user wallets without distinguishing between AI Accounts and EOAs. The transaction structure and interaction flow should remain identical, such that the dApp is agnostic to the account type it is interacting with.
  4. Identifier Uniqueness: Each AI Account must be uniquely identifiable. The system must guarantee a one-to-one correspondence between account instances and their associated identifiers.
  5. Programmable Authentication: Authentication logic must be fully programmable, enabling third-party developers to define and deploy custom authentication schemes tailored to specific application needs.
  6. Backward Compatibility: The system must preserve the functionality of existing EOAs, including their native signature-based authentication mechanisms.
  7. Open Account Creation: The creation of AI Accounts must be permissionless, i.e., any entity should be able to initialize an AI Account on behalf of another, provided the intended owner can later authenticate themselves.
  8. API Uniformity: Developer-facing APIs for querying balances, initiating transfers, and similar operations must behave identically for both AI Accounts and EOAs, promoting transparency and ease of integration.
  9. Transaction Format Consistency: The format and semantic structure of transaction payloads must remain unchanged. The protocol must handle TransactionData for AI Account transactions identically to legacy EOAs.
  10. Gas Cost Parity: Current IOTA protocol static signature verification methods and Move-based verification counterparts should be cost-equivalent in terms of gas.

Protocol Design

To support AI Accounts, we propose the addition of a new set of modules within the iota-framework. These modules define the core data structures and their associated methods for creating and interacting with an AI Account.

Account Representation and Addressing

AI Accounts are represented as objects within the IOTA Move framework, each associated with a globally unique 32-byte ObjectID. The ObjectID serves as the AI Account Identifier and is itself a valid IotaAddress. Ownership of on-chain objects by AI Accounts is expressed using the existing AddressOwner(IotaAddress) variant, where the IotaAddress is the AI Account Identifier.

Background: IOTA Ownership Model

Every object in the IOTA Protocol has a well-defined owner. Only the owner can use their objects as input to a transaction. The current ownership semantics are as follows:

  • AddressOwner(IotaAddress)
    • AddressOwner(IotaAddress=PubKey-derived) – Object owned by a single EOA; it can be set as input of a transaction if the EOA provides a valid signature using the private key associated with the public key from which the IotaAddress was derived.
    • AddressOwner(IotaAddress=ObjectID) – The owner is an IotaAddress which is interpreted as an ObjectID. Objects are, in essence, owning other objects. To unlock such owned objects, they need to be received in a transaction the first time they are accessed after being transferred.
  • ObjectOwner(IotaAddress=ObjectID) – Object owned by another object, in a hierarchical, parent-child relationship. The owned object can be dynamically accessed in transactions where the parent is used as input. This is used for Dynamic Fields.
  • SharedOwner – Mutably accessible by any address; it can be set as input of a transaction with no checks.
  • ImmutableOwner – Immutably accessible by any address; it can be set as input of a transaction with no checks.
Rationale for AddressOwner

While an ownership of type ObjectOwner might appear suitable for an AI Account, since it is indeed an object, it would require additional steps and access control logic when objects are transferred to it. Specifically, if an ObjectOwner ownership relationship were established, the sender of an object would be required to use the AI Account object as input of the transfer transaction; moreover, mutable access to the AI Account object would also be necessary.

Employing an AddressOwner ownership relation, instead, allows the sender to simply use the AI Account Identifier as the address. This choice renders AI Accounts indistinguishable from EOA addresses at the ownership level.

Objects transferred to an AI Account Identifier do not require explicit acceptance by the receiver. When set as inputs of a transaction, their “unlocking” is performed by verifying that the AI Account Identifier matches the sender of the transaction, thereby ensuring that the account is authenticated.

Figure 1 illustrates how the AddressOwner(IotaAddress) ownership variant applies to both EOA and AI Accounts. As shown, the external interface is identical, but the underlying derivation and verification mechanisms differ at the protocol level.

Ownership variants

The following table summarizes the key differences:

AspectEOAAbstract Account
How is the address of the account derived?0xffff address derived from the EOA public key.0xabc is the ObjectID of the object representing the account.
IotaAddress = AIAccountID = ObjectID
Owned object that the transaction wants to unlock (input)0x123 with type Coin<IOTA>, owner is 0xffff.0x123 with type Coin<IOTA>, owner is 0xabc.
Sender field of the transaction0xffff0xabc
Signature field of the transactionPayload containing a valid signature created using the private key (e.g., ED25519).Payload containing bytes created by logic unknown to the IOTA protocol, but verifiable by a Move AuthenticatorFunction that was arbitrarily created by a third-party developer and dynamically linked to the AI Account Identifier.

Move Authenticator

As seen above, AI Accounts are capable of initiating transactions, i.e., the AI Account Identifier can be used as sender of transactions. However, unlike EOAs, whose authentication derives from private key signatures, the AI Account Identifier is not necessarily tied to a specific cryptographic keypair, nor derived from one. Therefore, a new authentication flow is introduced into the protocol to allow the implementation of arbitrary authentication mechanisms in Move: the MoveAuthenticator.

The MoveAuthenticator is a protocol-level transaction signature variant that allows transaction senders to submit a vector of arguments in the transaction signature field that are passed to the AuthenticatorFunction of the AI Account. This enables third-party developers to implement custom programmable authentication schemes using Move.

Note

Digression on the structure of IOTA transactions

Currently, the structure of IOTA transactions includes:

  • TransactionData: Contains core transaction payload such as Programmable Transaction Blocks (PTB) commands and gas configuration.
  • GenericSignatures: A vector of protocol-supported authenticators, currently including MultiSig, Signature, ZkLoginAuthenticator (disabled), and PasskeyAuthenticator.

The proposed addition of MoveAuthenticator extends the set of GenericSignature variants to support dynamic authentication logic. With this extension, third-party developers can deploy Move packages implementing a custom AuthenticatorFunction. An AI Account can then be configured to delegate its authentication to such a package. Inputs to the AuthenticatorFunction are encoded as Vec<CallArg> (where CallArg is a type defining pure and object arguments) and provided in the MoveAuthenticator signature field. The function either completes successfully or aborts with an error if authentication fails.

Figure 2 compares the authentication flow for transactions issued by EOA and AI Accounts. The key distinction is that EOAs rely on cryptographic signature verification at the protocol level, whereas AI Accounts delegate verification to a developer-defined AuthenticatorFunction executed in Move.

Transaction authentication

The following subsections describe each authentication flow in detail.

(Traditional) EOA Transaction Authentication

TransactionData is the payload that makes transactions cryptographically unique. It includes sender, inputs, commands and gas data. Passing the raw bytes of the payload to a hash function yields its digest, i.e., TransactionDigest, which becomes the transaction identifier (encoded in base58).

An EOA user signs the TransactionDigest with the EOA private key (the description is simplified for the sake of readability). This signature becomes the payload of the GenericSignature part of the transaction. Once transmitted to a validator, the transaction is authenticated by extracting the sender address and transaction digest from the TransactionData and verifying them against the signature. If verification fails, the transaction is marked as invalid.

Abstract Account Transaction Authentication

In the case of AI Accounts, the TransactionData part is exactly the same as the EOA case; it necessitates no changes. The difference lies in the GenericSignature part. A developer-defined mechanism can be used (e.g., a different signature scheme, passkey method or business logic) to generate a proof on the client side that can be validated by the AuthenticatorFunction in Move.

This proof (or set of proofs) becomes the MoveAuthenticator payload of the GenericSignature field of the transaction. Validators authenticate the transaction by executing the account’s dynamically linked AuthenticatorFunction Move function with the proof provided as input argument(s). It is important to note that the AuthenticatorFunction has a “rich” context: it has access to the TxContext contained in the TransactionData, i.e., it knows the TransactionDigest, and also accesses the inputs and commands of TransactionData using an AuthContext. This AuthContext struct enables parsing of the PTB information included in the TransactionData.

Should the execution of AuthenticatorFunction fail for any reason, the transaction is marked as invalid.

The AI Account Interface

The AI Account representation in Move is designed such that third-party developers can implement any arbitrary type. There is no single AI Account framework object type; rather, any object type can become an AI Account provided it implements the required interface.

A Move type is considered an abstract account if, and only if:

  • It is an object (i.e., a struct type that has key ability and an id: UID field),
  • has a dynamic field with key of type 0x2::account::AuthenticatorFunctionRefV1Key and value of type 0x2::authenticator_function::AuthenticatorFunctionRef.

The type 0x2::account::AuthenticatorFunctionRef contains the fields necessary to uniquely identify an on-chain AuthenticatorFunction defined by an external package. For version 1:

public struct AuthenticatorFunctionRefV1<phantom Account: key> has copy, drop, store {
    package: ID,
    module_name: ascii::String,
    function_name: ascii::String,
}

Figure 3 illustrates a concrete example in which a custom Authenticator Move Package is deployed and linked to an AI Account to provide authentication:

Custom move based authenticator

In the example above, we imagine a scenario where a developer independently develops the Custom Authenticator package deployed at the address 0x789 (i.e., package id). This package defines the account interface through a module named custom_auth. In this module a 0x789::custom_auth::CustomAccount Move object type is defined, which represents a specific implementation of an AI Account. The CustomAccount uses the field auth_helpers to store data on-chain; the logic governing this account is arbitrarily defined by the developer.

The only field required to make CustomAccount an AI Account is a dynamic field using the 0x2::account::AuthenticatorFunctionRefV1Key key and 0x2::authenticator_function::AuthenticatorFunctionRef value. This dynamic field entry can only be created through a framework method in the 0x2::account module, namely create_account_v1.

The object with id 0xabc, once becoming an AI Account, has an authentication method clearly defined by the “attached” AuthenticatorFunctionRef. This struct references a specific function within the category of Move functions known as AuthenticatorFunction. In the example, it references the 0x789::custom_auth::authenticate function defined in the same module.

The Authenticator Function

The AuthenticatorFunction is designed to be as generic as possible, allowing third-party developers to implement arbitrary authentication logic. For instance, in the above example, the CustomAccount object’s auth_helpers field can be used within the AuthenticatorFunction. This field contains on-chain data that may have been modified by other parties prior to authentication. In general, an AuthenticatorFunction receives inputs and can read from the ledger state to allow or reject access to the AI Account. The vec<CallArg> passed through the MoveAuthenticator payload of the GenericSignature part of the transaction is converted into function parameters similarly to what happens today for PTBs.

An AuthenticatorFunction MUST satisfy the following rules:

  1. Visibility: The function MUST be declared as a public non-entry function.

  2. Read-only inputs: All inputs MUST be read-only. Accepted input types are pure types (integers, strings, etc.) and read-only references to objects. Owned Objects MUST NOT be passed as input; only Shared Objects and Immutable Objects are permitted.

  3. First parameter — Account reference: The first parameter MUST be a reference to the same Move type as the AI Account being authenticated. The object ID of the argument passed for this parameter MUST be exactly equal to the AI Account Identifier (i.e., the sender of the transaction).

  4. No return type: The function MUST NOT define a return type. Authentication succeeds if the function completes execution without error and fails if the function aborts.

  5. Context parameters: The second-to-last parameter MUST be &AuthContext and the last parameter MUST be &TxContext. The AuthContext struct exposes the underlying transaction fields (PTB inputs and commands), while TxContext exposes the transaction digest, gas parameters, and sponsor details. These context values are not created by the user; the protocol automatically creates and injects them before execution. The presence of AuthContext ensures that an AuthenticatorFunction cannot be invoked from within Move by other functions.

Execution Lifecycle

From the protocol point of view, the AuthenticatorFunction is invoked twice during the transaction lifecycle:

  1. Optimistic Pre-Consensus Authentication:
    • Upon receiving a transaction, all validators execute the AuthenticatorFunction related to the TX’s sender account with any shared object read-only reference or pure inputs.
    • If the pre-consensus authentication passes, then the TX is treated as any other, i.e., the TX’s owned objects (gas payment objects and the TX inputs) are locked and the validator signature needed for a certificate is returned.
    • In this phase, no state modifications occur, i.e., no gas is consumed for the execution of the authentication.
  2. Post-Consensus Authentication Execution:
    • Once the transaction is ready for the post-consensus execution, the AuthenticatorFunction is executed for the second time immediately before the normal execution.
    • The gas object versions for the gas payment are guaranteed for post-consensus execution since a majority of validators locked those gas objects.
    • In this second phase, AuthenticatorFunction execution can still fail due to changes in the state of input shared objects. In this case the authentication gas cost is deducted, but no other state changes are committed to the ledger and the transaction execution result is ABORTED.
    • Gas costs for authentication in non-sponsored transactions are deducted from an AI Account’s gas object. Sponsored transactions cover authentication costs via gas payment objects owned by the sponsor.

Creating AuthenticatorFunctionRef

The AuthenticatorFunctionRef, as described above, is a struct that uniquely identifies a function within a package deployed on-chain. Its creation is enabled via the Package Metadata Standard (see IIP-0010).

Figure 4 shows how the PackageMetadata object is used to validate and create an AuthenticatorFunctionRef during package publication.

Creating authenticator function references

Continuing with the same example from the previous subsection, consider the CustomAuthenticator package. When this package is published to the ledger, an associated PackageMetadata immutable object is created. This immutable object contains metadata for each module, including functions found in the 0x789::custom_auth module. Specifically, the 0x789::custom_auth::authenticate function is designated as an “authenticator” within the PackageMetadata object. To be designated as an authenticator, a function MUST follow the rules listed in the previous subsection and MUST be annotated with the #[authenticator] function attribute.

The PackageMetadata object acts as a source of validation. This object is created by the protocol during package publication. If an #[authenticator] attribute is found for a function, the protocol validates that function using the iota-move-verifier. Since PackageMetadata is an immutable object that can only be created by the protocol, it cannot be forged with an unauthorized authenticator function.

Given the trusted nature of the PackageMetadata, an AuthenticatorFunctionRef can be created by passing a PackageMetadata object as input. The function 0x2::authenticator_function::create_auth_function_ref takes a PackageMetadata, a module name, and a function name, then verifies whether these inputs resolve to a valid authenticator function; if so, it returns an AuthenticatorFunctionRef. This reference is then used to create an account.

Rationale

This specification introduces a flexible, developer-centric model for account abstraction that preserves compatibility with the existing transaction format and authorization model. By leveraging Move’s programmability and object-oriented design, the system supports a wide range of use cases—from key rotation and passwordless login to DAO-based access control and cross-device passkey authentication.

Alternative models—such as static multisignature schemes or fixed key lists—were deemed insufficient due to their lack of adaptability to dynamic and composable use cases. In contrast, the AI Account model integrates tightly with the Move-based models for asset management.

Similar models have emerged in other blockchain ecosystems, such as Ethereum’s ERC-4337 and Aptos’ dynamic dispatch system. However, this proposal is uniquely tailored to the IOTA protocol’s architecture, emphasizing on-chain object ownership, deterministic addressing, and native MoveVM integration.

Backwards Compatibility

This proposal is designed to be fully backward compatible with the existing IOTA protocol. The following areas are affected:

AreaImpact
Transaction FormatNo changes are made to the TransactionData structure.
Signature SupportA new MoveAuthenticator variant is added alongside the existing variants in GenericSignature. Existing signature types remain unaffected.
Validation PipelineAn optional AuthenticatorFunction hook is introduced for transaction validation and pre-PTB execution. The existing validation pipeline is not modified.
RPC and Wallet APIsRPC APIs remain unchanged. AI Accounts are fully compatible with existing APIs and indistinguishable from EOAs. However, wallets that wish to support the new account type MUST add support for each authenticator type defined on-chain to derive the bytes supplied to the MoveAuthenticator.

Test Cases

The following test cases SHOULD be implemented to validate the correctness and completeness of the AI Account model:

Test CaseDescriptionReference Move Implementation
Single-key EOA replicationAn AI Account that replicates standard single-key cryptographic authentication, including key rotation capability.https://github.com/iotaledger/iota/tree/develop/examples/move/iotaccount
Dynamic multisigAn AI Account authenticated via dynamic multisignature logic based on signatures or on-chain capabilities.https://github.com/lzpap/isafe/tree/main/contracts/isafe
Function-Call keysAn AI Account authenticated using a method similar to Near’s Function-Call keys, limiting access only to the execution of some methods for an account.https://github.com/iotaledger/iota/tree/develop/examples/move/function_keys
Spending limitAn AI Account where several users are authorized to access but having a specific allowance of IOTA conis set for each one.https://github.com/iotaledger/iota/tree/develop/examples/move/spending_limit
Third-party authenticationAn AI Account authenticated using an external or third-party verification mechanism, such as ZK proofs validation.https://github.com/iotaledger/iota/pull/10227
Time lockedAn AI Account where the authentication is constrained by time.https://github.com/iotaledger/iota/tree/develop/examples/move/time_locked

Reference Implementation

Main PR against the develop branch: https://github.com/iotaledger/iota/pull/9586

References to Account Abstraction projects in Web3

Copyright and related rights waived via CC0.