From 806a760b5b7a7abb84c10e99c357e97b9e6db982 Mon Sep 17 00:00:00 2001 From: scaler <240590331+sc4l3r@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:30:51 +0200 Subject: [PATCH 1/2] feat: add keeta caip2, caip10, caip19 --- keeta/README.md | 52 +++++++++++++ keeta/caip10.md | 190 ++++++++++++++++++++++++++++++++++++++++++++++++ keeta/caip19.md | 138 +++++++++++++++++++++++++++++++++++ keeta/caip2.md | 140 +++++++++++++++++++++++++++++++++++ 4 files changed, 520 insertions(+) create mode 100644 keeta/README.md create mode 100644 keeta/caip10.md create mode 100644 keeta/caip19.md create mode 100644 keeta/caip2.md diff --git a/keeta/README.md b/keeta/README.md new file mode 100644 index 00000000..c6549484 --- /dev/null +++ b/keeta/README.md @@ -0,0 +1,52 @@ +--- +namespace-identifier: keeta +title: Keeta +author: ["@sc4l3r"] +discussions-to: https://github.com/ChainAgnostic/namespaces/pull/XXXX +status: Draft +type: Informational +created: 2026-04-27 +requires: ["CAIP-2"] +--- + +# Namespace for Keeta + +[Keeta][keeta-home] is a Delegated Proof of Stake (DPoS) Layer-1 designed for high-throughput asset transfers, real-world asset tokenization, and built-in compliance primitives (KYC, sanctions, ACLs). +The protocol uses a [Directed Acyclic Graph (DAG)][whitepaper] of per-account block chains rather than a single linearly-ordered ledger, which lets independent accounts publish concurrently and lets the network achieve sub-second finality. + +The `keeta` namespace describes both the public Keeta main network and any of its sibling networks -- the test, staging, and dev networks, and any privately-launched **sub-network** (a private instance that runs the same protocol but with isolated transaction visibility and its own validator set). +All networks share the same key-pair format, address encoding, and operation set, so a single Keeta key pair can be used across networks and the only thing that distinguishes one from another in CAIP terms is its `networkId`. + +## Rationale + +Registering the `keeta` namespace enables standard CAIP-2 chain identifiers for every Keeta network distinguished by their numeric `networkId`. +Keeta does not run an EVM or similar and has no smart-contract addresses. +Therefore, CAIP profiles in this namespace focus on identifying networks, accounts (keyed and identifier-style), and tokens, not contracts. + +## Governance + +Keeta uses Delegated Proof of Stake. +Token holders elect representatives, which vote on the validity of vote staples (atomic groups of blocks). +Network-wide policy -- for example, the right to create new tokens or storage accounts -- is expressed as permissions on the [Network Account][accounts], a deterministically-generated identifier account that exists once per network. + +Protocol changes are coordinated by Keeta Token Genesis LLC and the validator set. +The reference implementation is published as the [`@keetanetwork/keetanet-client`][sdk] TypeScript SDK; the protocol specification is described in the [Keeta whitepaper][whitepaper]. + +## References + +- [Keeta home][keeta-home] - public site and ecosystem overview. +- [Keeta whitepaper][whitepaper] - protocol specification, DAG model, consensus, and account model. +- [Keeta documentation][keeta-docs] - developer guide, SDK reference, and anchor framework. +- [Keeta SDK source][sdk] - the canonical reference implementation that defines `NetworkIDs`, address encoding, and key algorithms cited by the CAIP profiles in this namespace. +- [Public network resources][official-links] - wallet, block explorer, and faucet endpoints for the public main and test networks. + +[keeta-home]: https://keeta.com/ +[whitepaper]: https://keeta.com/whitepaper.pdf +[keeta-docs]: https://docs.keeta.com/ +[sdk]: https://github.com/KeetaNetwork/keetanet-client +[official-links]: https://docs.keeta.com/other-documentation/official-links +[accounts]: https://docs.keeta.com/components/accounts + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/keeta/caip10.md b/keeta/caip10.md new file mode 100644 index 00000000..4568eaff --- /dev/null +++ b/keeta/caip10.md @@ -0,0 +1,190 @@ +--- +namespace-identifier: keeta-caip10 +title: Keeta - Account ID Specification +author: ["@sc4l3r"] +discussions-to: https://github.com/ChainAgnostic/namespaces/pull/XXXX +status: Draft +type: Standard +created: 2026-04-27 +requires: ["CAIP-2", "CAIP-10"] +--- + +# CAIP-10 + +_For context, see the [CAIP-10][] specification._ + +## Introduction + +Every [account][accounts] on a Keeta network -- whether it is a key-pair-backed account (ECDSA secp256k1, ECDSA secp256r1, or Ed25519), a token, a storage account, a network account, or a multisig -- is addressed by a single base32-encoded **public key string** with the prefix `keeta_`. + +The body of the address self-identifies its account type via its first base32 characters (e.g. `aa` is a secp256k1 keyed account, `am` is a token, `aq` is a storage account, `ai` is a Network Account), and ends in a checksum. + +This profile uses the full canonical `keeta_...` form as the [CAIP-10][] `account_address`. + +## Specification + +### Semantics + +The inputs are: + +- A Keeta `networkId` (defined in the [Keeta CAIP-2 Profile][CAIP-2 Profile]) identifying which network the account lives on. +- A Keeta canonical address string, of the form `keeta_`, returned by the SDK as `account.publicKeyString.toString()` (or accepted by `Account.fromPublicKeyString()`). + +The body of a Keeta address is the [RFC 4648][rfc4648] base32 encoding of a byte sequence that encodes an account-type indicator, the public key material, and a truncated hash checksum. +RFC 4648 base32 is case-insensitive; the canonical form on Keeta and in this profile is lowercase with no padding (see [Canonicalization](#canonicalization)). +The exact byte layout may change in future protocol versions; the SDK is the authoritative source for encoding and decoding (see [Resolution Mechanics](#resolution-mechanics)). + +The account-type indicator occupies the high bits of the encoded body, so the characters immediately following `keeta_` reflect the account's key algorithm and identifier type. +The following two-character prefixes are reserved by the SDK for the account types listed below as of `keetanet-client` version `0.16.1`: + +| First chars (after `keeta_`) | Account type | +| :--------------------------- | :------------------------------------- | +| `aa`, `ab`, `ac`, `ad` | Keyed account, ECDSA secp256k1 | +| `ae`, `af`, `ag`, `ah` | Keyed account, Ed25519 | +| `ay`, `az`, `a2`, `a3` | Keyed account, ECDSA secp256r1 (P-256) | +| `ai`, `aj`, `ak`, `al` | Network Account (one per network) | +| `am`, `an`, `ao`, `ap` | Token account | +| `aq`, `ar`, `as`, `at` | Storage account | +| `a4`, `a5`, `a6`, `a7` | Multisig identifier account | + +This table is illustrative and reflects the current SDK encoding; it is **not** normative for type discrimination. +Implementations MUST NOT switch on these prefixes to determine an account's type; instead, parse the address with a Keeta SDK and use `account.isToken()`, `account.isStorage()`, `account.isNetwork()`, and `account.isMultisig()` (see [Resolution Mechanics](#resolution-mechanics)). + +#### Canonicalization + +A Keeta address is **case-insensitive**: per RFC 4648, two base32-encoded bodies that differ only in letter case decode to the same byte sequence and therefore identify the same account. +For [CAIP-10][] purposes the canonical form is **all-lowercase**, and producers MUST lowercase the body before emitting a CAIP-10 string. +Consumers MUST treat differently-cased CAIP-10 strings whose lowercased forms are equal as identifiers for the same account, and SHOULD lowercase incoming CAIP-10 strings before using them as cache or lookup keys. +The literal prefix `keeta_` is always lowercase. + +### Syntax + +A Keeta [CAIP-10][] account identifier MUST take the form: + +``` +keeta::
+``` + +Where: + +- `keeta` is the namespace. +- `` is a CAIP-2 reference for a Keeta network as defined by the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +- `
` is the canonical Keeta public key string, **including** its `keeta_` prefix. + +The `
` segment MUST match: + +``` +^keeta_[a-z2-7]+$ +``` + +A regex match is necessary but not sufficient for validity as it does not verify the type indicator, checksum, or body length. +Implementations MUST validate addresses using the Keeta SDK or by following the steps in [Resolution Mechanics](#resolution-mechanics). + +### Resolution Mechanics + +To validate a Keeta CAIP-10 identifier: + +1. Split on `:` and verify the namespace is `keeta`. +2. Validate `` against the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +3. Lowercase the body of `
` (the part after `keeta_`) to obtain its canonical form; see [Canonicalization](#canonicalization). +4. Decode `
` using the Keeta public-key-string codec; verify the type byte and checksum. +5. Optionally, query a node on the indicated network to confirm the account exists and to retrieve its info: + +```ts +import * as KeetaNet from "@keetanetwork/keetanet-client"; + +const account = KeetaNet.lib.Account.fromPublicKeyString( + "keeta_aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi", +); +const client = KeetaNet.Client.fromNetwork("test"); +const { info } = await client.getAccountInfo(account); +``` + +Implementations SHOULD validate the checksum by round-tripping through `Account.fromPublicKeyString()` before treating the identifier as well-formed. + +Implementations SHOULD use the methods of a Keeta SDK (e.g. `account.isToken()`, `account.isStorage()`) to determine the type of an account. + +Note that account _existence_ is not required for a CAIP-10 string to be valid -- Keeta accounts come into existence when their first block is published, and a freshly-derived keyed address may be a perfectly valid recipient before any block has ever been published to its chain. + +## Rationale + +Keeta addresses are designed to be self-describing: the `keeta_` prefix and the algorithm-tag prefix are part of the canonical form everywhere -- wallet UIs, block explorers, the SDK, and on-chain metadata. +Stripping `keeta_` to "save space" inside a CAIP-10 string would force every consumer to reattach it before handing the address to the SDK, and would cause CAIP-10 strings to disagree with how the same address is rendered everywhere else in the ecosystem. + +The resulting form fits comfortably within the [CAIP-10][] `account_address` length budget of 128 characters: the currently longest possible Keeta address is `keeta_` (6 chars) plus a 63-char body for compressed ECDSA secp256k1 / secp256r1 keys = 69 chars total; Ed25519 and identifier accounts are 67 chars total. + +### Backwards Compatibility + +This is the first [CAIP-10][] specification for the `keeta` namespace, so there are no legacy identifiers to support. + +## Test Cases + +Valid (test network, `networkId = 1413829460`): + +``` +# Keyed secp256k1 account +keeta:1413829460:keeta_aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi +``` + +Valid forms on other networks: + +``` +# Mainnet, keyed Ed25519 account +keeta:21378:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo + +# Mainnet token account +keeta:21378:keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg + +# Mainnet storage account +keeta:21378:keeta_aqltdal4rshtky5iehd765y3mdjkcmku5d4ulo5fgonzqrxulwepnogq33mle + +# Mainnet Network Account +keeta:21378:keeta_alwerxoezkupzhifvpo5yvoazlsdqaweov66mokhq7xl4h5ow36v5xu6ek3js +``` + +Non-canonical (decode to a valid account but MUST be lowercased before being used as a CAIP-10 identifier; see [Canonicalization](#canonicalization)): + +``` +# Uppercase body — same account as the lowercase form +keeta:21378:keeta_AE23CU2WIMBYUVIB6P77AW7ZCHGJMFIA3FAUUHY6MH44XA57BUOKKHIFPSNXO +``` + +Invalid: + +``` +# Missing keeta_ prefix on the address +keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo + +# Wrong namespace casing +Keeta:21378:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo + +# Hex networkId (see Keeta CAIP-2 Profile) +keeta:0x5382:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo +``` + +## Additional Considerations + +### Security + +Keeta addresses encode their algorithm and type in the leading body characters and end in a checksum, so single-character typos and accidental cross-pasting between account types (e.g. sending to a token address instead of a keyed account) are detectable on the client side without a network round-trip. +Implementations SHOULD perform this check before signing a transaction that references a counterparty CAIP-10. + +The same key pair derives to the same `keeta_...` address on every Keeta network, but the `` segment of a CAIP-10 string is not redundant: the on-chain _state_ of an account (balances, permissions, certificates, history) is per-network, and applications MUST treat `keeta:21378:keeta_...` and `keeta:1413829460:keeta_...` as different accounts even when the address bodies are identical. + +## References + +- Keeta [accounts] - account types, identifier accounts, and address derivation. +- Keeta [SDK source][sdk] - `lib/account.d.ts` defines the `keeta_` prefix, the algorithm tag bytes, and the `PublicKeyString` codec. +- [Keeta CAIP-2 Profile][CAIP-2 Profile] - the network-id portion of the identifier. +- [RFC 4648][rfc4648] - the base32 alphabet used for the address body. + +[CAIP-2 Profile]: ./caip2.md +[accounts]: https://docs.keeta.com/components/accounts +[sdk]: https://github.com/KeetaNetwork/keetanet-client +[rfc4648]: https://datatracker.ietf.org/doc/html/rfc4648 +[CAIP-2]: https://chainagnostic.org/CAIPs/caip-2 +[CAIP-10]: https://chainagnostic.org/CAIPs/caip-10 + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/keeta/caip19.md b/keeta/caip19.md new file mode 100644 index 00000000..56260e7e --- /dev/null +++ b/keeta/caip19.md @@ -0,0 +1,138 @@ +--- +namespace-identifier: keeta-caip19 +title: Keeta - Asset Type and Asset ID Specification +author: ["@sc4l3r"] +discussions-to: https://github.com/ChainAgnostic/namespaces/pull/XXXX +status: Draft +type: Standard +created: 2026-04-27 +requires: ["CAIP-2", "CAIP-19"] +--- + +# CAIP-19 + +_For context, see the [CAIP-19][] specification._ + +## Introduction + +Keeta has a single, native, first-class token model: every transferable asset on a Keeta network -- including the network's own base token -- is a [token account][accounts] with its own `keeta_...` address. +There is no separate token-contract registry, no ERC-20-style metadata contract, and no parallel address space for NFTs: an NFT on Keeta is simply a token account whose total supply is `1` and whose decimal count is `0`. + +Because of this, every Keeta asset is fully identified by: + +1. The CAIP-2 reference for its network (defined by the [Keeta CAIP-2 Profile][CAIP-2 Profile]). +2. The token account's address (defined in the same form as the [Keeta CAIP-10 Profile][CAIP-10 Profile]). + +## Specification + +### Semantics + +The inputs are: + +- A Keeta `networkId` identifying the network the asset lives on. +- An asset-namespace tag drawn from the fixed set (`token`, `nft`). +- A Keeta token-account address (see [Keeta CAIP-10 Profile][CAIP-10 Profile]). + +The asset-namespace tag carries **no on-chain meaning** -- Keeta itself does not distinguish "fungible" from "non-fungible" at the protocol level -- and is purely an interoperability hint to consumers. +A CAIP-19 string with `nft:` referring to a token whose supply is greater than `1` SHOULD be treated as malformed by validators that have access to the chain. + +| `asset_namespace` | Use | Equivalent in other namespaces | +| :---------------- | :------------------------------------------------------------------------------------------------ | :--------------------------------------------- | +| `token` | Any fungible token on a Keeta network, including the network's base token. | `eip155:1/erc20:0x...`, `solana:.../token:...` | +| `nft` | A non-fungible token. Implementations SHOULD only use `nft` when the token's supply is exactly 1. | `eip155:1/erc721:0x...`, `solana:.../nft:...` | + +The base token of a Keeta network is just another token account: it is referenced via the `token` asset namespace using the address returned by `Account.generateBaseAddresses(networkId).baseToken`. +Mainnet KTA is also registered in [SLIP-0044][] as coin type `8887` for use in HD-wallet derivation paths and similar SLIP-0044-indexed contexts; this profile does not surface SLIP-0044 in [CAIP-19][] form, since on Keeta the base token has a first-class on-chain address that already serves as a canonical [CAIP-19][] reference, and only mainnet has a SLIP-0044 entry. + +### Syntax + +A Keeta [CAIP-19][] **asset type** identifier MUST take the form: + +``` +keeta:/:
+``` + +A Keeta [CAIP-19][] **asset ID** identifier (a specific NFT) MUST take the form: + +``` +keeta:/:
/ +``` + +Where: + +- `keeta` is the namespace. +- `` is a CAIP-2 reference for a Keeta network as defined by the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +- `` is `token` or `nft` (see the table above). +- `
` is the canonical Keeta token-account address, **including** its `keeta_` prefix (see [Keeta CAIP-10 Profile][CAIP-10 Profile] for validation). +- ``, when present, is the decimal index of an individual unit within a multi-unit token account; see [NFT collections](#nft-collections) below. It MUST match the [CAIP-19][] `token_id` character set, `^[-.%a-zA-Z0-9]{1,78}$`. + +#### NFT collections + +Multi-unit NFT collection identity is not yet standardized in the Keeta protocol. +The `` form is documented here because [CAIP-19][] permits it, but this profile does not define its semantics. +Producers SHOULD omit the `` segment; a future revision of this profile will specify its use once the protocol standardizes collection identity (e.g. for fractional NFTs that adopt sub-unit identity at the application layer). + +### Resolution Mechanics + +To validate a Keeta CAIP-19 identifier: + +1. Split on `/` and `:` and verify the namespace is `keeta` and the asset-namespace is `token` or `nft`. +2. Validate `` against the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +3. Validate `
` against the same codec used by the [Keeta CAIP-10 Profile][CAIP-10 Profile] (type must be `TOKEN`; checksum must verify). +4. For `nft`, consumers SHOULD query a node on the indicated network to confirm the token's `supply == 1` and `decimalPlaces == 0` before treating the identifier as a non-fungible asset, and SHOULD treat any identifier that fails this check as malformed. + +## Rationale + +Keeta uses a single, unified address space for every transferable asset, including the base token: there is no separate native-coin concept at the protocol level. +This profile reflects that by using `token` for every fungible asset and `nft` only as an interoperability hint for token accounts whose supply is `1` and decimals are `0`, mirroring the approach taken by the Solana namespace, which has a similarly unified address space for fungible and non-fungible assets. + +### Backwards Compatibility + +This is the first [CAIP-19][] specification for the `keeta` namespace, so there are no legacy identifiers to support. + +## Test Cases + +Base token (KTA) of the Keeta main network, referenced by its on-chain token-account address: + +``` +keeta:21378/token:keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg +``` + +Any other fungible token on mainnet uses the same form, with the address of the relevant token account. + +NFT on mainnet (total supply == 1, decimals == 0): + +``` +keeta:21378/nft:keeta_amob7pxzhexqych4g56bmmtovdgwr6kljloyzkyb34k37jntj24doaqfbx4xk +``` + +Invalid: + +``` +# Non-token address used with a token asset namespace (storage prefix `aq`) +keeta:21378/token:keeta_aqltdal4rshtky5iehd765y3mdjkcmku5d4ulo5fgonzqrxulwepnogq33mle + +# Missing keeta_ prefix +keeta:21378/token:anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg + +# Hex networkId (see Keeta CAIP-2 Profile) +keeta:0x5382/token:keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg +``` + +## References + +- Keeta [accounts] - defines token accounts and the unified address space for fungible/non-fungible assets. +- Keeta [SDK source][sdk] - `Account.AccountKeyAlgorithm.TOKEN` and `generateBaseAddresses(networkId)` define how a token account's address is constructed. +- [Keeta CAIP-2 Profile][CAIP-2 Profile] - the network portion of the identifier. +- [Keeta CAIP-10 Profile][CAIP-10 Profile] - the address syntax reused for the `
` segment. + +[CAIP-2 Profile]: ./caip2.md +[CAIP-10 Profile]: ./caip10.md +[accounts]: https://docs.keeta.com/components/accounts +[sdk]: https://github.com/KeetaNetwork/keetanet-client +[SLIP-0044]: https://github.com/satoshilabs/slips/blob/master/slip-0044.md +[CAIP-19]: https://chainagnostic.org/CAIPs/caip-19 + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/keeta/caip2.md b/keeta/caip2.md new file mode 100644 index 00000000..1827cece --- /dev/null +++ b/keeta/caip2.md @@ -0,0 +1,140 @@ +--- +namespace-identifier: keeta-caip2 +title: Keeta - Blockchain ID Specification +author: ["@sc4l3r"] +discussions-to: https://github.com/ChainAgnostic/namespaces/pull/XXXX +status: Draft +type: Standard +created: 2026-04-27 +requires: CAIP-2 +--- + +# CAIP-2 + +_For context, see the [CAIP-2][] specification._ + +## Introduction + +A Keeta network -- main, test, staging, dev, or any privately-launched network -- is uniquely identified by a single non-negative integer called its `networkId`. +A privately-launched network is a private instance that runs the same Keeta protocol but with isolated transaction visibility and its own validator set; it shares the key-pair format and address encoding with the public networks but is distinguished from them solely by its `networkId`. +The same `networkId` is used by the protocol to deterministically derive the network's [Network Account][accounts] and base token address, so two networks with the same `networkId` are by construction the same network. + +This profile maps that `networkId` to a [CAIP-2][] reference. + +## Specification + +### Semantics + +The single input is a Keeta `networkId`: a non-negative integer that the protocol treats as a `bigint`. +Every Keeta network has exactly one, baked into the network's deterministic [Network Account][accounts] and base token address. + +The four well-known networks shipped with the SDK ([source][sdk]) are: + +| Network alias | `networkId` (decimal) | `networkId` (hex) | +| :------------ | --------------------: | :---------------- | +| `main` | `21378` | `0x5382` | +| `test` | `1413829460` | `0x54455354` | +| `staging` | `5472769` | `0x538201` | +| `dev` | `4474198` | `0x444556` | + +Privately-launched networks have their own arbitrary `networkId` values chosen by the operator. +The four `networkId` values listed above are reserved for the public networks they identify and MUST NOT be reused by private networks. + +### Syntax + +The CAIP-2 namespace is `keeta`. The CAIP-2 `reference` MUST be the network's `networkId`, rendered as a decimal integer with no leading zeros and no `0x` prefix: + +``` +keeta: +``` + +The `reference` MUST match: + +``` +^(0|[1-9][0-9]{0,31})$ +``` + +This is the full 32-digit cap allowed by the [CAIP-2][] reference field. + +### Resolution Mechanics + +The `networkId` of a connected network can be obtained via the `@keetanetwork/keetanet-client` SDK: + +```ts +import * as KeetaNet from "@keetanetwork/keetanet-client"; + +const client = KeetaNet.UserClient.fromNetwork("test", null); +const networkId = client.network; // bigint +const caip2 = `keeta:${networkId.toString(10)}`; +``` + +To resolve a [CAIP-2][] string back to a network alias, parse the `reference` segment as a `bigint` and pass it through the SDK's `getNetworkAlias`: + +```ts +import * as KeetaNet from "@keetanetwork/keetanet-client"; + +const reference = "1413829460"; // from "keeta:1413829460" +const alias = KeetaNet.Client.Config.getNetworkAlias(BigInt(reference)); // "test" +``` + +For a Keeta address known to be a [Network Account][accounts], the `networkId` is the value the address was generated from via `Account.generateNetworkAddress(networkId)`. +Implementations that need to validate a [CAIP-2][] string against a live network SHOULD compare the address returned by `userClient.networkAddress` to the address recomputed from the candidate `networkId`. + +## Rationale + +Keeta defines its `networkId` as a `bigint`. + +Decimal was chosen for the [CAIP-2][] `reference` because: + +- It matches the integer nature of the underlying value. +- It is consistent with other numeric-id [CAIP-2][] namespaces such as `eip155`. +- It avoids ambiguity around case and `0x` prefixing. + +### Backwards Compatibility + +This is the first [CAIP-2][] specification for the `keeta` namespace, so there are no legacy identifiers to support. + +## Test Cases + +``` +# Main network +keeta:21378 + +# Test network +keeta:1413829460 + +# Staging network +keeta:5472769 + +# Dev network +keeta:4474198 + +# Example private network with an arbitrary numeric id +keeta:9000001 +``` + +The four well-known networks above correspond exactly to the entries in the SDK's `NetworkIDs` table (`main`, `test`, `staging`, `dev`). + +Invalid: + +``` +keeta:0x5382 # hex form is not permitted +keeta:021378 # leading zeros not permitted +keeta: # empty reference +keeta:main # symbolic aliases are not permitted +``` + +## References + +- Keeta [accounts] - describes the Network Account and how it is derived from `networkId`. +- Keeta [SDK source][sdk] - `config/index.ts` defines `NetworkIDs` and `getNetworkAlias()`. +- Keeta [whitepaper] - protocol overview. + +[accounts]: https://docs.keeta.com/components/accounts +[sdk]: https://github.com/KeetaNetwork/keetanet-client +[whitepaper]: https://keeta.com/whitepaper.pdf +[CAIP-2]: https://chainagnostic.org/CAIPs/caip-2 + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 6dae98908eee03cbd2d55b6b586e7acc67c7f0aa Mon Sep 17 00:00:00 2001 From: xescure Date: Fri, 11 Sep 2026 16:28:20 +0200 Subject: [PATCH 2/2] caip10: coherence + narrative + feedback pass --- keeta/caip10.md | 153 ++++++++++++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 58 deletions(-) diff --git a/keeta/caip10.md b/keeta/caip10.md index 4568eaff..58766c25 100644 --- a/keeta/caip10.md +++ b/keeta/caip10.md @@ -15,11 +15,17 @@ _For context, see the [CAIP-10][] specification._ ## Introduction -Every [account][accounts] on a Keeta network -- whether it is a key-pair-backed account (ECDSA secp256k1, ECDSA secp256r1, or Ed25519), a token, a storage account, a network account, or a multisig -- is addressed by a single base32-encoded **public key string** with the prefix `keeta_`. +Every [account][accounts] on a Keeta network -- whether it is a key-pair-backed account (ECDSA secp256k1/r1, or Ed25519), a token, a storage account, a network account, or a multisig -- is addressed from a single shared address space. +A **keyed account** holds a private key and signs its own blocks. +A **generated account**, also called an identifier account -- a token, storage account, network account, or multisig -- has no private key, and its blocks are signed by a keyed account holding permission over it. +A Keeta address is the account's public key itself with a type byte and a five-byte checksum, all of which is then encoded as base32. +The address alone reveals the account type and signature algorithm. -The body of the address self-identifies its account type via its first base32 characters (e.g. `aa` is a secp256k1 keyed account, `am` is a token, `aq` is a storage account, `ai` is a Network Account), and ends in a checksum. +The same key pair produces the same address on every Keeta network, but account state is per network, so `keeta:21378:
` and `keeta:1413829460:
` are different accounts. +Generated accounts exist only on the network where they were created (see [Semantics](#semantics)). -This profile uses the full canonical `keeta_...` form as the [CAIP-10][] `account_address`. +Keeta renders every address with the display prefix `keeta_`. +This profile uses the **bare base32 body**, without the prefix, as the [CAIP-10][] `account_address`. ## Specification @@ -27,57 +33,58 @@ This profile uses the full canonical `keeta_...` form as the [CAIP-10][] `accoun The inputs are: -- A Keeta `networkId` (defined in the [Keeta CAIP-2 Profile][CAIP-2 Profile]) identifying which network the account lives on. -- A Keeta canonical address string, of the form `keeta_`, returned by the SDK as `account.publicKeyString.toString()` (or accepted by `Account.fromPublicKeyString()`). +- A Keeta network identifier, as defined in the [Keeta CAIP-2 Profile][CAIP-2 Profile], identifying which network the account lives on. +- A Keeta address as rendered by the SDK, `keeta_`, returned by `account.publicKeyString.toString()` and accepted by `Account.fromPublicKeyString()`. -The body of a Keeta address is the [RFC 4648][rfc4648] base32 encoding of a byte sequence that encodes an account-type indicator, the public key material, and a truncated hash checksum. -RFC 4648 base32 is case-insensitive; the canonical form on Keeta and in this profile is lowercase with no padding (see [Canonicalization](#canonicalization)). -The exact byte layout may change in future protocol versions; the SDK is the authoritative source for encoding and decoding (see [Resolution Mechanics](#resolution-mechanics)). +Only the body is used; the display prefix is discarded (see [Syntax](#syntax)). -The account-type indicator occupies the high bits of the encoded body, so the characters immediately following `keeta_` reflect the account's key algorithm and identifier type. -The following two-character prefixes are reserved by the SDK for the account types listed below as of `keetanet-client` version `0.16.1`: +The body is the [RFC 4648][rfc4648] base32 encoding, lowercase and without padding, of the following byte sequence: -| First chars (after `keeta_`) | Account type | -| :--------------------------- | :------------------------------------- | -| `aa`, `ab`, `ac`, `ad` | Keyed account, ECDSA secp256k1 | -| `ae`, `af`, `ag`, `ah` | Keyed account, Ed25519 | -| `ay`, `az`, `a2`, `a3` | Keyed account, ECDSA secp256r1 (P-256) | -| `ai`, `aj`, `ak`, `al` | Network Account (one per network) | -| `am`, `an`, `ao`, `ap` | Token account | -| `aq`, `ar`, `as`, `at` | Storage account | -| `a4`, `a5`, `a6`, `a7` | Multisig identifier account | +``` +type || key || SHA3-256(type || key)[0..5] +``` + +- `type` is one byte identifying the account kind: `0` ECDSA secp256k1, `1` Ed25519, `2` network account, `3` token, `4` storage account, `6` ECDSA secp256r1, `7` multisig. +- `key` is the 33-byte compressed public key for ECDSA accounts, the 32-byte public key for Ed25519 accounts, or a 32-byte identifier for generated accounts. +- The last five bytes are a checksum over the preceding bytes. -This table is illustrative and reflects the current SDK encoding; it is **not** normative for type discrimination. -Implementations MUST NOT switch on these prefixes to determine an account's type; instead, parse the address with a Keeta SDK and use `account.isToken()`, `account.isStorage()`, `account.isNetwork()`, and `account.isMultisig()` (see [Resolution Mechanics](#resolution-mechanics)). +As of `keetanet-client` version `0.18.4` the body is 63 characters for ECDSA accounts and 61 characters for all others. +Keeta may introduce further signing algorithms; the SDK is the authoritative source for the set of type bytes and for encoding and decoding (see [Resolution Mechanics](#resolution-mechanics)). + +A keyed address is a valid recipient on any Keeta network before any block has been published to its chain. +A generated account is created by a `CREATE_IDENTIFIER` operation, and its identifier is derived from the creating account, the hash of the creating account's previous block on that network, and the index of the operation within that block. +The network account is derived from the network identifier alone, and the base token from the network account. #### Canonicalization A Keeta address is **case-insensitive**: per RFC 4648, two base32-encoded bodies that differ only in letter case decode to the same byte sequence and therefore identify the same account. For [CAIP-10][] purposes the canonical form is **all-lowercase**, and producers MUST lowercase the body before emitting a CAIP-10 string. Consumers MUST treat differently-cased CAIP-10 strings whose lowercased forms are equal as identifiers for the same account, and SHOULD lowercase incoming CAIP-10 strings before using them as cache or lookup keys. -The literal prefix `keeta_` is always lowercase. ### Syntax A Keeta [CAIP-10][] account identifier MUST take the form: ``` -keeta::
+keeta::
``` Where: - `keeta` is the namespace. -- `` is a CAIP-2 reference for a Keeta network as defined by the [Keeta CAIP-2 Profile][CAIP-2 Profile]. -- `
` is the canonical Keeta public key string, **including** its `keeta_` prefix. +- `` is a Keeta network identifier as defined by the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +- `
` is the body of the Keeta public key string, **without** the `keeta_` display prefix, in lowercase. + +To produce `
` from an address as rendered by the SDK, remove the display prefix up to and including the `_` and lowercase the remainder. +To pass `
` to the SDK, prepend `keeta_`. The `
` segment MUST match: ``` -^keeta_[a-z2-7]+$ +^[a-z2-7]+$ ``` -A regex match is necessary but not sufficient for validity as it does not verify the type indicator, checksum, or body length. +A regex match is necessary but not sufficient for checking address validity as it does not verify the type byte, the checksum, or the length. Implementations MUST validate addresses using the Keeta SDK or by following the steps in [Resolution Mechanics](#resolution-mechanics). ### Resolution Mechanics @@ -85,17 +92,16 @@ Implementations MUST validate addresses using the Keeta SDK or by following the To validate a Keeta CAIP-10 identifier: 1. Split on `:` and verify the namespace is `keeta`. -2. Validate `` against the [Keeta CAIP-2 Profile][CAIP-2 Profile]. -3. Lowercase the body of `
` (the part after `keeta_`) to obtain its canonical form; see [Canonicalization](#canonicalization). -4. Decode `
` using the Keeta public-key-string codec; verify the type byte and checksum. +2. Validate `` against the [Keeta CAIP-2 Profile][CAIP-2 Profile]. +3. Lowercase `
` to obtain its canonical form; see [Canonicalization](#canonicalization). +4. Prepend `keeta_` and decode the result using the Keeta public-key-string codec; verify the type byte and checksum. 5. Optionally, query a node on the indicated network to confirm the account exists and to retrieve its info: ```ts import * as KeetaNet from "@keetanetwork/keetanet-client"; -const account = KeetaNet.lib.Account.fromPublicKeyString( - "keeta_aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi", -); +const address = "aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi"; +const account = KeetaNet.lib.Account.fromPublicKeyString(`keeta_${address}`); const client = KeetaNet.Client.fromNetwork("test"); const { info } = await client.getAccountInfo(account); ``` @@ -104,14 +110,15 @@ Implementations SHOULD validate the checksum by round-tripping through `Account. Implementations SHOULD use the methods of a Keeta SDK (e.g. `account.isToken()`, `account.isStorage()`) to determine the type of an account. -Note that account _existence_ is not required for a CAIP-10 string to be valid -- Keeta accounts come into existence when their first block is published, and a freshly-derived keyed address may be a perfectly valid recipient before any block has ever been published to its chain. +Note that account _existence_ is not required for a CAIP-10 string to be valid; a keyed address may be a valid recipient before any block has been published to its chain (see [Semantics](#semantics)). ## Rationale -Keeta addresses are designed to be self-describing: the `keeta_` prefix and the algorithm-tag prefix are part of the canonical form everywhere -- wallet UIs, block explorers, the SDK, and on-chain metadata. -Stripping `keeta_` to "save space" inside a CAIP-10 string would force every consumer to reattach it before handing the address to the SDK, and would cause CAIP-10 strings to disagree with how the same address is rendered everywhere else in the ecosystem. +The `keeta_` prefix is omitted because `_` is not a permitted character in a [CAIP-10][] `account_address`, and because the `keeta:` namespace segment already identifies the ecosystem. +The prefix is not part of the encoded bytes or the checksum, so the body alone identifies the account. -The resulting form fits comfortably within the [CAIP-10][] `account_address` length budget of 128 characters: the currently longest possible Keeta address is `keeta_` (6 chars) plus a 63-char body for compressed ECDSA secp256k1 / secp256r1 keys = 69 chars total; Ed25519 and identifier accounts are 67 chars total. +The resulting form fits comfortably within the [CAIP-10][] `account_address` length budget of 128 characters: the body is currently 63 characters for compressed ECDSA secp256k1/r1 keys and 61 characters for Ed25519 and generated accounts. +The syntax does not fix these lengths, so a signing algorithm added to Keeta is usable under this profile as soon as the SDK supports it; only implementations that handle accounts of the new kind need to update. ### Backwards Compatibility @@ -119,68 +126,98 @@ This is the first [CAIP-10][] specification for the `keeta` namespace, so there ## Test Cases -Valid (test network, `networkId = 1413829460`): +Valid (test network, `1413829460`): ``` # Keyed secp256k1 account -keeta:1413829460:keeta_aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi +keeta:1413829460:aabfo65nbz4toez4ouzit3ej5elpcjnatv6p6vxtgj5gj4x4cbs3nkytnpniyhi ``` Valid forms on other networks: ``` # Mainnet, keyed Ed25519 account -keeta:21378:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo +keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo + +# Mainnet, keyed secp256r1 account +keeta:21378:aybzhzcxweiencq2glio3sj6rs5g2sb7qcgmvrumi2y3dmt4sqqvg2zders2ggy # Mainnet token account -keeta:21378:keeta_anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg +keeta:21378:anqdilpazdekdu4acw65fj7smltcp26wbrildkqtszqvverljpwpezmd44ssg # Mainnet storage account -keeta:21378:keeta_aqltdal4rshtky5iehd765y3mdjkcmku5d4ulo5fgonzqrxulwepnogq33mle +keeta:21378:aqltdal4rshtky5iehd765y3mdjkcmku5d4ulo5fgonzqrxulwepnogq33mle + +# Mainnet network account +keeta:21378:alwerxoezkupzhifvpo5yvoazlsdqaweov66mokhq7xl4h5ow36v5xu6ek3js -# Mainnet Network Account -keeta:21378:keeta_alwerxoezkupzhifvpo5yvoazlsdqaweov66mokhq7xl4h5ow36v5xu6ek3js +# Mainnet multisig account +keeta:21378:a6wiuzcmz4lx3tenp5p24gs76epchg5i22xdkz4r3egopog47onnhhokvqjam ``` Non-canonical (decode to a valid account but MUST be lowercased before being used as a CAIP-10 identifier; see [Canonicalization](#canonicalization)): ``` -# Uppercase body — same account as the lowercase form -keeta:21378:keeta_AE23CU2WIMBYUVIB6P77AW7ZCHGJMFIA3FAUUHY6MH44XA57BUOKKHIFPSNXO +# Uppercase body -- same account as the lowercase form +keeta:21378:AE23CU2WIMBYUVIB6P77AW7ZCHGJMFIA3FAUUHY6MH44XA57BUOKKHIFPSNXO ``` Invalid: ``` -# Missing keeta_ prefix on the address -keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo +# Display prefix included in the address +keeta:21378:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo # Wrong namespace casing -Keeta:21378:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo +Keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo + +# Hex network identifier (see Keeta CAIP-2 Profile) +keeta:0x5382:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo -# Hex networkId (see Keeta CAIP-2 Profile) -keeta:0x5382:keeta_ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxo +# Invalid checksum (last character altered) +keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnxa + +# Wrong length (one character dropped) +keeta:21378:ae23cu2wimbyuvib6p77aw7zchgjmfia3fauuhy6mh44xa57buokkhifpsnx ``` ## Additional Considerations +### Account type prefixes + +The type byte occupies the high bits of the encoded body, so the first two characters of the body reflect the account kind. +As of `keetanet-client` version `0.18.4`: + +| First two characters | Account type | +| :--------------------- | :------------------------------- | +| `aa`, `ab`, `ac`, `ad` | Keyed account, ECDSA secp256k1 | +| `ae`, `af`, `ag`, `ah` | Keyed account, Ed25519 | +| `ay`, `az`, `a2`, `a3` | Keyed account, ECDSA secp256r1 | +| `ai`, `aj`, `ak`, `al` | Network account | +| `am`, `an`, `ao`, `ap` | Token account | +| `aq`, `ar`, `as`, `at` | Storage account | +| `a4`, `a5`, `a6`, `a7` | Multisig account | + +This table is illustrative and is **not** normative for type discrimination. +Implementations MUST NOT switch on these prefixes to determine an account's type; instead, parse the address with a Keeta SDK and use `account.isToken()`, `account.isStorage()`, `account.isNetwork()`, and `account.isMultisig()`. + ### Security -Keeta addresses encode their algorithm and type in the leading body characters and end in a checksum, so single-character typos and accidental cross-pasting between account types (e.g. sending to a token address instead of a keyed account) are detectable on the client side without a network round-trip. +Keeta addresses encode their type and end in a checksum, so single-character typos and accidental cross-pasting between account types (e.g. sending to a token address instead of a keyed account) are detectable on the client side without a network round-trip. Implementations SHOULD perform this check before signing a transaction that references a counterparty CAIP-10. -The same key pair derives to the same `keeta_...` address on every Keeta network, but the `` segment of a CAIP-10 string is not redundant: the on-chain _state_ of an account (balances, permissions, certificates, history) is per-network, and applications MUST treat `keeta:21378:keeta_...` and `keeta:1413829460:keeta_...` as different accounts even when the address bodies are identical. - ## References -- Keeta [accounts] - account types, identifier accounts, and address derivation. -- Keeta [SDK source][sdk] - `lib/account.d.ts` defines the `keeta_` prefix, the algorithm tag bytes, and the `PublicKeyString` codec. -- [Keeta CAIP-2 Profile][CAIP-2 Profile] - the network-id portion of the identifier. +- Keeta [accounts] - account types, generated accounts, and address derivation. +- Keeta [digital signatures][signatures] - the signing algorithms supported by the network. +- Keeta [SDK package][sdk] - `lib/account.d.ts` declares the type bytes and the `PublicKeyString` codec; the reference for encoding and decoding. +- [Keeta CAIP-2 Profile][CAIP-2 Profile] - the network portion of the identifier. - [RFC 4648][rfc4648] - the base32 alphabet used for the address body. [CAIP-2 Profile]: ./caip2.md [accounts]: https://docs.keeta.com/components/accounts -[sdk]: https://github.com/KeetaNetwork/keetanet-client +[signatures]: https://docs.keeta.com/security/digital-signatures +[sdk]: https://www.npmjs.com/package/@keetanetwork/keetanet-client [rfc4648]: https://datatracker.ietf.org/doc/html/rfc4648 [CAIP-2]: https://chainagnostic.org/CAIPs/caip-2 [CAIP-10]: https://chainagnostic.org/CAIPs/caip-10