# EIP-XXXX: Associated Accounts A standard way to publicly associate two separate accounts with arbitrary contextual data. **Author(s)**: Steve Katzman **Created**: 10/21/25 **State**: DRAFT **Depends on**: EIP-712: https://eips.ethereum.org/EIPS/eip-712 ERC-1271: https://eips.ethereum.org/EIPS/eip-1271 ERC-5267: https://eips.ethereum.org/EIPS/eip-5267 ERC-7930: https://eips.ethereum.org/EIPS/eip-7930 ## Abstract This specification defines a standard for establishing and verifying associations between blockchain accounts. This allows addresses to publicly declare, prove and revoke a relationship with other addresses by doubly signing a fixed payload. This enables use cases like sub-account identity inheritance, authorization delegation, and reputation collation. ## Motivation A key motivation is the simplification of multi-address resolution, which is essential for managing complex digital identities across various platforms with various accounts. This simplification aims to streamline the process of locating and verifying individuals or entities by efficiently handling multiple addresses linked by Associations. By providing a standard mechanism for signaling and proving associations between accounts, this standard unlocks the capability for linking the activities of these accounts, offering a comprehensive view of user behavior and interactions across platforms or even disparate blockchain architectures. The inclusion of arbitrary data into the shared payload ensures flexibility for various use-cases such as delegation, hierarchical relationships, and authentication. By maintaining a flexible architecture that accepts an interface identifier paired with arbitrary data bytes, accounts that associate can do so with application-specific context. Overview The system outlined in this document describes a way for two accounts to doubly sign a shared payload which describes the relationship between them. It focuses on the structure and process for generating, validating and revoking such records while maintaining implementation agnostic. ## Core Concepts Each Association between two accounts denotes the participating addresses as initiator and approver. These accounts can be on disparate chains of disparate architectures made possible by a combination of [ERC-7930 Interoperable Addresses](https://eips.ethereum.org/EIPS/eip-7930) and an enumeration of cryptographic curves. To accommodate non-evm account types, addresses are recorded in the association as raw bytes. ### Associated Account Record The following is a Solidity implementation of an `AssociatedAccountRecord` which contains the shared payload describing the association. ```solidity /// @notice Represents an association between two accounts. struct AssociatedAccountRecord { /// @dev The ERC-7930 binary representation of the iniating account's address. bytes initiator; /// @dev The ERC-7930 binary representation of the approving account's address. bytes approver; /// @dev Optional 4-byte selector for interfacing with the `data` field. bytes4 interfaceId; /// @dev Optional additional data. bytes data; } ``` Where the AssociatedAccountRecord contains: `initiator` is the binary representation of an ERC-7930 address for the initiating account. `approver` is the binary representation of an ERC-7930 address for the approving account. (optional) `interfaceId` is the 4-byte uuid for type/interface of the `data` field. (optional) `data` is the arbitrary context data payload. ### Signed Association Record The EIP-712 hash of the `AssociatedAccountRecord` is signed by both the initating and approving accounts. The resulting signatures are included in a complete association record. ```solidity /// @notice Complete payload containing a finalized association. struct SignedAssociationRecord { /// @dev The timestamp from which the association is valid. uint120 validAt; /// @dev The timestamp the association was revoked. uint120 revokedAt; /// @dev The initiator curve specifier. bytes1 initiatorCurve; /// @dev The approver curve specifier. bytes1 approverCurve; /// @dev The signature of the initiator. bytes initiatorSignature; /// @dev The signature of the approver. bytes approverSignature; /// @dev The underlying AssociatedAccountRecord. AssociatedAccountRecord record; } ``` Where the SignedAssociationRecord contains: The `validAt` timestamp, describing the time at which the approver wishes the association to be valid. The `revokedAt` timestamp, which is `0` unless the association has been revoked by either party. The `initiatorSignature` which is the signature bytes generated by the `initiator` by signing the EIP-712 compliant hash of the AssociatedAccountRecord. The`initiatorCurve` is the curve enumeration for the initiator account. The `approverSignature` which is the signature bytes generated by the `approver` by signing the EIP-712 compliant hash of the AssociatedAccountRecord. The `approverCurve` is the curve enumeration for the approver account. The `record` which is the AssociatedAccountRecord that was signed by both parties. ### Curves To accommodate known curves and provide future extensibility, this specification relies on the enumeration of cryptographic curves defined in this [Draft EIP](https://github.com/chunter-cb/EIPs/blob/enshrined-aa-validation/EIPS/eip-draft_aa_enshrined_validation.md) (*@TODO switch to link to formal EIP once accepted*). Each signature MUST be paired with a valid curve designator. ## Specification ### Support for EIP-712 All signatures contained in this specification must comply with EIP-712 wherein the signature hash can be generated from: ```solidity keccak256(abi.encodePacked( hex"1901", DOMAIN_SEPARATOR, keccak256(abi.encode( keccak256("AssociatedAccountRecord(bytes initiator, bytes approver, bytes4 interfaceId, bytes data)"), keccak256(address1), keccak256(address2), interfaceId, keccak256(data) )) )) ``` Where `address1` and `address2` are the `initiator` and `approver` addresses sorted in ascending numerical order. This ensures deterministic hashes without requiring origination ordering and allows for associations between two accounts with shared context to exist bidirectionally. See Sorting Addresses below. Where `DOMAIN_SEPARATOR` is defined according to EIP-712. The `DOMAIN_SEPARATOR` for this EIP shall be defined as: ```solidity keccak256(abi.encode( keccak256("EIP712Domain(string name,string version)"), keccak256(bytes(name)), keccak256(bytes(version)) )) ``` ## Flow ![Screenshot 2025-11-04 at 9.39.33 AM](https://hackmd.io/_uploads/SJtd53wkZx.png) *Flow diagram depicting a rudimentary flow for Associating two accounts through some shared, generic client app.* ### Origination To originate an association, the initiator account generates the underlying Associated Account Record and then signs it. ##### Message Creation The Initiator who wants to create an association with a second Approver address constructs an `AssociatedAccountRecord` with both accounts’ addresses and any optional data in the `interfaceId` and `data` fields respectively. ##### Signing The Initiator then generates an EIP-712 message hash over the `AssociatedAccountRecord` and signs this message hash. ##### Output The inititator returns a partial SignedAssociationRecord (SAR) which contains the following: - `validAt`: The timestamp that signifies the earliest that the association can be considered valid. This MAY be left `0` during this step. - `revokedAt`: MUST be left `0` during this step. - `initiatorSignature`: The signature bytes returned from the Origination.Signing step above. - `initiatorCurve` the Curve designator describing the signature format for the `initiatorSignature` field. - `approverSignature` MUST be left empty during this step. - `approverCurve` MUST be left empty during this step. - `AssociatedAccountRecord`: The AAR from the Message Creation step above. The resulting partially signed SAR SHALL be returned to the client for use in-context. *@TODO we need to opine on ways for initiators to publicize the partially signed SAR so that approvers can reasonably find and access them*. ### Approval *Somehow (see @TODO above)* the approver receives the partially signed SAR generated in the Origination section above. This account then needs to sign the EIP-712 hash of the AAR contained within and append its signature and timestamps. ##### Signing The `approver` hashes the `record` containing the AAR from Origination per EIP-712 and signs the resulting hash. ##### Completing the SAR The `approver` then appends the following data to the SAR provided initially: - The `validAt` field MAY be set iff the field is zero. - The `revokedAt` field MUST be left `0` during this step. - The`approverSignature` MUST be filled with the signature generated in Approval.Signing above. - The `approverCurve` MUST be filled with a valid Curve deisgnator describing the signature format for the `appoverSignature`. ### Storage Upon completeing a SAR, an approver, initiator or relayer service can submit the struct to a compliant storage contract or offchain indexer. An onchain storage contract MUST comply with the following steps: 1. The SAR must be validated according to the Validation steps delineated below. 2. The contract MUST emit the `AssocationCreated` event: ```solidity event AssociationCreated( bytes32 indexed uuid, bytes32 indexed initiator, bytes32 indexed approver, SignedAssociationRecord sar ); ``` where: - `uuid` is the indexed uuid for the SignedAssociationRecord, equivalent to the EIP712 hash of the underlying AAR. - `initiator` is the keccak256 hash of the ERC-7930 address of the account that initiated the association. - `approver` is the keccak256 hash of ERC-7930 address of the account that accepted and completed the association. - `sar` is the completed SignedAssociationRecord. ### Validation and Consumption Clients or contracts determining whether a SignedAssociationRecord is valid at the time of consumption must check all of the following validation steps: 1. The current timestamp MUST be greater or equal to the `validAt` timestamp. 2. The current timestamp MUST be less than the `revokedAt` timestamp. 3. The `initiatorSignature` recovers to the `initiator` address when using the provided `initiatorCurve`'s recovery mechanism. 4. The `approverSignature` recovers to the `approver` address when using the provided `approverCurve`'s recovery mechanism. Onchain validation is possible so long as there are sufficient validation mechanisms for the various curves used by the two accounts. In the case that validation occurs onchain, implementations MUST replace "current timestamp" with `block.timestamp`. Once a SAR has been validated, its underlying AssocatedAccountRecord is considered valid and can be used in-context. ### Revocation Either party of an Association can revoke a valid, active association by submitting a revocation request. For onchain assocation stores, the implementation contract MUST emit the following event: ```solidity event AssociationRevoked(bytes32 indexed uuid, bytes32 indexed revoker); ``` where: - `uuid` is the indexed unique identifier for the association, equivalent to the EIP712 hash of the underlying AAR. - `revoker` is the indexed keccak256 hash of the ERC-7930 address of the revoking account. Then, storage contracts must update the `revokedAt` field of the SAR to `block.timestamp` OR the account-specified revocation timestamp, whichever is greater.