diff --git a/docs/content/GettingStarted.mdx b/docs/content/GettingStarted.mdx index 2fecb9fa9..e74467ec8 100644 --- a/docs/content/GettingStarted.mdx +++ b/docs/content/GettingStarted.mdx @@ -88,6 +88,12 @@ const decryptedBytes = await client.decrypt({ }); ``` +:::tip + +If your `seal_approve` function takes an owned object as an argument (for example, a `PurchaseReceipt` NFT), you must call `tx.setSender(account.address)` before `tx.build()`. See the [Decryption Guide](/UsingSeal#decryption) for details. + +::: + Learn more in the [Decryption Guide](/UsingSeal#decryption). ## Next steps diff --git a/docs/content/UsingSeal.mdx b/docs/content/UsingSeal.mdx index fc7cd368e..ba80cec1d 100644 --- a/docs/content/UsingSeal.mdx +++ b/docs/content/UsingSeal.mdx @@ -295,6 +295,27 @@ const decryptedBytes = await client.decrypt({ Seal evaluates the transaction as if the user sent it. In Move, `TxContext::sender()` returns the account that signed with the session key. +:::tip[Using owned objects in seal_approve] + +If your `seal_approve` function takes an **owned object** as a parameter (for example, a `PurchaseReceipt` or access NFT used as proof of access), you must call `tx.setSender(address)` before building the transaction: + +```typescript +const tx = new Transaction(); +tx.setSender(account.address); // Required when seal_approve references owned objects +tx.moveCall({ + target: `${packageId}::${moduleName}::seal_approve`, + arguments: [ + tx.pure.vector("u8", fromHEX(id)), + tx.object(ownedObjectId), // owned object + ] +}); +const txBytes = await tx.build({ client: suiClient, onlyTransactionKind: true }); +``` + +Without `setSender()`, `tx.build()` defaults the sender to `0x0` and fails to resolve owned object inputs, resulting in an ownership mismatch error. This is not needed when `seal_approve` only uses shared objects and pure values. + +::: + :::tip To debug a transaction, call `dryRunTransactionBlock` directly with the transaction block. diff --git a/examples/frontend/src/utils.ts b/examples/frontend/src/utils.ts index 78e0debb0..cf8d12b38 100644 --- a/examples/frontend/src/utils.ts +++ b/examples/frontend/src/utils.ts @@ -66,6 +66,8 @@ export const downloadAndDecrypt = async ( const ids = batch.map((enc) => EncryptedObject.parse(new Uint8Array(enc)).id); const tx = new Transaction(); ids.forEach((id) => moveCallConstructor(tx, id)); + // Note: if moveCallConstructor references owned objects, tx.setSender(address) + // must be called before build(). See docs/content/UsingSeal.mdx for details. const txBytes = await tx.build({ client: suiClient, onlyTransactionKind: true }); try { await sealClient.fetchKeys({ ids, txBytes, sessionKey, threshold: 2 }); @@ -87,6 +89,8 @@ export const downloadAndDecrypt = async ( const fullId = EncryptedObject.parse(new Uint8Array(encryptedData)).id; const tx = new Transaction(); moveCallConstructor(tx, fullId); + // Note: if moveCallConstructor references owned objects, tx.setSender(address) + // must be called before build(). See docs/content/UsingSeal.mdx for details. const txBytes = await tx.build({ client: suiClient, onlyTransactionKind: true }); try { // Note that all keys are fetched above, so this only local decryption is done