Skip to content

add interface for multisig account wallet#32

Open
quocle108 wants to merge 42 commits into
tetherto:mainfrom
quocle108:add-interface-multisig-account-wallet
Open

add interface for multisig account wallet#32
quocle108 wants to merge 42 commits into
tetherto:mainfrom
quocle108:add-interface-multisig-account-wallet

Conversation

@quocle108

@quocle108 quocle108 commented Feb 12, 2026

Copy link
Copy Markdown
Member

Description

Add common IMultiSigWalletAccount and IWalletAccountMultisigReadOnly interface 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

  • New feature (non-breaking change which adds functionality)

@quocle108
quocle108 changed the base branch from main to develop February 12, 2026 10:35
@quocle108
quocle108 force-pushed the add-interface-multisig-account-wallet branch from 9c47034 to 58d3535 Compare February 13, 2026 02:45

@Davi0kProgramsThings Davi0kProgramsThings left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well done, quoc. I left a few comments to improve the interfaces.

Comment thread src/wallet-account-multisig-read-only.js Outdated
Comment thread types/src/wallet-account-multisig-read-only.d.ts Outdated
Comment thread src/wallet-account-multisig-read-only.js Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread types/src/wallet-account-multisig.d.ts Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread src/wallet-account-multisig-read-only.js Outdated
Comment thread src/wallet-account-multisig-read-only.js Outdated
jonathunne and others added 2 commits February 14, 2026 22:16
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Co-authored-by: Davide Casale <davi0kprogramsthings@gmail.com>
Comment thread src/wallet-account-multisig-read-only.js Outdated
Comment thread src/wallet-account-read-only-multisig.js Outdated
Comment thread src/wallet-account-multisig-read-only.js Outdated
Comment thread src/wallet-account-multisig-read-only.js Outdated
quocle108 and others added 8 commits February 16, 2026 06:53
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
quocle108 force-pushed the add-interface-multisig-account-wallet branch from 86801f7 to 5ffcb43 Compare February 19, 2026 00:48
Comment thread types/src/wallet-account-multisig.d.ts Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread src/wallet-account-read-only-multisig.js Outdated
Comment thread src/wallet-account-multisig.js Outdated
Comment thread src/wallet-account-multisig.js Outdated
Co-authored-by: Jonathan Dunne <jonathanpdunne@gmail.com>
Comment thread src/wallet-account-multisig.js Outdated
quocle108 and others added 6 commits February 25, 2026 09:00
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().
Comment thread src/multisig/wallet-account-read-only-multisig.js Outdated
Comment thread src/multisig/wallet-account-read-only-multisig.js Outdated
Comment thread src/multisig/wallet-account-read-only-multisig.js
Comment thread src/multisig/wallet-account-read-only-multisig.js Outdated
Comment thread src/multisig/wallet-account-multisig.js Outdated
Comment thread src/multisig/i-multisig-transport.js Outdated
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.
@AlonzoRicardo
AlonzoRicardo changed the base branch from develop to main July 20, 2026 18:09
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).
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