diff --git a/api/rpc/contracts.mdx b/api/rpc/contracts.mdx
index 17b9618d2e1..e897984a247 100644
--- a/api/rpc/contracts.mdx
+++ b/api/rpc/contracts.mdx
@@ -12,6 +12,8 @@ The RPC API enables you to view details about accounts and contracts as well as
|--------|---------|
| [`view_account`](#view-account) | Get basic account information |
| [`view_account_changes`](#view-account-changes) | Monitor account state changes |
+| [`view_access_key`](#view-access-key) | Get the nonce and permissions of a single access key |
+| [`view_access_key_list`](#view-access-key-list) | List all access keys on an account |
| [`view_code`](#view-contract-code) | Get deployed contract WASM code |
| [`view_state`](#view-contract-state) | Get contract storage data |
| [`data_changes`](#view-contract-state-changes) | Monitor contract state changes |
@@ -128,6 +130,164 @@ Returns account changes from transactions in a given account.
---
+## View Access Key
+
+Returns the nonce and permissions of a single [access key](/protocol/accounts-contracts/access-keys), given the account and the key's public key.
+
+- **method**: `query`
+- **params**: `request_type: view_access_key`, `finality` OR `block_id`, `account_id`, `public_key`
+
+
+
+ ```json
+ {
+ "jsonrpc": "2.0",
+ "id": "dontcare",
+ "method": "query",
+ "params": {
+ "request_type": "view_access_key",
+ "finality": "final",
+ "account_id": "account.rpc-examples.testnet",
+ "public_key": "ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa"
+ }
+ }
+ ```
+
+
+ ```js
+ import { JsonRpcProvider } from "near-api-js";
+
+ const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" });
+
+ const response = await provider.query({
+ request_type: 'view_access_key',
+ finality: 'final',
+ account_id: 'account.rpc-examples.testnet',
+ public_key: 'ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa',
+ });
+ ```
+
+
+ ```bash
+ http POST https://rpc.testnet.near.org \
+ jsonrpc=2.0 id=dontcare method=query \
+ params:='{"request_type":"view_access_key","finality":"final","account_id":"account.rpc-examples.testnet","public_key":"ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa"}'
+ ```
+
+
+
+
+```json
+{
+ "jsonrpc": "2.0",
+ "result": {
+ "nonce": 187310139000001,
+ "permission": "FullAccess",
+ "block_hash": "56xEo2LorUFVNbkFhCncFSWNiobdp1kzm14nZ47b5JVW",
+ "block_height": 187440904
+ },
+ "id": "dontcare"
+}
+```
+
+For a function-call key, `permission` is an object instead of the `"FullAccess"` string:
+
+```json
+"permission": {
+ "FunctionCall": {
+ "allowance": "250000000000000000000000",
+ "receiver_id": "contract.rpc-examples.testnet",
+ "method_names": ["get_greeting"]
+ }
+}
+```
+
+
+
+For a post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) key, pass the **full** `ml-dsa-65:` public key in `public_key`. The network hashes it to locate the on-chain entry, so you query it exactly like an `ed25519` or `secp256k1` key.
+
+
+---
+
+## View Access Key List
+
+Returns all [access keys](/protocol/accounts-contracts/access-keys) for an account, each with its nonce and permissions.
+
+- **method**: `query`
+- **params**: `request_type: view_access_key_list`, `finality` OR `block_id`, `account_id`
+
+
+
+ ```json
+ {
+ "jsonrpc": "2.0",
+ "id": "dontcare",
+ "method": "query",
+ "params": {
+ "request_type": "view_access_key_list",
+ "finality": "final",
+ "account_id": "account.rpc-examples.testnet"
+ }
+ }
+ ```
+
+
+ ```js
+ import { JsonRpcProvider } from "near-api-js";
+
+ const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" });
+
+ const response = await provider.query({
+ request_type: 'view_access_key_list',
+ finality: 'final',
+ account_id: 'account.rpc-examples.testnet',
+ });
+ ```
+
+
+ ```bash
+ http POST https://rpc.testnet.near.org \
+ jsonrpc=2.0 id=dontcare method=query \
+ params:='{"request_type":"view_access_key_list","finality":"final","account_id":"account.rpc-examples.testnet"}'
+ ```
+
+
+
+
+```json
+{
+ "jsonrpc": "2.0",
+ "result": {
+ "keys": [
+ {
+ "public_key": "ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa",
+ "access_key": {
+ "nonce": 187310139000001,
+ "permission": "FullAccess"
+ }
+ },
+ {
+ "public_key": "ml-dsa-65-hash:7Xx2X4vHb9dG8pJ5tZqD3mN6kRfWcA1sLuYoP2eB4hT",
+ "access_key": {
+ "nonce": 187420021000003,
+ "permission": "FullAccess"
+ }
+ }
+ ],
+ "block_hash": "56xEo2LorUFVNbkFhCncFSWNiobdp1kzm14nZ47b5JVW",
+ "block_height": 187440904
+ },
+ "id": "dontcare"
+}
+```
+
+
+
+Post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) keys are stored on-chain by hash, so they appear here with an `ml-dsa-65-hash:` prefix (a base58-encoded 48-byte SHA3-384 digest) rather than the full `ml-dsa-65:` key. To match an entry to one of your own keys, hash your public key and compare it against the returned value.
+
+
+---
+
## View Contract Code
Returns the contract code (Wasm binary) deployed to the account. The returned code is encoded in base64.
diff --git a/protocol/accounts-contracts/access-keys.mdx b/protocol/accounts-contracts/access-keys.mdx
index 0bb855e4e23..da1756348f8 100644
--- a/protocol/accounts-contracts/access-keys.mdx
+++ b/protocol/accounts-contracts/access-keys.mdx
@@ -58,6 +58,44 @@ You should never share your `Full-Access`, otherwise you are giving **total cont
---
+## Signature Schemes
+
+Independently of its permission level, every access key is a cryptographic key pair belonging to one of NEAR's supported **signature schemes**. A public key is written as `:`, where the prefix identifies the scheme — for example `ed25519:CQLP1o1F3Jbdttek3GoRJYhzfT...`.
+
+| Scheme | Public key prefix | Public key size | Quantum-resistant |
+| --------- | ----------------- | --------------- | ----------------- |
+| Ed25519 | `ed25519:` | 32 bytes | No |
+| secp256k1 | `secp256k1:` | 64 bytes | No |
+| ML-DSA-65 | `ml-dsa-65:` | 1952 bytes | Yes |
+
+`ed25519` is the default scheme, used by most wallets and tooling and by NEAR [implicit accounts](./account-id#implicit-address). `secp256k1` is used mainly for [chain signatures](/chain-abstraction/chain-signatures) and Ethereum-compatible flows. A single account can hold keys from different schemes at the same time.
+
+### Post-Quantum Keys (ML-DSA-65)
+
+`ml-dsa-65` is a **post-quantum** signature scheme, standardized by NIST as [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) (Module-Lattice-Based Digital Signature Algorithm, security category 3). Unlike `ed25519` and `secp256k1` — whose security a large enough quantum computer could break — `ml-dsa-65` is designed to stay secure against quantum attacks, so an account protected by an `ml-dsa-65` key cannot be taken over by forging its signature.
+
+You add and use an `ml-dsa-65` key exactly like any other key: it can be a [full-access](#full-access-keys) or a [function-call](#function-call-keys) key, and it signs transactions the same way.
+
+
+**Post-quantum keys are stored by hash**
+
+An `ml-dsa-65` public key is large — **1952 bytes**, versus 32 for `ed25519` — and its signatures are **3309 bytes**. To keep accounts cheap to store, NEAR does **not** keep the full public key on-chain; instead it stores a 48-byte [SHA3-384](https://en.wikipedia.org/wiki/SHA-3) hash of it, which keeps the per-key [storage cost](/protocol/storage/storage-staking) close to that of a classical key.
+
+
+Because only the hash is stored, listing an account's keys returns the **hash**, not the full key, for `ml-dsa-65` entries. When you query an account's keys (for example with [`view_access_key_list`](/api/rpc/contracts#view-access-key-list)), `ml-dsa-65` keys appear with an `ml-dsa-65-hash:` prefix instead of `ml-dsa-65:`:
+
+```
+ml-dsa-65-hash:7Xx2X... # base58-encoded 48-byte SHA3-384 digest
+```
+
+To recognize one of your own post-quantum keys in such a list, hash your known public key and compare it against the returned value. To look up a specific key directly with [`view_access_key`](/api/rpc/contracts#view-access-key), pass the **full** `ml-dsa-65:` public key and the network hashes it for you.
+
+
+Post-quantum support currently covers **transaction signing and access keys**. Validator (staking) keys, block production, and [implicit account](./account-id#implicit-address) addresses continue to use `ed25519`.
+
+
+---
+
## Limited Access Key Caveats
### Account with Only Function-Call Keys
diff --git a/smart-contracts/anatomy/actions.mdx b/smart-contracts/anatomy/actions.mdx
index 6f6d4deed9e..64bef85eb6d 100644
--- a/smart-contracts/anatomy/actions.mdx
+++ b/smart-contracts/anatomy/actions.mdx
@@ -593,6 +593,10 @@ class ExampleContract(Contract):
Notice that what you actually add is a "public key". Whoever holds its private counterpart, i.e. the private-key, will be able to use the newly access key.
+
+The `public_key` can use any of NEAR's [signature schemes](/protocol/accounts-contracts/access-keys#signature-schemes) — `ed25519`, `secp256k1`, or the post-quantum `ml-dsa-65`. Adding an `ml-dsa-65` key needs no code changes: pass an `ml-dsa-65:...` key exactly like the examples above.
+
+
If an account with a contract deployed does **not** have any access keys, this is known as a locked contract. When the account is locked, it cannot sign transactions therefore, actions can **only** be performed from **within** the contract code.
diff --git a/smart-contracts/anatomy/environment.mdx b/smart-contracts/anatomy/environment.mdx
index 27b22abf349..8975d166d39 100644
--- a/smart-contracts/anatomy/environment.mdx
+++ b/smart-contracts/anatomy/environment.mdx
@@ -119,6 +119,10 @@ During a simple transaction (no [cross-contract calls](../anatomy/crosscontract)
In most scenarios you will **only need to know the predecessor**. However, there are situations in which the signer is very useful. For example, when adding [NFTs](../../primitives/nft/nft) into [this marketplace](https://github.com/near-examples/nft-tutorial/blob/7fb267b83899d1f65f1bceb71804430fab62c7a7/market-contract/src/nft_callbacks.rs#L42), the contract checks that the `signer`, i.e. the person who generated the transaction chain, is the NFT owner.
+
+`signer_account_pk` returns the signer's public key in the same `:` format used elsewhere. If the transaction was signed with a post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) key, the returned key is correspondingly larger (1952 bytes) — keep that in mind if your contract stores or compares signer public keys.
+
+
---
## Balances and Attached NEAR
diff --git a/tools/cli.mdx b/tools/cli.mdx
index c096dcbcbf6..043f1d75790 100644
--- a/tools/cli.mdx
+++ b/tools/cli.mdx
@@ -185,7 +185,36 @@ near account export-account $ACCOUNT_ID using-web-wallet network-config testnet
## Keys
-Showing, adding and removing account keys.
+Generating, showing, adding and removing account keys. NEAR keys can use any of the supported [signature schemes](/protocol/accounts-contracts/access-keys#signature-schemes): `ed25519` (default), `secp256k1`, or the post-quantum `ml-dsa-65`.
+
+### Generate a key pair
+
+`generate-keypair` - create a fresh key pair offline, without touching an account or the network. Use `--signature-scheme` to choose the scheme (defaults to `ed25519`).
+
+
+
+ ```bash
+ # Print a new key pair to the terminal
+ near generate-keypair print-to-terminal
+
+ # ...or save it to a JSON file
+ near generate-keypair save-to-file ./my-key.json
+ ```
+
+
+ ```bash
+ # Print a new post-quantum key pair to the terminal
+ near generate-keypair --signature-scheme ml-dsa-65 print-to-terminal
+
+ # ...or save it to a JSON file
+ near generate-keypair --signature-scheme ml-dsa-65 save-to-file ./pq-key.json
+ ```
+
+
+
+
+Run in an interactive terminal without `--signature-scheme` and the CLI prompts you to pick a scheme; otherwise it defaults to `ed25519`. Unlike `ed25519`, `ml-dsa-65` key pairs are generated randomly and have no seed phrase or implicit-account address — be sure to back up the saved key file.
+
### List keys
@@ -225,6 +254,27 @@ Showing, adding and removing account keys.
+Instead of providing a key, you can have the CLI **autogenerate** one and add it in a single step with `autogenerate-new-keypair --signature-scheme `:
+
+
+
+ ```bash
+ export ACCOUNT_ID=bob.testnet
+ near account add-key $ACCOUNT_ID grant-full-access autogenerate-new-keypair save-to-keychain network-config testnet sign-with-keychain send
+ ```
+
+
+ ```bash
+ export ACCOUNT_ID=bob.testnet
+ near account add-key $ACCOUNT_ID grant-full-access autogenerate-new-keypair --signature-scheme ml-dsa-65 save-to-keychain network-config testnet sign-with-keychain send
+ ```
+
+
+
+
+The same `--signature-scheme` flag is available on the `account create-account fund-myself` and `sponsor-by-faucet-service` autogenerate flows. You can also add an externally generated `ml-dsa-65:` key through the `use-manually-provided-public-key` flow shown above. Post-quantum keys can be added to **named** accounts only — they have no implicit-account form.
+
+
### Delete key
`delete-keys` - delete an access key from an account.
@@ -244,6 +294,10 @@ Showing, adding and removing account keys.
+
+The interactive key picker only lists `ed25519` and `secp256k1` keys. A post-quantum `ml-dsa-65` key is stored on-chain only as a hash, so its full public key can't be read back from the network — to delete one, pass the full `ml-dsa-65:` key explicitly with `public-keys`, exactly as in the example above.
+
+
## Tokens
This will allow you to manage your token assets such as NEAR, FTs and NFTs.