Skip to content

docs: add BitGoWasm API design conventions#176

Open
lcovar wants to merge 1 commit intomasterfrom
conventions
Open

docs: add BitGoWasm API design conventions#176
lcovar wants to merge 1 commit intomasterfrom
conventions

Conversation

@lcovar
Copy link
Contributor

@lcovar lcovar commented Feb 23, 2026

documents 7 architectural patterns enforced in code reviews across wasm-utxo, wasm-solana, wasm-mps, and future packages.

these are design decisions that could reasonably be made differently but we've standardized on:

  1. prefer Uint8Array - avoid unnecessary base conversions in the wasm interface
  2. bigint for amounts - never number or string; conversions are the caller's responsibility outside the wasm-* package
  3. as const arrays for union types - not enums, not magic strings
  4. return transaction objects from builders - not raw bytes
  5. parsing separate from transaction - standalone parseTransaction() function, not .parse() method
  6. use wrapper classes - wrap WASM bindings in TS classes for better types and camelCase naming
  7. follow wasm-utxo conventions - fromBytes, toBytes, getId, get wasm()

distilled from otto's recurring review comments. prevents review churn by making the patterns explicit.

ref: wasm-dot PR #145 discussion about convention alignment

@lcovar lcovar requested a review from a team as a code owner February 23, 2026 23:48
@lcovar lcovar marked this pull request as draft February 23, 2026 23:49
@lcovar lcovar marked this pull request as draft February 23, 2026 23:49
@lcovar lcovar changed the title Add BitGoWasm API design conventions docs: add BitGoWasm API design conventions Feb 23, 2026
Copy link
Contributor

@OttoAllmendinger OttoAllmendinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are on the right track

CONVENTIONS.md Outdated
const signedBytes = tx.toBytes();

// For parsing — decode instructions
const parsed = parseTransaction(txBytes, context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually would prefer if parseTransaction took the tx as an argument here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parseTransaction takes bytes to keep it decoupled from Transaction, most callers start with bytes from an api anyway

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this be a typical flow?

if parseTransaction takes Transaction we only have to decode once

const tx = Transaction.fromBytes(bytes);
const parsed = parseTransaction(tx);
if (validateParsed(parsed, buildParams)) { throw new Error(); }
tx.sign(privkey);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

youse right. udpated, refactor prs for solana/dot coming up after.

CONVENTIONS.md Outdated
}

// For high-level explanation — business logic
const explained = explainTransaction(txBytes, options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also we probably need to figure out what the distinction between parse and explain is

maybe just remove the explain example here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, removing.

parseTransaction returns a plain data object with decoded transaction data, Transaction.fromBytes is for signing. explain is basically parseTransaction with bitgo business logic on top, derives the type, extracts outputs/inputs, computes fees. its what bitgojs expects for the explain flow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it could live outside the wasm packages then and be built around parseTransaction? at least explainTransaction should be in a bitgo-specific subfolder

For wasm-utxo we have a BitGoPsbt class with a parseWithWalletKeys() and both bitgo explainTransaction and parseTransaction are built around that outside the wasm-utxo package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, currently for dot/sol i have it here but im convinced, it should live in bitgojs, ill pull it out.

document 7 architectural patterns enforced in code reviews:
- prefer Uint8Array, avoid unnecessary base conversions
- bigint for monetary amounts (conversions are caller's responsibility)
- as const arrays for union types, not magic strings
- builders return Transaction objects, not bytes
- parsing separate from Transaction (standalone parseTransaction)
- use wrapper classes over raw WASM bindings
- consistent wrapper API (fromBytes, toBytes, getId, get wasm())

distilled from recurring review comments across wasm-utxo, wasm-solana,
and wasm-dot. ref: PR #145 discussion.
@lcovar lcovar marked this pull request as ready for review February 26, 2026 22:43
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.

2 participants