Skip to content

feat(sdk-core): support preparedTransaction in TSS message signing - #9581

Merged
ravibitgo merged 1 commit into
masterfrom
CSHLD-1557
Aug 28, 2026
Merged

ravibitgo merged 1 commit into
masterfrom
CSHLD-1557

Conversation

@ravibitgo

@ravibitgo ravibitgo commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

wallet.signMessage() had no way to pass preparedTransaction — a field Canton's backend requires (as coinSpecific.preparedTransactionBase64) for both CANTON_SIGN_TRANSACTION and CANTON_SIGN_TOPOLOGY message standard types. Every field on the message-signing intent path is explicitly enumerated end-to-end (no generic passthrough bag), so any fresh Canton message-signing request created via the SDK was missing a mandatory field, regardless of whether the earlier commit-step server defect was fixed.

Without this, a client could only create a valid Canton message request via the raw POST /wallet/:id/msgrequests API directly, then use wallet.signMessage({ message: { txRequestId } }) only to sign it — wallet.signMessage() could never create one end-to-end on its own for Canton.

Changes

Threaded preparedTransaction?: string through the existing message-intent type chain, following the same pattern already established for Canton's cantonCommandParams on the transaction-intent side:

  • Message (bitgo/baseCoin/iBaseCoin.ts) — public field on wallet.signMessage({ message: { preparedTransaction } }).
  • IntentOptionsForMessage (bitgo/utils/tss/baseTypes.ts)
  • PopulatedIntentForMessageSigning (bitgo/utils/tss/baseTypes.ts) — the shape POSTed to /wallet/:id/msgrequests.
  • baseTSSUtils.buildSignMessageRequest — passes it into the built intent.
  • wallet.buildSignMessageRequest — passes params.message.preparedTransaction.
  • wallet.signMessageTss's inline intent construction (the fresh-request path taken when no txRequestId is given) — same, since it duplicates the intent shape instead of delegating to buildSignMessageRequest.

buildSignMessageRequestBase already spreads {...intent} into the POST body with no field whitelist, so no additional wiring was needed there.

Purely additive and optional — every other coin (SOL, ADA, Midnight, ETH typed-data) never sets this field, so it's undefined and omitted from their requests exactly as before.

Result

await wallet.signMessage({
  message: {
    messageRaw,
    messageStandardType: MessageStandardType.CANTON_SIGN_TOPOLOGY,
    preparedTransaction: '<base64 protobuf bytes>',
  },
  walletPassphrase,
});```

now creates the message txRequest (with the field Canton's backend requires) and signs it in one call, instead of requiring a raw API call to build the request first.

Test plan

- [x] New unit tests in modules/bitgo/test/v2/unit/wallet.ts (Message Signing suite):
- should include preparedTransaction in the msgrequests body when provided (via buildSignMessageRequest())
- should include preparedTransaction when signing a fresh message request(via signMessage(), no txRequestId) Both assert the intercepted POST body's intent.preparedTransaction.
- [x] npx tsc --noEmit on sdk-core  clean (no new errors)
- [ ] Manual: full Canton wallet.signMessage() flow against bitgo-test with preparedTransaction set, paired with the server-side commit-step

Ticket: CSHLD-1557

@linear-code

linear-code Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

CSHLD-1557

@ravibitgo
ravibitgo marked this pull request as ready for review August 27, 2026 11:40
@ravibitgo
ravibitgo requested review from a team as code owners August 27, 2026 11:40
@ravibitgo
ravibitgo merged commit 839a0c3 into master Aug 28, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants