From b81ccdf28988cab7a8a6ca825b7a848e252c6c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Thu, 12 Feb 2026 14:55:08 +0100 Subject: [PATCH 1/4] session keys --- docs/4-write-ten-dapp/4-session-keys.md | 258 ++++++++++++++---------- 1 file changed, 154 insertions(+), 104 deletions(-) diff --git a/docs/4-write-ten-dapp/4-session-keys.md b/docs/4-write-ten-dapp/4-session-keys.md index ead13d19..81926fe2 100644 --- a/docs/4-write-ten-dapp/4-session-keys.md +++ b/docs/4-write-ten-dapp/4-session-keys.md @@ -2,148 +2,198 @@ sidebar_position: 4 --- -# Account Abstraction +# Account Abstraction & Session Keys in TEN -The key feature of [Account Abstraction](https://medium.com/p/2e85bde4c54d) (EIP-4337) is “session keys” (SKs) through a proxy smart contract. -SKs allow users to interact with the blockchain without signing every transaction, which is a major UX improvement. +In the classic Account Abstraction model (EIP‑4337) described on [ethereum.org](https://ethereum.org/roadmap/account-abstraction/), “session keys” (SKs) are typically implemented using proxy smart contracts and a bundler. +TEN’s Account Abstraction design, outlined in [this post](https://medium.com/p/2e85bde4c54d), instead provides **native session keys managed by the gateway and TEEs**, eliminating the need for proxy contracts or a bundler. -TEN supports "native" SKs - these are managed by the platform and do not require a proxy contract. +Session keys in TEN are: -In TEN, SKs are managed by dApp developers through dedicated RPC endpoints. +- **Ephemeral Ethereum accounts** managed by the gateway on behalf of a user. +- **Linked to the user’s viewing key / encryption token** ([details](https://docs.ten.xyz/docs/write-ten-dapp/programmable-gateway-access)). +- **Used to sign and submit transactions** via the gateway, enabling “no‑click” UX for dApps. -## Solution overview +Each user can have **multiple session keys** (up to 100), and the gateway can automatically expire idle keys and recover funds back to the user’s primary account. -Imagine you're developing an on-chain game, and you want a smooth UX without the distraction of signing every move. +--- + +## High‑level flow for dApps -Conceptually, the game will create a session key (SK) for the user, then ask the user to move some funds to that address, and then create “move” transactions signed with the SK. +Imagine an on‑chain game that wants a smooth UX without prompting the user to sign every move: -If the game were to create the SK in the browser, there would be a risk of the user losing the SK, and the funds associated with it, in case of an accidental exit. -With TEN, the dApp developer doesn't have to worry about this, because the SKs are managed by TEEs. +1. **User authenticates to the gateway** and gets an `encryptionToken`. +2. **Gateway creates a session key** for this user and exposes its address. +3. **User funds the session key** with a normal, user‑signed transfer. +4. **Game sends transactions using the session key** (no additional wallet popups). +5. **Gateway tracks activity and can expire/clean up idle session keys**, sweeping funds back to the account which was first authenticated with the gateway. -## Usage +From the dApp’s point of view, a session key behaves like a normal Ethereum account whose private key is held inside the TEE‑backed gateway. -The steps below describe the implementation for a game developer—the primary use case for SKs. -Note that SKs can be used for any dApp that requires a no‑click UX. +--- -### When the game starts +## Using session keys via custom queries -Before the user can start playing, the game must create the SK and ask the user to move some funds to that address. -The funds will be used to pay for moves. +On TEN, session key management is exposed as **custom queries** behind standard JSON‑RPC, primarily via `eth_getStorageAt` with special “method addresses”. -- Call the RPC `eth_getStorageAt` with address `0x0000000000000000000000000000000000000003` - this will return the hex-encoded address of the SK. The dApp needs to store this address for future use. -- Create a normal transaction that transfers some ETH to the SK. The amount depends on how many "moves" the user is prepared to prepay for. -- Ask the user to sign this transaction with their standard wallet, and submit it to the network using the library of your choice. -- The session key is automatically activated and ready to use. +All examples below assume you are calling through the gateway: -### During the game +```text +https://testnet-rpc.ten.xyz/v1/?token= +``` -After sending funds to the SK, create a transaction for each move, but don't ask the user to sign them. -Instead, submit them to the network unsigned using the RPC `eth_getStorageAt` with address `0x0000000000000000000000000000000000000005` and the following parameters: +### 1. Create a session key -```json -{ - "sessionKeyAddress": "0x...", // The session key address - "tx": "base64_encoded_transaction" // The unsigned transaction encoded as base64 +To create a new session key for the authenticated user: + +- Call `eth_getStorageAt` with: + - **address**: `0x0000000000000000000000000000000000000003` + - other parameters can be left as in the example below. + +The response returns the **hex‑encoded address** of the new session key. + +```javascript +async function createSessionKey(encryptionToken) { + const response = await fetch( + `https://testnet-rpc.ten.xyz/v1/?token=${encryptionToken}`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "eth_getStorageAt", + params: [ + "0x0000000000000000000000000000000000000003", // create SK + "0x0", + "latest", + ], + id: 1, + }), + }, + ); + + const data = await response.json(); + return data.result; // Session key address (0x...) } ``` -The platform will sign the transactions on behalf of the user. +### 2. Fund the session key -As a game developer, you are responsible for keeping track of the SK’s balance. You can also query the network for the balance of the SK address. -If the SK runs out of balance, you must ask the user to move more funds to the SK. +Once you have the session key address, fund it with a **normal user‑signed transaction** from the user’s main wallet: -### Managing session keys +- Build a standard `eth_sendTransaction` / wallet transaction from the user’s account to the session key address. +- Let the wallet sign and submit it. -TEN provides additional RPC endpoints for managing session keys: +The gateway observes this and considers the session key funded and ready to use. -- `eth_getStorageAt` with address `0x0000000000000000000000000000000000000004` — permanently removes the session key. This requires the following parameters: +--- + +## Sending transactions with a session key + +There are two main integration patterns: + +1. **Custom‑query based signing** (low‑level; uses `eth_getStorageAt`). +2. **Direct `eth_sendTransaction` from the session key** (simpler, when your client library supports it). + +### 1) Custom‑query signing (low‑level) + +You can ask the gateway to sign and submit an unsigned transaction using the session key by calling: + +- `eth_getStorageAt` with **address** `0x0000000000000000000000000000000000000005` +- The second parameter encodes: ```json { - "sessionKeyAddress": "0x..." // The session key address to delete + "sessionKeyAddress": "0x...", // Session key address + "tx": "base64_encoded_transaction" // Unsigned transaction, base64‑encoded } ``` -### Finishing the game +Example: -When a game ends, you must move the remaining funds back to the main address. +```javascript +async function sendWithSessionKey( + encryptionToken, + sessionKeyAddress, + unsignedTx, +) { + const txBase64 = btoa(JSON.stringify(unsignedTx)); // client‑side encoding + + const response = await fetch( + `https://testnet-rpc.ten.xyz/v1/?token=${encryptionToken}`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "eth_getStorageAt", + params: [ + "0x0000000000000000000000000000000000000005", // sign+send with SK + JSON.stringify({ + sessionKeyAddress, + tx: txBase64, + }), + "latest", + ], + id: 1, + }), + }, + ); -- Create a transaction (tx) that moves the funds back from the SK to the main address. Submit it unsigned, because the funds are controlled by the SK. + const data = await response.json(); + return data.result; // tx hash +} +``` -## Example implementation +The gateway validates that the session key belongs to the user identified by `encryptionToken`, signs the transaction with the session key’s private key, and propagates it to the TEN node. -Here's a complete example of how to implement session keys in a JavaScript dApp: +### 2) Direct `eth_sendTransaction` with a session key -```javascript -// 1. Create a session key -async function createSessionKey() { - const response = await fetch("https://testnet.ten.xyz/v1/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "eth_getStorageAt", - params: ["0x0000000000000000000000000000000000000003", "0x0", "latest"], - id: 1, - }), - }); +The gateway can also support flows where: - const data = await response.json(); - return data.result; // Returns the session key address -} +- The **`from` field** is a session key address owned by the user. +- The gateway signs and sends the transaction if the session key is valid and sufficiently funded. -// 2. Fund the session key (user signs this transaction) -async function fundSessionKey(sessionKeyAddress, amount) { - // This would be a normal transaction signed by the user's wallet - // transferring ETH to the session key address -} +This path is used in integration tests and may be preferred when your client already expects standard `eth_sendTransaction` semantics. The exact UX and error handling (e.g. non‑session‑key `from`) should follow the gateway implementation. -// 3. Send unsigned transactions using the session key -async function sendUnsignedTransaction(sessionKeyAddress, unsignedTx) { - const txBase64 = btoa(JSON.stringify(unsignedTx)); // Convert to base64 - - const response = await fetch("https://testnet.ten.xyz/v1/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "eth_getStorageAt", - params: [ - "0x0000000000000000000000000000000000000005", - JSON.stringify({ - sessionKeyAddress: sessionKeyAddress, - tx: txBase64, - }), - "latest", - ], - id: 1, - }), - }); +--- - const data = await response.json(); - return data.result; // Returns the transaction hash -} +## Deleting and expiring session keys -// 4. Delete the session key when done -async function deleteSessionKey(sessionKeyAddress) { - const response = await fetch("https://testnet.ten.xyz/v1/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - jsonrpc: "2.0", - method: "eth_getStorageAt", - params: [ - "0x0000000000000000000000000000000000000004", - JSON.stringify({ - sessionKeyAddress: sessionKeyAddress, - }), - "latest", - ], - id: 1, - }), - }); +### Manual deletion via custom query - const data = await response.json(); - return data.result; // Returns 0x01 for success, 0x00 for failure +To explicitly delete a session key: + +- Call `eth_getStorageAt` with **address** `0x0000000000000000000000000000000000000004` and a JSON payload: + +```json +{ + "sessionKeyAddress": "0x..." // Session key address to delete } ``` + +The gateway: + +- Removes the session key from storage (access to the private key is lost forever). +- **Sweeps remaining funds** back to the user’s primary account, using its internal `TxSender`. + +### Automatic expiration & fund recovery + +In addition, the gateway runs a background **SessionKeyExpirationService**: + +- Tracks last activity for each session key. +- Periodically scans for keys older than `--sessionKeyExpirationThreshold`. +- For each candidate, it: + - Reloads the owning user. + - Attempts to move remaining funds back to the user’s primary account. + - Removes the key from the activity tracker and persists updated state. + +--- + +## UX & safety considerations for dApp developers + +- **Balance management**: Track the session key’s balance (e.g. via `eth_getBalance`) and top it up when needed. +- **Per‑dApp keys**: Use separate session keys per dApp / game instance if you want stronger isolation. +- **Lifecycle**: Delete or let the gateway expire session keys when they are no longer needed to keep the system tidy. +- **Security**: The session key’s private key never leaves the TEE‑backed gateway, but users should still treat them as scoped spending keys and only fund them with limited amounts. + +Combined with **programmable access tokens** (`encryptionToken`) and the TEN gateway, session keys give you EIP‑4337‑style UX without additional contracts or bundler infrastructure. From 1bdd6ee595354fc62bbc6b3edd4262335ad1c3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Thu, 12 Feb 2026 14:55:34 +0100 Subject: [PATCH 2/4] programmable access --- .../9-programmable-gateway-access.md | 229 ++++++++++++++---- 1 file changed, 185 insertions(+), 44 deletions(-) diff --git a/docs/4-write-ten-dapp/9-programmable-gateway-access.md b/docs/4-write-ten-dapp/9-programmable-gateway-access.md index f90a036c..83e7a1e7 100644 --- a/docs/4-write-ten-dapp/9-programmable-gateway-access.md +++ b/docs/4-write-ten-dapp/9-programmable-gateway-access.md @@ -4,113 +4,254 @@ sidebar_position: 9 # Programmable Access to Gateway -# TEN Network Gateway API +## TEN Network Gateway API (testnet) -Base URL: `https://testnet.ten.xyz` +**Base URL (testnet gateway):** `https://testnet-rpc.ten.xyz` + +**Testnet frontend (UI):** [`https://testnet.ten.xyz`](https://testnet.ten.xyz) – hosted gateway UI that talks to the same testnet gateway. + +All HTTP endpoints below are served under the gateway base URL and versioned under `/v1`. + +### High‑level flow + +- **1. Create a user & encryption token** via `/v1/join/`. +- **2. Get a message to sign** via `/v1/getmessage/` using that token. +- **3. Authenticate** by sending the signed message to `/v1/authenticate/`. +- **4. (Optional) Query registration state** for specific addresses via `/v1/query/`. +- **5. (Optional) Revoke** the token via `/v1/revoke/`. +- **6. Call JSON‑RPC** using the token as a query parameter. + +--- ## 1. Get Encryption Token -**Endpoint:** `GET /v1/join` +**Endpoint:** `POST /v1/join/` -Generates and returns an EncryptionToken. +Creates a new gateway user, generates a fresh viewing key pair, and returns the corresponding **encryption token** (also called “gateway token”) as a hex string. This token is used with both HTTP endpoints and JSON‑RPC. ```bash -curl -X GET https://testnet.ten.xyz/v1/join +curl -X POST https://testnet-rpc.ten.xyz/v1/join/ ``` ```javascript -const response = await fetch('https://testnet.ten.xyz/v1/join'); -const token = await response.text(); +const response = await fetch("https://testnet-rpc.ten.xyz/v1/join/", { + method: "POST", +}); +const encryptionToken = await response.text(); // hex string ``` +--- + ## 2. Get Message to Sign -**Endpoint:** `POST /v1/getmessage` +**Endpoint:** `POST /v1/getmessage/` -Generates and returns a message (if needed 712 typed message too) for the user to sign based on the provided encryption token. +Generates and returns an authentication message for the user to sign based on the provided encryption token. The gateway can return either an EIP‑712 typed message or a `personal_sign` message. + +**Request body:** + +```json +{ + "encryptionToken": "", + "formats": ["EIP712", "personal_sign"] // optional; order is preference +} +``` + +**Example (cURL):** ```bash -curl -X POST "https://testnet.ten.xyz/v1/getmessage/" \ +curl -X POST "https://testnet-rpc.ten.xyz/v1/getmessage/" \ -H "Content-Type: application/json" \ -d '{ - "encryptionToken": "$EncryptionToken", + "encryptionToken": "", "formats": ["EIP712"] }' ``` +**Example (JavaScript):** + ```javascript -const msgRes = await fetch('https://testnet.ten.xyz/v1/getmessage/', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, +const msgRes = await fetch("https://testnet-rpc.ten.xyz/v1/getmessage/", { + method: "POST", + headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - encryptionToken: token, - formats: ['EIP712'], + encryptionToken, + formats: ["EIP712"], }), }); -const data = await msgRes.json(); + +const { message, type } = await msgRes.json(); // type: "EIP712" | "personal_sign" ``` +You then pass `message` to the user’s wallet to be signed. + +--- + ## 3. Authenticate User -**Endpoint:** `POST /v1/authenticate?token=$EncryptionToken` +**Endpoint:** `POST /v1/authenticate/?token=` + +Submits the signed message and proves wallet ownership. The gateway verifies the signature and binds the wallet address to the user identified by `token`. + +**Request body:** + +```json +{ + "address": "0x...", + "signature": "0x...", + "type": "EIP712" // optional, defaults to EIP712 +} +``` -Submits a signed message in the format address & signature, proving ownership of the private keys for the account, and links that account with the encryption token. +**Example (cURL):** ```bash -curl -X POST "https://testnet.ten.xyz/v1/authenticate/?token=$EncryptionToken" \ +curl -X POST "https://testnet-rpc.ten.xyz/v1/authenticate/?token=" \ -H "Content-Type: application/json" \ -d '{ - "address": "$Address", - "signature": "$Signature" + "address": "0xYourAddress", + "signature": "0xYourSignature", + "type": "EIP712" }' ``` +**Example (JavaScript):** + ```javascript -await fetch(`https://testnet.ten.xyz/v1/authenticate/?token=${EncryptionToken}`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - address, - signature: sig - }), -}); +await fetch( + `https://testnet-rpc.ten.xyz/v1/authenticate/?token=${encryptionToken}`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + address, + signature: sig, + type: "EIP712", + }), + }, +); ``` +Multiple addresses can be associated with a single encryption token. + +--- + ## 4. Query Address Registration -**Endpoint:** `GET /v1/query/address?token=$EncryptionToken&a=$Address` +**Endpoint:** `GET /v1/query/?token=&a=
` -Returns a JSON response indicating whether the address "a" is registered for the user. +Returns whether a given address is already registered (bound) for the user. + +**Example (cURL):** ```bash -curl -X GET "https://testnet.ten.xyz/v1/query/address?token=$EncryptionToken&a=$Address" +curl "https://testnet-rpc.ten.xyz/v1/query/?token=&a=0xYourAddress" +``` + +**Response:** + +```json +{ + "status": true +} ``` +**Example (JavaScript):** + ```javascript -const response = await fetch(`https://testnet.ten.xyz/v1/query/address?token=${token}&a=${address}`); -const data = await response.text(); +const res = await fetch( + `https://testnet-rpc.ten.xyz/v1/query/?token=${encryptionToken}&a=${address}`, +); +const data = await res.json(); // { status: boolean } ``` +--- + ## 5. Revoke Access -**Endpoint:** `POST /v1/revoke?token=$EncryptionToken` +**Endpoint:** `POST /v1/revoke/?token=` -Deletes encryption token associated with the account. +Deletes the user identified by `token` along with associated viewing keys and session keys in the gateway. ```bash -curl -X POST "https://testnet.ten.xyz/v1/revoke?token=abc123" +curl -X POST "https://testnet-rpc.ten.xyz/v1/revoke/?token=" ``` ```javascript -await fetch(`https://testnet.ten.xyz/v1/revoke?token=${token}`, { - method: 'POST' +await fetch(`https://testnet-rpc.ten.xyz/v1/revoke/?token=${encryptionToken}`, { + method: "POST", }); ``` -## RPC Usage +--- + +## 6. JSON‑RPC Usage -After authentication, use the encryption token with RPC calls: +After authenticating at least one address for a user, you can send Ethereum‑style JSON‑RPC requests through the gateway by including the encryption token as a query parameter. +**RPC endpoint (HTTP):** + +```text +https://testnet-rpc.ten.xyz/v1/?token= ``` -https://testnet.ten.xyz/v1/?token= -``` \ No newline at end of file + +Example payload: + +```bash +curl -X POST "https://testnet-rpc.ten.xyz/v1/?token=" \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_getBalance", + "params": ["0xYourAddress", "latest"] + }' +``` + +The gateway enforces that reads and writes are scoped to accounts that are bound to the encryption token and forwards calls to the TEN node over an encrypted channel. + +--- + +## 7. Health & Network Endpoints + +These endpoints are useful for monitoring and discovering network configuration: + +- **`GET /v1/ready/`** – lightweight readiness probe indicating the gateway process is up. +- **`GET /v1/health/`** – basic health check of the gateway itself (cached briefly). +- **`GET /v1/network-health/`** – TEN node health wrapped in a JSON‑RPC‑like response. +- **`GET /v1/network-config/`** – static network configuration (contract addresses, registries, etc.). +- **`GET /v1/version/`** – current gateway version as a plain string. + +All are exposed under `https://testnet-rpc.ten.xyz/v1/...`. + +--- + +## 8. Token Cookie Helper Endpoints (Browser flows) + +When integrating with a browser frontend, the gateway can store the encryption token in a secure cookie (domain‑wide on `.ten.xyz`): + +- **`GET /v1/get-token/`** + - Reads the gateway token cookie, validates it, ensures the user exists, and returns the token as a hex string. + +- **`POST /v1/set-token/`** + - Request body: + ```json + { + "token": "" + } + ``` + - Validates the token, checks that the user exists, and sets a long‑lived `HttpOnly`, `Secure` cookie for use by browser dApps. + +Both endpoints are also served under `https://testnet-rpc.ten.xyz/v1/...` and are guarded by CORS based on the configured frontend URL. + +--- + +## 9. Using the TEN Hardhat plugin + +For Hardhat‑based development, you can also integrate with the TEN testnet gateway using the **TEN Hardhat plugin**: + +- **Package:** `@tenprotocol/ten-hardhat-plugin` +- **Docs & usage:** see the plugin README on npm: + [`https://www.npmjs.com/package/@tenprotocol/ten-hardhat-plugin`](https://www.npmjs.com/package/@tenprotocol/ten-hardhat-plugin) + +The plugin configures a Hardhat network to talk to the same JSON‑RPC gateway at `https://testnet-rpc.ten.xyz` and is the recommended way to interact with TEN from Hardhat projects. From 3a302aad483f2a18119fab609dbd2634165a0fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Thu, 12 Feb 2026 14:56:28 +0100 Subject: [PATCH 3/4] testnet --- docs/4-write-ten-dapp/6-testnet.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/4-write-ten-dapp/6-testnet.md b/docs/4-write-ten-dapp/6-testnet.md index 3741e221..76707a9a 100644 --- a/docs/4-write-ten-dapp/6-testnet.md +++ b/docs/4-write-ten-dapp/6-testnet.md @@ -4,7 +4,7 @@ sidebar_position: 6 # Testnet -TEN Sepolia is our testnet that replicates the capabilities of the TEN Mainnet network. Linked to the Sepolia testnet, you can authenticate with the testnet TEN gateway, use the TEN faucet, and develop and deploy dApps for testing. +TEN Sepolia is our testnet that replicates the capabilities of the TEN Mainnet network. Linked to the Sepolia testnet, you can authenticate with the testnet TEN gateway, use the TEN faucet, and develop and deploy dApps for testing. ## TEN Gateway @@ -12,21 +12,25 @@ TEN Sepolia is our testnet that replicates the capabilities of the TEN Mainnet n Visit the TEN testnet gateway [here](https://testnet.ten.xyz/). Follow the on‑screen instructions to authenticate with the hosted gateway that allows you to interact with the testnet. -## TEN Sepolia Faucet +## Getting funds to the testnet -![TEN Discord Faucet](../assets/faucet.ten.xyz.jpg) +### **Option 1: Requesting Testnet ETH from the TEN Gas Station** -Using the steps provided, you can request testnet ETH from the faucet available on the TEN Gas Station. - -## **Requesting Testnet ETH** 1. Make a note of your EVM wallet address or copy it to your clipboard. -2. Head over to [TEN Gas Station](https://testnet-faucet.ten.xyz/). +2. Head over to [TEN Gas Station](https://faucet.ten.xyz/). 3. Paste your EVM wallet address into the wallet address field. 4. Log in with your Discord and X (Twitter) accounts. -5. Then, complete the available tasks. +5. Complete the available tasks to receive testnet ETH. +### **Option 2: Bridging Sepolia testnet funds to TEN** -## TENscan +If you already have ETH on the Sepolia testnet, you can bridge it directly to TEN Sepolia: -You can use the TEN block explorer to view transaction data occurring on the testnet [here](https://testnet.tenscan.io/). +1. Go to the TEN testnet bridge: [`https://testnet-bridge.ten.xyz/`](https://testnet-bridge.ten.xyz/). +2. Connect your wallet (Sepolia network selected). +3. Choose how much Sepolia ETH you want to bridge from L1 to L2 (TEN). +4. Confirm the transaction in your wallet and wait for it to finalize. +## TEN Scan + +You can use the TEN block explorer to view transaction data occurring on the testnet [here](https://testnet.tenscan.io/). From 611fcfac41a1a455a589138a7f05e91e681878ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Fri, 13 Feb 2026 10:44:21 +0100 Subject: [PATCH 4/4] fixes after review --- docs/4-write-ten-dapp/4-session-keys.md | 19 ++++++++++++++----- docs/4-write-ten-dapp/6-testnet.md | 6 +++--- .../9-programmable-gateway-access.md | 11 +++-------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/4-write-ten-dapp/4-session-keys.md b/docs/4-write-ten-dapp/4-session-keys.md index 81926fe2..78c6a294 100644 --- a/docs/4-write-ten-dapp/4-session-keys.md +++ b/docs/4-write-ten-dapp/4-session-keys.md @@ -144,16 +144,25 @@ async function sendWithSessionKey( } ``` -The gateway validates that the session key belongs to the user identified by `encryptionToken`, signs the transaction with the session key’s private key, and propagates it to the TEN node. +The gateway first confirms that the session key belongs to the user identified by `encryptionToken`. It then signs the transaction with the session key’s private key and sends it to the TEN node. ### 2) Direct `eth_sendTransaction` with a session key -The gateway can also support flows where: +You can send transactions directly using `eth_sendTransaction` by setting the `from` field to a session key address that belongs to the authenticated user. The gateway signs the transaction with the session key's private key and broadcasts it. -- The **`from` field** is a session key address owned by the user. -- The gateway signs and sends the transaction if the session key is valid and sufficiently funded. +**Requirements:** -This path is used in integration tests and may be preferred when your client already expects standard `eth_sendTransaction` semantics. The exact UX and error handling (e.g. non‑session‑key `from`) should follow the gateway implementation. +- The request must include the user's `encryptionToken` (same authentication as other gateway calls). +- The `from` address must be a session key owned by that user. +- The transaction must include all required fields (`gas`, `gasPrice` or `maxFeePerGas`/`maxPriorityFeePerGas`, `nonce`, etc.). The gateway does not auto-fill missing fields. + +The gateway verifies that the session key belongs to the user identified by `encryptionToken`, signs the transaction with the session key's private key, and sends it to the TEN node. + +**Error handling:** + +- If `from` is not a session key for the authenticated user, the call fails with an error indicating the session key address was not found. +- If the session key lacks sufficient balance, the transaction is rejected by the network (not by the gateway). +- Standard authentication errors apply if the `encryptionToken` is missing or invalid. --- diff --git a/docs/4-write-ten-dapp/6-testnet.md b/docs/4-write-ten-dapp/6-testnet.md index 76707a9a..2c9bf2ae 100644 --- a/docs/4-write-ten-dapp/6-testnet.md +++ b/docs/4-write-ten-dapp/6-testnet.md @@ -4,7 +4,7 @@ sidebar_position: 6 # Testnet -TEN Sepolia is our testnet that replicates the capabilities of the TEN Mainnet network. Linked to the Sepolia testnet, you can authenticate with the testnet TEN gateway, use the TEN faucet, and develop and deploy dApps for testing. +TEN Sepolia is our testnet environment that mirrors the capabilities of the TEN Mainnet. In this environment, you can authenticate with the TEN testnet gateway, use the TEN faucet, and develop and deploy dApps for testing. ## TEN Gateway @@ -12,7 +12,7 @@ TEN Sepolia is our testnet that replicates the capabilities of the TEN Mainnet n Visit the TEN testnet gateway [here](https://testnet.ten.xyz/). Follow the on‑screen instructions to authenticate with the hosted gateway that allows you to interact with the testnet. -## Getting funds to the testnet +## Getting funds on the testnet ### **Option 1: Requesting Testnet ETH from the TEN Gas Station** @@ -31,6 +31,6 @@ If you already have ETH on the Sepolia testnet, you can bridge it directly to TE 3. Choose how much Sepolia ETH you want to bridge from L1 to L2 (TEN). 4. Confirm the transaction in your wallet and wait for it to finalize. -## TEN Scan +## TENScan You can use the TEN block explorer to view transaction data occurring on the testnet [here](https://testnet.tenscan.io/). diff --git a/docs/4-write-ten-dapp/9-programmable-gateway-access.md b/docs/4-write-ten-dapp/9-programmable-gateway-access.md index 83e7a1e7..49f99ece 100644 --- a/docs/4-write-ten-dapp/9-programmable-gateway-access.md +++ b/docs/4-write-ten-dapp/9-programmable-gateway-access.md @@ -27,7 +27,7 @@ All HTTP endpoints below are served under the gateway base URL and versioned und **Endpoint:** `POST /v1/join/` -Creates a new gateway user, generates a fresh viewing key pair, and returns the corresponding **encryption token** (also called “gateway token”) as a hex string. This token is used with both HTTP endpoints and JSON‑RPC. +Creates a new gateway user, generates a fresh viewing key pair, and returns the corresponding **encryption token** (also called “gateway token”) as a hex string. This is used with HTTP and JSON-RPC endpoints ```bash curl -X POST https://testnet-rpc.ten.xyz/v1/join/ @@ -46,7 +46,7 @@ const encryptionToken = await response.text(); // hex string **Endpoint:** `POST /v1/getmessage/` -Generates and returns an authentication message for the user to sign based on the provided encryption token. The gateway can return either an EIP‑712 typed message or a `personal_sign` message. +Generates and returns an authentication message for the user to sign based on the provided encryption token. The gateway can return either an EIP‑712 typed message (default) or a `personal_sign` message in case of compatibility with older wallets. **Request body:** @@ -234,12 +234,7 @@ When integrating with a browser frontend, the gateway can store the encryption t - Reads the gateway token cookie, validates it, ensures the user exists, and returns the token as a hex string. - **`POST /v1/set-token/`** - - Request body: - ```json - { - "token": "" - } - ``` + - Request body: `{ "token": "" }` - Validates the token, checks that the user exists, and sets a long‑lived `HttpOnly`, `Secure` cookie for use by browser dApps. Both endpoints are also served under `https://testnet-rpc.ten.xyz/v1/...` and are guarded by CORS based on the configured frontend URL.