diff --git a/docs/content/UsingSeal.mdx b/docs/content/UsingSeal.mdx index 0e1e50bb4..b1dd40639 100644 --- a/docs/content/UsingSeal.mdx +++ b/docs/content/UsingSeal.mdx @@ -240,6 +240,85 @@ The `encryptedBytes` returned from the encryption call can be parsed using `Encr ::: +## Decryption + +Decryption involves a few additional steps: + +- The app must create a `SessionKey` object to access the decryption keys for a specific package. +- The user must approve the request by signing it in their wallet. This grants time-limited access to the associated keys. +- The app stores the resulting signature in the `SessionKey` to complete its initialization. + +Once initialized, the session key can be used to retrieve multiple decryption keys for the specified package without requiring further user confirmation. +```typescript +const sessionKey = await SessionKey.create({ + address: suiAddress, + packageId: fromHEX(packageId), + ttlMin: 10, // TTL of 10 minutes + suiClient: new SuiClient({ url: getFullnodeUrl('testnet') }), +}); +const message = sessionKey.getPersonalMessage(); +const { signature } = await keypair.signPersonalMessage(message); // User confirms in wallet +sessionKey.setPersonalMessageSignature(signature); // Initialization complete +``` + +:::note[Notes on Session Key] + +1. You can also optionally initialize a `SessionKey` with a passed in `Signer` in the constructor. This is useful for classes that extend `Signer`, for example, `EnokiSigner`. +2. You can optionally set an `mvr_name` value in the `SessionKey`. This should be the [Move Package Registry](https://www.moveregistry.com/) name for the package. Seal requires the MVR name to be registered to the first version of the package for this to work. If this is set, the message shown to the user in the wallet would use the much more readable MVR package name instead of `packageId`. +3. You can optionally store the `SessionKey` object in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) instead of localStorage if you would like to persist the `SessionKey` across tabs. See usage for `import` and `export` methods in the `SessionKey` class. + +::: + +The simplest way to perform decryption is to call the client's `decrypt` function. This function expects a `Transaction` object that invokes the relevant `seal_approve*` functions. The transaction must meet the following requirements: + +- It can only call `seal_approve*` functions. +- All calls must be to the same package. +```typescript +// Create the Transaction for evaluating the seal_approve function. +const tx = new Transaction(); +tx.moveCall({ + target: `${packageId}::${moduleName}::seal_approve`, + arguments: [ + tx.pure.vector("u8", fromHEX(id)), + // other arguments + ] + }); +const txBytes = tx.build( { client: suiClient, onlyTransactionKind: true }) +const decryptedBytes = await client.decrypt({ + data: encryptedBytes, + sessionKey, + txBytes, +}); +``` + +Seal evaluates the transaction as if the user sent it. In Move, `TxContext::sender()` returns the account that signed with the session key. + +:::tip + +To debug a transaction, call `dryRunTransactionBlock` directly with the transaction block. + +::: + +The `SealClient` caches keys retrieved from Seal key servers to optimize performance during subsequent decryptions, especially when the same ID is used across multiple encryptions. Reusing the same client instance helps reduce backend calls and improve latency. Refer to overall [Performance Recommendations](#optimizing-performance). + +To retrieve multiple keys more efficiently, use the `fetchKeys` function with a multi-command PTB. This approach is recommended when multiple keys are required, as it reduces the number of requests to the key servers. Because key servers can apply rate limiting, developers should design their applications and access policies to minimize the frequency of key retrieval requests. +```typescript +await client.fetchKeys({ + ids: [id1, id2], + txBytes: txBytesWithTwoSealApproveCalls, + sessionKey, + threshold: 2, +}); +``` + +Check out the [integration tests](https://github.com/MystenLabs/ts-sdks/blob/main/packages/seal/test/unit/integration.test.ts) for a full end-to-end example. You can also explore the [example app](https://github.com/MystenLabs/seal/tree/main/examples) to see how to implement allowlist and NFT-gated content access in practice. + +:::tip + +If a key server request fails with an `InvalidParameter` error, the cause can be a recently created on-chain object in the PTB input. The key server's full node might not have indexed it yet. Wait a few seconds and retry the request, as subsequent attempts should succeed once the node is in sync. + +::: + ### On-chain decryption Seal supports on-chain HMAC-CTR decryption in Move through the [`seal::bf_mac_encryption`](https://github.com/MystenLabs/seal/tree/main/move/seal/sources/bf_hmac_encryption.move) package. This enables Move packages to decrypt Seal-encrypted objects and use the results in on-chain logic such as auctions, secure voting (see [voting.move](https://github.com/MystenLabs/seal/tree/main/move/patterns/sources/voting.move)), or other verifiable workflows. @@ -350,85 +429,6 @@ const decrypted = tx.moveCall({ // The decryption result is in an option to be consumed if successful, `none` otherwise. ``` -## Decryption - -Decryption involves a few additional steps: - -- The app must create a `SessionKey` object to access the decryption keys for a specific package. -- The user must approve the request by signing it in their wallet. This grants time-limited access to the associated keys. -- The app stores the resulting signature in the `SessionKey` to complete its initialization. - -Once initialized, the session key can be used to retrieve multiple decryption keys for the specified package without requiring further user confirmation. -```typescript -const sessionKey = await SessionKey.create({ - address: suiAddress, - packageId: fromHEX(packageId), - ttlMin: 10, // TTL of 10 minutes - suiClient: new SuiClient({ url: getFullnodeUrl('testnet') }), -}); -const message = sessionKey.getPersonalMessage(); -const { signature } = await keypair.signPersonalMessage(message); // User confirms in wallet -sessionKey.setPersonalMessageSignature(signature); // Initialization complete -``` - -:::note[Notes on Session Key] - -1. You can also optionally initialize a `SessionKey` with a passed in `Signer` in the constructor. This is useful for classes that extend `Signer`, for example, `EnokiSigner`. -2. You can optionally set an `mvr_name` value in the `SessionKey`. This should be the [Move Package Registry](https://www.moveregistry.com/) name for the package. Seal requires the MVR name to be registered to the first version of the package for this to work. If this is set, the message shown to the user in the wallet would use the much more readable MVR package name instead of `packageId`. -3. You can optionally store the `SessionKey` object in [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) instead of localStorage if you would like to persist the `SessionKey` across tabs. See usage for `import` and `export` methods in the `SessionKey` class. - -::: - -The simplest way to perform decryption is to call the client's `decrypt` function. This function expects a `Transaction` object that invokes the relevant `seal_approve*` functions. The transaction must meet the following requirements: - -- It can only call `seal_approve*` functions. -- All calls must be to the same package. -```typescript -// Create the Transaction for evaluating the seal_approve function. -const tx = new Transaction(); -tx.moveCall({ - target: `${packageId}::${moduleName}::seal_approve`, - arguments: [ - tx.pure.vector("u8", fromHEX(id)), - // other arguments - ] - }); -const txBytes = tx.build( { client: suiClient, onlyTransactionKind: true }) -const decryptedBytes = await client.decrypt({ - data: encryptedBytes, - sessionKey, - txBytes, -}); -``` - -Seal evaluates the transaction as if the user sent it. In Move, `TxContext::sender()` returns the account that signed with the session key. - -:::tip - -To debug a transaction, call `dryRunTransactionBlock` directly with the transaction block. - -::: - -The `SealClient` caches keys retrieved from Seal key servers to optimize performance during subsequent decryptions, especially when the same ID is used across multiple encryptions. Reusing the same client instance helps reduce backend calls and improve latency. Refer to overall [Performance Recommendations](#optimizing-performance). - -To retrieve multiple keys more efficiently, use the `fetchKeys` function with a multi-command PTB. This approach is recommended when multiple keys are required, as it reduces the number of requests to the key servers. Because key servers can apply rate limiting, developers should design their applications and access policies to minimize the frequency of key retrieval requests. -```typescript -await client.fetchKeys({ - ids: [id1, id2], - txBytes: txBytesWithTwoSealApproveCalls, - sessionKey, - threshold: 2, -}); -``` - -Check out the [integration tests](https://github.com/MystenLabs/ts-sdks/blob/main/packages/seal/test/unit/integration.test.ts) for a full end-to-end example. You can also explore the [example app](https://github.com/MystenLabs/seal/tree/main/examples) to see how to implement allowlist and NFT-gated content access in practice. - -:::tip - -If a key server request fails with an `InvalidParameter` error, the cause can be a recently created on-chain object in the PTB input. The key server's full node might not have indexed it yet. Wait a few seconds and retry the request, as subsequent attempts should succeed once the node is in sync. - -::: - ## Optimizing performance To reduce latency and improve efficiency when using the Seal SDK, apply the following strategies based on your use case: diff --git a/docs/site/sidebars.js b/docs/site/sidebars.js index 4a9cb0d8b..065976364 100644 --- a/docs/site/sidebars.js +++ b/docs/site/sidebars.js @@ -12,13 +12,11 @@ const sidebars = { type: `doc`, id: `index`, }, - collapsed: false, items: [ `ServerOverview`, ] }, 'GettingStarted', - 'ServerOverview', { type: 'category', label: 'Developer Guide',