# Method: `account-signature.<profile>.v1`

An ENS address record connects a name to an account:

```solidity
addr(namehash(name), coinType) -> accountBytes
```

This method asks both sides to approve that connection:

1. the current authority for the ENS name signs the claim; and
2. the account stored in the address record signs the same claim.

A result is positive only when both signatures are valid for the live record.
This prevents another ENS name from copying an address and reusing its proof.

Each target chain signs messages differently. The profile in the method name
selects the chain, supported account type, and native verification algorithm.
The claim, proof envelope, and verification flow remain the same for every
profile.

## Supported Records

The method supports an `addr(coinType)` record when the selected
[profile](#profiles) defines that chain and account type. It does not apply to
text records or to an unregistered chain profile.

For an address claim:

| Claim field  | Required value                                                   |
| ------------ | ---------------------------------------------------------------- |
| `recordType` | `addr`                                                           |
| `recordKey`  | Coin type as an unsigned decimal string                          |
| `valueHash`  | Keccak-256 of the exact bytes returned by `addr(node, coinType)` |
| `method`     | Exact profile identifier from the registry                       |
| `target`     | Canonical CAIP-10 account derived by the selected profile        |

Hash the resolver bytes, not a displayed or re-encoded address.

`claim.target` is the canonical [CAIP-10 account
ID](https://standards.chainagnostic.org/CAIPs/caip-10) for the live address. The
selected profile defines how to derive it from the coin type and resolver bytes.

## Candidate Profiles

The following profiles are candidates for future support. A profile is not
supported until its complete native rules and interoperability vectors are
defined. A verifier must use the exact profile selected by `claim.method` and
must not fall back to another profile.

| Profile             | Chains              | Account type                                           | Algorithm                                                                                                      |
| ------------------- | ------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `eip155`            | EVM chains          | EOA or deployed ERC-1271 contract                      | [EIP-712](https://eips.ethereum.org/EIPS/eip-712) or [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271)       |
| `bip322`            | Bitcoin             | BIP-322-compatible address                             | [BIP-322 Simple](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki)                               |
| `solana-offchain`   | Solana              | On-curve Ed25519 account                               | [Solana off-chain message v1](https://github.com/solana-foundation/SRFCs/discussions/3)                        |
| `arweave-rsapss`    | Arweave             | RSA account                                            | [RSA-PSS message signing](https://docs.arconnect.io/api/sign-message)                                          |
| `nep413`            | NEAR                | Named or implicit account with a valid full-access key | [NEP-413](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)                                           |
| `cip8`              | Cardano             | Address with a key payment credential                  | [CIP-8 COSE signing](https://cips.cardano.org/cip/CIP-8)                                                       |
| `adr36`             | Cosmos SDK chains   | Direct secp256k1 account                               | [ADR-036](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md)     |
| `substrate-signraw` | Polkadot and Kusama | Direct SS58 account                                    | [Substrate `signRaw`](https://polkadot.js.org/docs/extension/cookbook/#sign-a-message)                         |
| `arc60`             | Algorand            | Direct, non-rekeyed Ed25519 account                    | [ARC-60](https://dev.algorand.co/arc-standards/arc-0060/)                                                      |
| `sui-personal`      | Sui                 | Native Sui account                                     | [Sui personal-message signing](https://sdk.mystenlabs.com/sui/cryptography)                                    |
| `sep53`             | Stellar             | Ed25519 `G...` account                                 | [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md)                        |
| `tip191`            | TRON                | Direct secp256k1 account                               | [TIP-191](https://github.com/tronprotocol/tips/blob/master/tip-191.md)                                         |
| `filecoin-wallet`   | Filecoin            | Direct `f1` secp256k1 or `f3` BLS account              | [WalletSign and WalletVerify](https://docs.filecoin.io/reference/json-rpc/wallet)                              |
| `tezos-micheline`   | Tezos               | Implicit `tz1`, `tz2`, `tz3`, or `tz4` account         | [Micheline string signing](https://gitlab.com/tezos/tzip/-/blob/master/drafts/current/draft-string-signing.md) |

## Descriptor

The descriptor selects the profile and points to the exact proof envelope.

HTTPS publication:

```text
ensrv1 a=1 m=account-signature.<profile>.v1 u=https://proofs.example/account-proof.json
```

IPFS publication:

```text
ensrv1 a=1 m=account-signature.<profile>.v1 u=ipfs://<proof-envelope-cid>
```

Replace `<profile>` with one exact identifier from the profile table.

`u` is required and points directly to the proof envelope. Account records do
not provide a deterministic place from which to fetch a signature, so these
methods do not use a proof key.

The proof may be published at an HTTPS URL or as immutable IPFS content. For
IPFS, `u` contains the CID of the proof envelope. Changing the envelope creates
a new CID, so the descriptor must also be updated.

Apply the URI and retrieval rules defined by the
[verification descriptor](../components/verification-descriptor). A verifier
must not search for a proof at another location.

## Proof

The resource at `u` is a complete [proof
envelope](../components/claims-and-signatures#proof-envelope). For example, a
Bitcoin address proof has this shape:

```json
{
  "v": "ensrv1",
  "claim": {
    "name": "alice.eth",
    "node": "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec",
    "recordType": "addr",
    "recordKey": "0",
    "valueHash": "0x11b469329dc927a13638ec09294fa2e578ef6f71e2c8ed15266a02822114e8ef",
    "authorityVersion": "1",
    "authority": "0x1234567890abcdef1234567890abcdef12345678",
    "method": "account-signature.bip322.v1",
    "target": "bip122:000000000019d6689c085ae165831e93:bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l",
    "issuedAt": "1783728000",
    "validUntil": "1791504000"
  },
  "authoritySignature": "0x<authority-signature>",
  "proof": {
    "targetSignature": "<bip322-simple-signature>"
  }
}
```

`authoritySignature` is the ENS authority's signature. `targetSignature` is the
signature made by the account in `claim.target`.

A profile may also require `proof.publicKey` when the signing key cannot be
recovered from the address, signature, or native proof. The profile defines the
exact encoding. Unknown fields are forbidden, and the verifier must prove that
any supplied public key controls the exact CAIP-10 target.

## Signed Message

Calculate one 32-byte digest from the [common EIP-712
claim](../components/claims-and-signatures#common-claim):

```text
commonClaimDigest = keccak256(
  0x1901 || domainSeparator || hashStruct(commonClaim)
)

authoritySignature = authority.signEIP712(commonClaim)
targetSignature = selectedProfile.sign(
  account = commonClaim.target,
  message = bytes(commonClaimDigest)
)
```

The authority signature is validated against `commonClaimDigest` using the
common authority-signature rules. The target account signs the raw 32 digest
bytes using the native algorithm selected by `commonClaim.method`.

## Verification

```mermaid
sequenceDiagram
  autonumber
  actor App
  participant ENS as ENS and Universal Resolver
  participant SDK as Verification SDK
  participant Host as Proof URI
  participant Account as Native account verifier

  App->>SDK: Verify one ENS address record
  SDK->>ENS: Read address, descriptor, and authority at one block
  ENS-->>SDK: Live ENS state
  SDK->>SDK: Derive value hash, profile, and target
  SDK->>Host: Fetch the exact proof URI
  Host-->>SDK: Return proof envelope
  SDK->>SDK: Compare every common claim field
  SDK->>SDK: Validate authoritySignature
  SDK->>Account: Validate targetSignature with selected profile
  Account-->>SDK: Return signer or account result
  SDK->>SDK: Bind result to target and check time bounds
  SDK-->>App: Return control result
```

A verifier succeeds only when it:

1. resolves the address, descriptor, and authority at one ENS evaluation block;
2. requires the selected profile to support the live coin type and account form;
3. recomputes `valueHash` and the canonical `target` from the live address;
4. fetches and parses the closed envelope from the exact descriptor `u`;
5. compares every common claim field with the live inputs;
6. validates `authoritySignature` against the live exact-name authority;
7. validates `targetSignature` using only the selected native profile;
8. proves that the native signer or account policy controls the exact target;
9. validates any required target-chain state; and
10. requires the claim, authority, and method evidence to be current.

The minimal positive result is:

```json
{
  "verified": true,
  "verificationType": "control"
}
```

An SDK should expose the exact method, coin type, canonical target, ENS block,
and any target-chain block as diagnostic metadata.

An unknown profile, unsupported account form, unavailable proof URI, or
unavailable target-chain state is not a positive result. A transport failure is
not evidence that either signature is cryptographically invalid.

## Caching

The proposed maximum signed lifetime is 90 days:

```text
validUntil - issuedAt <= 7,776,000 seconds
```

A positive result must not be reused past the earliest of:

* five minutes after evaluation;
* `claim.validUntil`;
* `authorityValidUntil`, when present; and
* a shorter target-chain or account-policy validity bound.

The 90-day signature lifetime does not permit a 90-day cache. ENS state and
stateful account authority can change earlier and must be re-evaluated.

## Security Boundary

This method returns `verificationType: "control"` only when:

1. the current exact-name ENS authority approved the exact address record; and
2. the account or account policy selected by the live address approved the same
   claim through its native signing standard.

This prevents another ENS name from copying an address and reusing its proof.
The target signature is bound to the common claim, which includes the ENS name,
record key, address value, authority, method, target, and validity period.

The result does not prove identity, legal ownership, account safety, balance,
or willingness to receive funds. For stateful accounts, it proves control only
under the profile's account-state and freshness rules.

## Open Issues

<Accordion>
  <AccordionItem>
    <AccordionTitle>Should the maximum signed lifetime be 90 days?</AccordionTitle>

    <AccordionContent>
      The current draft proposes a 90-day maximum. A longer lifetime reduces how
      often both signers must renew the proof, but it also leaves old proof material
      usable for longer if the descriptor later points to it again.

      This limit is still subject to discussion. It does not change the five-minute
      maximum for caching a positive verification result.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Which profiles are ready to be frozen?</AccordionTitle>

    <AccordionContent>
      Every row needs one exact target mapping, native procedure for approving the
      32-byte `commonClaimDigest`, signature and key encoding, size limit, and
      positive and negative test vectors. Profiles without two interoperable
      implementations should remain draft and be reported as unsupported by
      production verifiers.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>How should stateful account authority be evaluated?</AccordionTitle>

    <AccordionContent>
      Contract wallets, NEAR access keys, rekeyed accounts, delegated accounts, and
      multisig policies can change after signing. Each profile that accepts mutable
      authority must define the target-chain block, finality rule, maximum block age,
      and which live policy is checked.

      Until those rules are fixed, the profile must support only direct accounts or
      return unsupported.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Which proof URI transports are final?</AccordionTitle>

    <AccordionContent>
      HTTPS and immutable IPFS publication are intended, but the descriptor component
      still needs final, shared retrieval profiles. Each profile must use those exact
      rules; it must not invent redirects, gateway behavior, authentication, or
      scheme fallback.
    </AccordionContent>
  </AccordionItem>
</Accordion>

## Decisions

<Accordion>
  <AccordionItem>
    <AccordionTitle>Why is the profile part of the method identifier?</AccordionTitle>

    <AccordionContent>
      The descriptor and signed claim select the verification algorithm before the
      untrusted proof is read. The proof cannot request a different curve, message
      format, network, or account model.

      A change to one native profile allocates a new method version without changing
      the common account-signature flow.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Why are both signatures required?</AccordionTitle>

    <AccordionContent>
      The target signature alone proves only that an account signed a message. Any ENS
      name could otherwise copy that account and signature. The authority signature
      alone proves only that the ENS authority made a claim about an account.

      Requiring both signatures over the same record-bound claim proves that the
      current ENS authority and the target account approved this exact binding.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Why not define profiles only by curve?</AccordionTitle>

    <AccordionContent>
      A curve is not an account-verification protocol. Ethereum, Bitcoin, TRON, and
      Cosmos commonly use secp256k1 but have different message wrappers, addresses,
      networks, and account rules. Solana and Stellar both use Ed25519 but also use
      different message and account formats.

      The profile therefore selects the complete native signing standard, not only
      the mathematical signature algorithm.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Why link native standards instead of copying them?</AccordionTitle>

    <AccordionContent>
      This method defines the shared claim, target binding, and permitted profile.
      The linked native standard remains authoritative for its cryptographic
      algorithm and native proof validation.

      Each frozen profile must pin an exact standard version and its accepted options.
      A moving link to “latest” is not sufficient.
    </AccordionContent>
  </AccordionItem>

  <AccordionItem>
    <AccordionTitle>Why is a proof key not used?</AccordionTitle>

    <AccordionContent>
      Descriptor `u` already selects one exact proof resource. A proof key is needed
      only when a method derives a shared publication location, such as an HTTPS
      well-known path or DNS owner name.

      Adding another key here would not improve signature validation or prevent proof
      reuse; the signed common claim already binds the proof to one record and target.
    </AccordionContent>
  </AccordionItem>
</Accordion>
