-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Docs: missing setSender() guidance when seal_approve uses owned objects #507
Description
Problem
The Seal documentation and example code do not mention that tx.setSender(address) is required before tx.build({ onlyTransactionKind: true }) when a seal_approve function takes an owned object as a parameter (e.g., a PurchaseReceipt NFT, access token, or membership object used as proof of access).
Developers building access policies with owned objects hit this error with no guidance in the docs:
Error checking transaction input objects: Transaction was not signed by the correct
sender: Object 0x... is owned by account address 0x..., but given owner/signer
address is 0x0000000000000000000000000000000000000000000000000000000000000000
Root cause
tx.build({ onlyTransactionKind: true }) still performs full input resolution against chain state. For owned objects, the SDK needs the sender address to verify ownership. Without tx.setSender(), the sender defaults to 0x0000...0000, causing the ownership check to fail — either during tx.build() or during the key server's dry-run.
The onlyTransactionKind: true flag only affects the output format (strips sender/gas info from the serialized bytes). It does not skip input resolution.
Reproduction steps
- Write a
seal_approveMove function that takes an owned object parameter:entry fun seal_approve(id: vector<u8>, receipt: &PurchaseReceipt) { ... }
- Build the PTB in TypeScript without
setSender():const tx = new Transaction(); tx.moveCall({ target: `${packageId}::module::seal_approve`, arguments: [ tx.pure.vector("u8", fromHex(id)), tx.object(receiptObjectId), // owned object ], }); const txBytes = await tx.build({ client: suiClient, onlyTransactionKind: true });
- Observe the ownership mismatch error.
Expected behavior
The docs should clearly state that tx.setSender(account.address) must be called before tx.build() when the PTB references owned objects:
const tx = new Transaction();
tx.setSender(account.address); // Required for owned object resolution
tx.moveCall({ ... });
const txBytes = await tx.build({ client: suiClient, onlyTransactionKind: true });Why current docs miss this
All existing documentation examples (UsingSeal.mdx, GettingStarted.mdx) and the example frontend only demonstrate shared objects and pure values in seal_approve calls — scenarios where setSender() is unnecessary. Real-world access policies commonly use owned objects as proof of access, creating a gap.
Proposed fix
- Add a tip/note in the decryption sections of
docs/content/UsingSeal.mdxanddocs/content/GettingStarted.mdx - Add code comments in
examples/frontend/src/utils.tsneartx.build()calls