add interface for multisig account wallet#32
Open
quocle108 wants to merge 42 commits into
Open
Conversation
quocle108
force-pushed
the
add-interface-multisig-account-wallet
branch
from
February 13, 2026 02:45
9c47034 to
58d3535
Compare
Davi0kProgramsThings
requested changes
Feb 13, 2026
Davi0kProgramsThings
left a comment
Contributor
There was a problem hiding this comment.
Well done, quoc. I left a few comments to improve the interfaces.
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
jonathunne
reviewed
Feb 14, 2026
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
quocle108
force-pushed
the
add-interface-multisig-account-wallet
branch
from
February 19, 2026 00:48
86801f7 to
5ffcb43
Compare
…countReadOnlyMultisig
jonathunne
reviewed
Feb 24, 2026
Co-authored-by: Jonathan Dunne <jonathanpdunne@gmail.com>
Davi0kProgramsThings
requested changes
Feb 24, 2026
Adds IMultisigTransport to src/multisig/: a transport contract for sharing transaction and message proposals (and their confirmations) between the owners of a distributed multisig account. Proposal/message payloads are opaque so the same transport can back account-abstraction, UTXO (PSBT) or other multisig wallets; each chain's package interprets them. Exported from the @tetherto/wdk-wallet/multisig subpath alongside the existing multisig interfaces. Consumed by @tetherto/wdk-protocol-multisig-safe, which previously defined this interface locally.
…lletAccount Per the team decision on PR tetherto#32: a multisig sendTransaction proposes (it does not execute), so the inherited IWalletAccount { hash, fee } contract cannot be honored across chains (off-chain proposal id on Safe/Bitcoin, proposal-not-execution on Solana). Decouple the two families and model the multisig flow directly. - Add internal IWalletAccountReadOnlyBase (getAddress, verify, getBalance, getTokenBalance, getTransactionReceipt); IWalletAccountReadOnly and IWalletAccountReadOnlyMultisig both extend it. Not exported from the entry point. - IWalletAccountMultisig extends IWalletAccountReadOnlyMultisig only (no longer IWalletAccount). Rename sendTransaction->propose, transfer->proposeTransfer, approveTx->approveProposal, rejectTx->rejectProposal, executeTx->executeProposal. - propose returns MultisigProposalResult { proposalId, fee, confirmations, threshold, executed } (no fake on-chain hash); executeProposal returns the on-chain { hash, fee }. - Move owner management into a separate optional IMultisigOwnerManagement interface; chains with an immutable owner set (e.g. Bitcoin script multisig) don't implement it. - Re-add index/path/keyPair to IWalletAccountReadOnlyMultisig (previously inherited via IWalletAccount). Types hand-maintained to match the repo's interface-style .d.ts.
A read-only account has no private key, so signerKeyPair/index/path cannot be honored on IWalletAccountReadOnlyMultisig — same reason single-signer keeps them on IWalletAccount, not IWalletAccountReadOnly. Move them to IWalletAccountMultisig; the read-only interface keeps the keyless getSignerAddress().
quocle108
commented
Jun 18, 2026
quocle108
commented
Jun 18, 2026
quocle108
commented
Jun 18, 2026
quocle108
commented
Jun 18, 2026
quocle108
commented
Jun 18, 2026
quocle108
commented
Jun 18, 2026
Add type parameters with defaults so concrete transports can declare their specific payload shapes while staying backward-compatible: IMultisigTransport<TProposal = Record<string, unknown>, TMessage = MultisigTransportMessageInput> submitProposal/submitMessage take TProposal/TMessage. Impls that don't supply type arguments fall back to the opaque defaults, so existing transports are unaffected and custom transports remain pluggable via parameter contravariance. get/confirm stay non-generic (chain-agnostic confirmations view + string ids).
…h->messageId - MultisigExecuteQuote.fee: 'gas cost' -> 'cost' (gas is EVM-specific; the interface is chain-agnostic). - Rename messageHash -> messageId (and messageHashes -> messageIds) across the message-proposal flow (MultisigMessage, MultisigMessageProposal, getMessages, getMessage/confirmMessage/approveMessage), for consistency with proposalId and to stay generic (an id may be a hash or otherwise).
…ports Transports that serialize proposals/messages themselves (rather than delegating to an SDK) can use toTransportJson to convert native BigInt values into JSON-safe strings before JSON.stringify. Exported from @tetherto/wdk-wallet/multisig; SafeTxServiceTransport is unaffected (api-kit keeps native bigint).
…turn type MultisigProposal gains a 'status: pending | executed' field, and propose/proposeTransfer now return MultisigProposal instead of the separate MultisigProposalResult (dropped, along with its 'executed' boolean). Addresses review tetherto#3 (status enum) and tetherto#5 (reuse the read-only proposal type).
…word - Root types/index.d.ts no longer re-exports multisig types from deleted root-level paths (./src/wallet-account-*-multisig.js) using pre-restructure names; multisig is exposed via the /multisig subpath, matching index.js. - IWalletAccountMultisig uses the real 'extends IWalletAccountReadOnlyMultisig' keyword (with a runtime import) instead of the redundant @extends JSDoc, matching its sibling read-only interface (review: jonathunne).
Adds TProposalResponse/TMessageResponse template params (defaulting to the opaque MultisigTransportProposal/MultisigTransportMessage) so getProposal/getMessage mirror the already-generic write side. Implementations can advertise the concrete response shape they return, giving direct consumers real type-checking instead of opaque payloads; unparameterized usage is unchanged.
toTransportJson now also converts Uint8Array/Buffer to 0x-prefixed hex (in addition to BigInt->decimal-string), so payloads that carry raw bytes survive JSON.stringify. Documents the conversion as one-way (consumers restore types per field).
…racts Adds SignerError, ValueError and NoSuchElementError (verbatim from the taxonomy agreed on tetherto#46) to the core errors module and exports them, and documents them via @throws on the multisig interface contracts (propose/approve/reject/execute/quoteExecuteProposal + owner management). Interface bodies still throw NotImplementedError; @throws documents the contract implementations must honor. UnsupportedOperationError is deferred with the optional-method (abstract-base) work. Mirrors tetherto#46's agreed definitions so the shared errors module reconciles cleanly when both land.
States that isCreated is omitted when the implementation cannot determine account creation without an on-chain lookup, so the optionality is self-explaining (optional-field audit).
…execute convention Drops the bespoke MultisigExecuteQuote typedef and returns Omit<TransactionResult, 'hash'> from quoteExecuteProposal, matching how quoteSendTransaction/quoteTransfer relate to their execute counterparts in the base wallet. Makes 'execute return extends quote return' explicit (executeProposal returns the full TransactionResult) and removes a redundant type. Type-only; the type is unreleased (new in this PR).
Syncs the ValueError doc to the wording agreed on the reference PR ('Thrown when an argument fails validation'), keeping the shared error class definition byte-identical.
Resolves conflicts in the shared error module + root barrels against main (which merged tetherto#46's SDA taxonomy). main is a superset of our error additions, so took its versions: errors.js/errors.d.ts gain UnsupportedOperationError alongside our SignerError/ValueError/NoSuchElementError; index.js/types keep the 5-class export plus the new ISigner export and the beta.14 bump. The multisig interface family (unique to this branch) merged without conflict.
…nsfer and MultisigInfo.isCreated Per the interface design requirements: the multisig write surface is propose/proposeMessage/approve*/reject*/execute* only — transfer is not a separate interface member (propose is the generic replacement; concrete classes can still offer transfer via propose). Also removes MultisigInfo.isCreated, which fails the common-field criteria (not universal across multisig chains, and never actually undefined in practice).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Add common
IMultiSigWalletAccountandIWalletAccountMultisigReadOnlyinterface for multisig account wallets. This provides a unified API contract that all multisig implementations (EVM, Bitcoin, Solana, etc.) should follow.Related Issue
https://github.com/tetherto/wdk-protocol-multisig-safe/pull/2
Type of change