Skip to main content
Signing quorum currently covers message signing on all supported chains and transaction signing on EVM and Solana. Bitcoin transactions are not covered. For the quorum model itself, the approval lifecycle, and the requirement fields, see Approvals.
Before this: create and initialize a Dynamic client (see Creating a Dynamic Client, Initializing the Dynamic Client). Governance requirements apply to account changes instead of signing. See Governance for actions such as adding a member or transferring ownership. approvalRequirements is a PolicyRules field. It composes with other rules in the same policy layer and uses the Account-Layer, Wallet-Layer, and Signer-Layer scope of that layer. The policy layer can further restrict the applicable chain and chain IDs.

Create a transaction quorum rule

The following rule requires one approval from a Business Account member with the admin role for native-token transfers of at least 1 ETH on Ethereum mainnet.
List one or more built-in roles such as admin, or custom Business Account roles. A member holding any listed role can approve. A value such as anyAdmin works only when the Business Account defines a custom role with that exact name.
Set eligibleApproverRoles explicitly. If you omit it, any verified approver role can satisfy the requirement. The initiator never counts toward requiredApprovals.

Requirement fields

getPolicy returns approvalRequirements with the same authoring shape. Approval progress is reported during signing. It is not part of the policy definition.

Limit a rule by transaction value

Use when.minValue to apply the requirement at or above a transaction value. Conditions in the same when object are combined with AND logic.
  • amount is an integer string in the asset’s smallest unit.
  • Omit asset for the chain’s native token.
  • An asset address selects that token.
Do not use when.initiatorRole or when.actionTypes to narrow a production rule. These fields are accepted but are not resolved during evaluation. A requirement that sets either field applies more broadly instead of being limited by that field.

The signing intent

Every quorum-gated signing request produces a SigningIntent: a signed, complete description of the operation that approvers review and that the resume step executes. The SDK prepares and signs it automatically when you initiate signing, and it is stored on the proposal so approvers see the same object the initiator signed.

Intent properties

Operation kinds

operation.kind discriminates the union and determines the remaining fields: For message, typedData, and raw intents, payload is the exact prepared payload the signature covers. For EVM transaction intents, limits instead carries the maxGas, maxFeePerGas, maxPriorityFeePerGas, and maxTotalFee ceilings described in Transaction fields the intent binds. A Solana transaction intent carries the compiled message itself, described in Solana transactions.

Complete a quorum-gated signing operation

The signing-quorum flow has three stages:
  1. The initiator attempts to sign. The SDK prepares and signs an operation-bound intent automatically.
  2. Other eligible Business Account members review and approve the resulting proposal.
  3. After every requirement is satisfied, the initiator resumes the proposal to produce the signature.

1. Start signing

Use a Business Account wallet with its standard signing methods, such as signMessage, signRawMessage, signTypedData, or signSerializedTransaction. When a matching quorum rule requires approval, the call rejects with SigningConsentRequiredError. Save its proposalId so you can find the proposal later.
The error’s reason tells you what consent is still missing:
  • APPROVALS_REQUIRED: a proposal opened and needs other members’ approvals. The error carries proposalId and approvalRequirements.
  • INTENT_REQUIRED: the operation’s intent still needs your session-key signature. This is rare because the SDK signs the intent automatically, but it can occur when the local client cannot produce the signature.
For INTENT_REQUIRED, sign the returned intent and retry the same call with the signed approvalQuorum. Pass the operation’s stored payload as preparedPayload for message, typed data, and raw signing so the intent is checked against your request before it is signed.
Every chain’s WaaS signing methods accept approvalQuorum for this retry, including signPsbt on Bitcoin and signSerializedTransaction on EVM. Omit it on the first call: populate it only in response to an INTENT_REQUIRED outcome, with the exact intent that was signed.

Transaction fields the intent binds

For an EVM transaction, approvers consent to the stable fields of the operation: from, to, value, data, chainId, and nonce. Those fields cannot change between the approved intent and the signed transaction. Only the gas and fee values may move, because a proposal can sit pending for as long as its completion window while gas prices drift. The intent carries four ceilings that bound how much the signed transaction may spend on gas: When you initiate signing, signSerializedTransaction defaults these limits to the values your unsigned transaction carries. Pass refreshLimits to approve wider ceilings up front, for example padded estimates, so the resumed signature still fits current network conditions after the proposal is approved:
At resume time the transaction is rebuilt from the approved operation with gas and fee values set at the approved limits, and wallet-service checks the transaction against those limits again. The approvers’ consent covers the operation’s identity and its spending ceiling, never a different destination, value, or calldata.

Solana transactions

Start a Solana transaction by calling signTransaction from @dynamic-labs-sdk/solana with a Business Account wallet. The SDK prepares and signs the intent automatically, and the call rejects with SigningConsentRequiredError when a quorum rule requires approval, as it does for EVM.
For a Solana transaction, approvers consent to the compiled message except its recent blockhash. The intent’s operation carries the fee payer, the message version ('legacy' or 'v0'), the message header, the static account keys, and every instruction with its program, accounts, and hex-encoded data. Compute-unit limits and priority fees are instructions, so they are bound too, and none of these can change between approval and signing. The SDK refuses to prepare an intent for a message it cannot bind completely. The message must be complete and unsigned, the wallet must be its fee payer, and it must not use address lookup tables or a durable nonce.

Refreshable fields

A proposal can wait for approvals for as long as its completion window, which is far longer than some chain values stay valid. If the intent bound those values at initiation, an approved proposal could become impossible to sign. The intent instead carries a signed lifetime marker that names the one value the SDK replaces with a current value when you resume. Because the marker is stored in the intent, an approver who inspects it can see which value will change. Every Solana transaction intent carries lifetime: { refreshable: 'blockhash' }. An EVM intent carries lifetime: { refreshable: 'nonce' } only when it was signed with a refreshable nonce. The intents that signSerializedTransaction prepares pin the nonce and have no lifetime. Message, typed data, raw, and EIP-7702 intents never have one. When you resume, the SDK reads the replacement value from the network the intent was approved for. For Solana it fetches the latest blockhash. For an EVM intent with a refreshable nonce it reads the account’s pending transaction count, so the nonce follows any transaction the account already sent. The SDK then rebuilds the transaction from the approved operation and signs it. The refreshed value is the only difference from what was approved, apart from EVM gas and fee values that stay within the approved limits. If the active network differs from the intent’s chainId, the resume throws SigningProposalNetworkMismatchError instead of reading a value from the wrong chain. EVM gas and fee values are separate from lifetime. They are the limits ceilings described in Transaction fields the intent binds, and Solana intents have no limits.

2. Approve the proposal

An eligible Business Account member other than the initiator approves the proposal from their own authenticated session.
The satisfied value is authoritative. Do not infer readiness from approved >= required because a requirement can also require specific roles. An approver can inspect the operation before approving it. getBusinessAccountSigningIntent reads the signed intent stored on a signing proposal, so your UI can show the message, typed data, or transaction the quorum is consenting to.
The helper returns status: 'unsupported' for proposals that are not signing operations and status: 'mismatch' when the stored intent does not match its proposal.

3. Resume the approved operation

Read the pending proposal again, verify that every requirement is satisfied, and resume it with the same Business Account wallet. resumeSigningProposal consumes the proposal and returns the signature. For an EVM transaction intent the signature is the serialized signed transaction, which you then broadcast yourself. For a Solana transaction intent the result is the hex-encoded signature alone, computed over a message that uses the latest blockhash the SDK fetched at resume time. The SDK does not return that blockhash, so the result is not a broadcastable Solana transaction.
resumeSigningProposal verifies that the proposal is pending and fully approved and that the selected wallet belongs to the proposal’s Business Account and matches the intent address. It throws a dedicated error when the proposal cannot be resumed:
  • SigningProposalNotPendingError when the proposal already left the pending state (executed, expired, or vetoed).
  • SigningProposalApprovalsRequiredError when its approvals are missing or unmet.
  • SigningProposalIntentUnavailableError when it has no valid stored intent.
  • SigningProposalWalletMismatchError when the wallet belongs to another business account; pass the walletAccount the intent names.
  • SigningProposalNetworkMismatchError when the wallet’s active network differs from the network the transaction intent was approved for, for a Solana transaction or an EVM transaction with a refreshable nonce.
The proposal is consumed when the resume succeeds. The intent stored on the proposal binds the signature to the operation, so resume the stored proposal rather than rebuilding the request.
Review the proposal’s stored operation before approving it, and test the complete initiation, approval, resume, and broadcast flow in a non-production environment before enabling a quorum rule in production.
Changes to approver roles, thresholds, or deadlines apply to subsequent evaluations. Start a new signing request after changing a quorum rule so the request is evaluated against the current policy.
Last modified on October 20, 2026