Queue operations

Methods for creating, listing, and managing queue operations in B2BINPAY DeFi API, including multisig approvals, nonces, and execution control

Use these methods to list, inspect, and manage queue operations for a deployment, including multisig configuration changes, rejects, and signatures.

GET
/api/v1/deployments/{deploymentId}/operations

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

Query Parameters

page?number

Page number (starts from 1)

Range0 < value <= 9007199254740991
Default1
pageSize?number

Number of items per page (max 100)

Range0 < value <= 100
Default10
statuses?array<>
operationTypes?array<>
createdBy?string
sortOrder?string

Value in

  • "asc"
  • "desc"
createdAtFrom?string

Filter by createdAt date starting from this date

createdAtTo?string

Filter by createdAt date up to this date

Response Body

application/json

curl -X GET "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations"
{  "total": 100,  "page": 1,  "pageSize": 10,  "items": [    {      "id": "187654321987654321",      "executeOperationId": "0x123e4567e89b12d3a45642661417400000000000000000000000000000000000",      "nonce": "5",      "operationType": "MULTISIG_CONFIG_CHANGE",      "status": "PENDING",      "calls": [        {          "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",          "value": "0",          "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"        }      ],      "payload": {        "approvers": [          "0x123..."        ],        "threshold": 2      },      "signaturesCollected": 2,      "signaturesRequired": 3,      "txHash": "0x5555666677778888999900001111222233334444555566667777888899990000",      "userSigned": false,      "canSign": true,      "canExecute": false,      "canDelete": true,      "canReject": true,      "isBlocked": false,      "createdAt": "2024-01-01T00:00:00.000Z",      "createdBy": "0x0000000000000000000000000000000000000000",      "signatures": [        {          "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",          "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"        }      ],      "signatureBlob": "0x000000000000000000000000000000000000dead0041...",      "expiresAt": "2024-01-01T01:00:00.000Z",      "dappMetadata": {        "dappName": "string",        "dappUrl": "http://example.com",        "icon": "http://example.com"      }    }  ],  "nextExecutableNonce": "5"}
Empty
Empty
GET
/api/v1/deployments/{deploymentId}/operations/{operationId}

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
operationId*string

Entity ID

Response Body

application/json

curl -X GET "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/1234567890123456789"
{  "id": "187654321987654321",  "executeOperationId": "0x123e4567e89b12d3a45642661417400000000000000000000000000000000000",  "nonce": "5",  "operationType": "MULTISIG_CONFIG_CHANGE",  "status": "PENDING",  "calls": [    {      "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",      "value": "0",      "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"    }  ],  "payload": {    "approvers": [      "0x123..."    ],    "threshold": 2  },  "signaturesCollected": 2,  "signaturesRequired": 3,  "txHash": "0x5555666677778888999900001111222233334444555566667777888899990000",  "userSigned": false,  "canSign": true,  "canExecute": false,  "canDelete": true,  "canReject": true,  "isBlocked": false,  "createdAt": "2024-01-01T00:00:00.000Z",  "createdBy": "0x0000000000000000000000000000000000000000",  "signatures": [    {      "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",      "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"    }  ],  "signatureBlob": "0x000000000000000000000000000000000000dead0041...",  "expiresAt": "2024-01-01T01:00:00.000Z",  "dappMetadata": {    "dappName": "string",    "dappUrl": "http://example.com",    "icon": "http://example.com"  }}
Empty
Empty
DELETE
/api/v1/deployments/{deploymentId}/operations/{operationId}

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
operationId*string

Entity ID

Response Body

curl -X DELETE "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/1234567890123456789"
Empty
Empty
Empty
Empty
POST
/api/v1/deployments/{deploymentId}/operations/multisig-config

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/multisig-config" \  -H "Content-Type: application/json" \  -d '{    "approvers": [      "0x0000000000000000000000000000000000000000"    ],    "threshold": 1,    "calls": [      {        "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",        "value": "0",        "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"      }    ]  }'
{  "id": "187654321987654321",  "executeOperationId": "0x123e4567e89b12d3a45642661417400000000000000000000000000000000000",  "nonce": "5",  "operationType": "MULTISIG_CONFIG_CHANGE",  "status": "PENDING",  "calls": [    {      "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",      "value": "0",      "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"    }  ],  "payload": {    "approvers": [      "0x123..."    ],    "threshold": 2  },  "signaturesCollected": 2,  "signaturesRequired": 3,  "txHash": "0x5555666677778888999900001111222233334444555566667777888899990000",  "userSigned": false,  "canSign": true,  "canExecute": false,  "canDelete": true,  "canReject": true,  "isBlocked": false,  "createdAt": "2024-01-01T00:00:00.000Z",  "createdBy": "0x0000000000000000000000000000000000000000",  "signatures": [    {      "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",      "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"    }  ],  "signatureBlob": "0x000000000000000000000000000000000000dead0041...",  "expiresAt": "2024-01-01T01:00:00.000Z",  "dappMetadata": {    "dappName": "string",    "dappUrl": "http://example.com",    "icon": "http://example.com"  }}
Empty
Empty
Empty
POST
/api/v1/deployments/{deploymentId}/operations/reject

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/reject" \  -H "Content-Type: application/json" \  -d '{    "nonce": "0"  }'
{  "id": "187654321987654321",  "executeOperationId": "0x123e4567e89b12d3a45642661417400000000000000000000000000000000000",  "nonce": "5",  "operationType": "MULTISIG_CONFIG_CHANGE",  "status": "PENDING",  "calls": [    {      "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",      "value": "0",      "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"    }  ],  "payload": {    "approvers": [      "0x123..."    ],    "threshold": 2  },  "signaturesCollected": 2,  "signaturesRequired": 3,  "txHash": "0x5555666677778888999900001111222233334444555566667777888899990000",  "userSigned": false,  "canSign": true,  "canExecute": false,  "canDelete": true,  "canReject": true,  "isBlocked": false,  "createdAt": "2024-01-01T00:00:00.000Z",  "createdBy": "0x0000000000000000000000000000000000000000",  "signatures": [    {      "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",      "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"    }  ],  "signatureBlob": "0x000000000000000000000000000000000000dead0041...",  "expiresAt": "2024-01-01T01:00:00.000Z",  "dappMetadata": {    "dappName": "string",    "dappUrl": "http://example.com",    "icon": "http://example.com"  }}
Empty
Empty
Empty
POST
/api/v1/deployments/{deploymentId}/operations/{operationId}/sign

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
operationId*string

Entity ID

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X POST "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/1234567890123456789/sign" \  -H "Content-Type: application/json" \  -d '{    "signature": "string"  }'
{  "signaturesCollected": 2,  "signaturesRequired": 3,  "canExecute": false,  "userSigned": false,  "signatures": [    {      "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",      "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"    }  ],  "operationId": "187654321987654321"}
Empty
Empty
Empty
PATCH
/api/v1/deployments/{deploymentId}/operations/{operationId}/nonce

Authorization

ApiKeyAuth
x-api-key<token>

Provide API key in X-API-Key header

In: header

Path Parameters

deploymentId*string

UUID

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid
operationId*string

Entity ID

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

curl -X PATCH "https://example.com/api/v1/deployments/123e4567-e89b-12d3-a456-426614174000/operations/1234567890123456789/nonce" \  -H "Content-Type: application/json" \  -d '{    "nonce": "0"  }'
{  "id": "187654321987654321",  "executeOperationId": "0x123e4567e89b12d3a45642661417400000000000000000000000000000000000",  "nonce": "5",  "operationType": "MULTISIG_CONFIG_CHANGE",  "status": "PENDING",  "calls": [    {      "to": "0xa0b86a33e6c3b4c0b7b8c4e6f2d8e9f1a2b3c4d5",      "value": "0",      "data": "0xa9059cbb000000000000000000000000742d35cc6635c0532925a3b8d0a7f79ec35bdc52"    }  ],  "payload": {    "approvers": [      "0x123..."    ],    "threshold": 2  },  "signaturesCollected": 2,  "signaturesRequired": 3,  "txHash": "0x5555666677778888999900001111222233334444555566667777888899990000",  "userSigned": false,  "canSign": true,  "canExecute": false,  "canDelete": true,  "canReject": true,  "isBlocked": false,  "createdAt": "2024-01-01T00:00:00.000Z",  "createdBy": "0x0000000000000000000000000000000000000000",  "signatures": [    {      "user": "0x111d35cc6635c0532925a3b8d0a7f79ec35bdc11",      "sign": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"    }  ],  "signatureBlob": "0x000000000000000000000000000000000000dead0041...",  "expiresAt": "2024-01-01T01:00:00.000Z",  "dappMetadata": {    "dappName": "string",    "dappUrl": "http://example.com",    "icon": "http://example.com"  }}
Empty
Empty

Sign a queue operation with a private key (EIP-712)

Queue operations are signed using EIP‑712 typed data. The signature is created off‑chain with a raw private key, without a wallet UI, and authorizes execution of a multisig operation on‑chain.

What is signed

Only the following data is signed:

Execute {
  Call[] calls;
  uint256 nonce;
}

No other fields from the queue operation are included in the signature.

Input data sources

From queue operation (API)

To build the signed payload, load the queue operation from the API:

  • GET /api/v1/deployments/{deploymentId}/operations
  • GET /api/v1/deployments/{deploymentId}/operations/{operationId}

From the queue operation object, use only:

{
  "nonce": "1",
  "calls": [
    {
      "to": "0xf127e5b7666f51aa346f374213113298014f5969",
      "value": "100000000000000",
      "data": "0x"
    }
  ]
}

When building the typed data:

  • Treat nonce as uint256.
  • Treat value as uint256.
  • Treat data as a hex‑encoded bytes value (the literal "0x" is valid for empty data).

From deployment and network

The EIP‑712 domain uses deployment and network data:

  • name — always MultiSigWallet.
  • version — current smart contract version.
  • chainId — blockchain chain ID of the deployment.
  • verifyingContract — address of the multisig contract.

You can obtain verifyingContract from the account:

  • GET /api/v1/accounts
  • GET /api/v1/accounts/{accountId}

Use the value from the account.contract field for the multisig contract address.

EIP-712 typed data structure

The exact typed data that is signed has the following structure:

{
  "domain": {
    "name": "MultiSigWallet",
    "version": "1.0.0",
    "chainId": "11155111",
    "verifyingContract": "0x71db8821df07d95f35d7c3bef22987397a965060"
  },
  "primaryType": "Execute",
  "types": {
    "EIP712Domain": [
      { "name": "name", "type": "string" },
      { "name": "version", "type": "string" },
      { "name": "chainId", "type": "uint256" },
      { "name": "verifyingContract", "type": "address" }
    ],
    "Execute": [
      { "name": "calls", "type": "Call[]" },
      { "name": "nonce", "type": "uint256" }
    ],
    "Call": [
      { "name": "to", "type": "address" },
      { "name": "value", "type": "uint256" },
      { "name": "data", "type": "bytes" }
    ]
  },
  "message": {
    "calls": [
      {
        "to": "0xf127e5b7666f51aa346f374213113298014f5969",
        "value": "100000000000000",
        "data": "0x"
      }
    ],
    "nonce": "1"
  }
}

Use this structure as a template. Do not change field names, types, or their order when building the typed data object.

Signing algorithm

Step 1. Build EIP-712 typed data

  • Use the structure shown above with domain, types, primaryType, and message.
  • Encode all numeric values (chainId, nonce, value) as uint256.

Step 2. Compute the EIP-712 digest

The digest is computed as:

keccak256(
  "\x19\x01" ||
  hashDomain(domain) ||
  hashStruct(Execute(message))
)

Standard EIP‑712 libraries perform this step automatically when you sign typed data.

Step 3. Sign the digest with a private key

Sign the digest using ECDSA over secp256k1:

signature = sign(digest, privateKey)

The resulting signature has the format:

0x{r}{s}{v}

Where:

  • r — 32 bytes.
  • s — 32 bytes.
  • v — 1 byte.

Example signature

Example of a valid signature value:

0xf8d5a66ed464b5d39bf2b3f6c45932c901467b84bdfc4d534a24dcc532569bf3\
27b3f19289912d223f69aceeab7a61edbffb1fc26d755e1db53f68263cbe03491b

Submit the signature to the API

After computing the signature, submit it using the Sign operation endpoint:

POST /api/v1/deployments/{deploymentId}/operations/{operationId}/sign
x-api-key: {your-api-key}
Content-Type: application/json
Accept: application/json

{
  "signature": "0x..."
}

On success, the API returns the updated signature status for the operation. If the same signer submits another signature for the same operation, the API returns a conflict error.

Common errors when signing

Common issues when building or submitting signatures include:

  • Invalid signature — incorrect domain (chainId or verifyingContract do not match the deployment).
  • Invalid signature — wrong data types in the message (for example, nonce passed as a string instead of uint256 in the typed data).
  • Invalid signaturecalls array order does not match the operation in the queue.
  • You have already signed this operation — the same address already submitted a signature.
  • canSign = false in the operation — the signer address is not an approver or is not allowed to sign.

Summary

  • Extract calls[] and nonce from the queue operation.
  • Build the EIP‑712 Execute typed data (domain, types, message).
  • Sign the EIP‑712 digest with a private key.
  • Submit the resulting signature to the B2BINPAY DeFi API.

Execute a READY queue operation with a private key

When a queue operation reaches the READY status and canExecute = true, you execute it by sending a regular Ethereum transaction to the deployed MultiSigWallet contract and calling:

function execute(Operation[] operations) external returns (bytes[][] results);

struct Operation {
  Call[] calls;
  bytes signatures; // packed signatures
  bytes32 id;
}

struct Call {
  address to;
  uint256 value;
  bytes data;
}

Preconditions

The queue operation must satisfy all of the following:

  • status = "READY".
  • canExecute = true.
  • signaturesCollected >= signaturesRequired.
  • The signatures array in the API response contains at least the threshold number of signatures.

Required inputs

From API (queue operation)

  • executeOperationId — used as Operation.id.
  • calls[] — used as Operation.calls.
  • signatures[] — used to build packed bytes for Operation.signatures.

From deployment and network

  • verifyingContract — multisig contract address for the deployment:
    • GET /api/v1/accounts
    • GET /api/v1/accounts/{accountId}
    • use account.contract.
  • chainId — chain ID of the network where the multisig is deployed.
  • rpcUrl — RPC endpoint for sending the transaction.
  • executorPrivateKey — private key of the externally owned account (EOA) that sends the transaction.

Build Operation.calls

Convert each API call object into the Solidity Call struct:

  • toCall.to.
  • value (decimal string) → Call.value (uint256).
  • data (hex string) → Call.data (bytes).

Keep the order of calls exactly the same as in the queue operation and in the EIP‑712 signing step.

Build Operation.signatures (packed bytes)

In the API response, signatures are returned as separate entries:

"signatures": [
  { "user": "0x...", "sign": "0x<65 bytes>" }
]

The contract expects a single bytes value:

bytes signatures; // NOT bytes[]

Signature format

Each signature is a standard 65‑byte ECDSA signature:

r (32 bytes) || s (32 bytes) || v (1 byte)

For example:

0xf8d5...3491b

Packing rule

Build Operation.signatures as:

packedSignatures = sig1 || sig2 || ... || sigN

Sort signatures by signer address in ascending alphabetical order before concatenation.

Build the operations array

Even if you execute a single queue operation, you must pass an array with one element:

operations = [
  Operation({
    calls: [...],
    signatures: packedSignatures,
    id: executeOperationId
  })
];

ABI-encode execute(operations)

Encode the function call data for:

execute((Call[] calls, bytes signatures, bytes32 id)[] operations)

This produces the transaction data field that you send to the multisig contract.

Build, sign, and broadcast the Ethereum transaction

Transaction fields

Set the transaction fields as follows:

  • to — multisig contract address (verifyingContract).
  • data — ABI‑encoded execute(operations) call.
  • value0.
  • chainId — correct chain ID (for example, Sepolia 11155111).
  • Gas parameters — EIP‑1559 fields (maxFeePerGas, maxPriorityFeePerGas) appropriate for the network.
  • nonce — EOA nonce of the executor account (this is not the multisig queue nonce).

Sign

Sign the transaction with executorPrivateKey using ECDSA (secp256k1).

Broadcast

Send the raw signed transaction through the RPC endpoint, for example using eth_sendRawTransaction. The result is a txHash.

Expected on-chain result

If the transaction succeeds:

  • The contract verifies the packed signatures internally (for example, via checkSignatures(hash, signatures)).
  • All calls are executed in order.
  • An ExecuteSuccess(nonce, digest, id) event is emitted.
  • The function returns operation and call‑level results as bytes[][] results.

The backend then updates the queue operation:

  • status changes to EXECUTED.
  • txHash is populated with the resulting on‑chain transaction hash.

Common reverts and errors

Common revert classes when executing operations include:

  • InsufficientSignatures(signatures, threshold) — packed signatures contain fewer signatures than the required threshold.
  • InvalidSignature(owner) — signature bytes, signed digest, or ordering are incorrect for at least one signer.
  • DuplicateSignature(owner) — the same signer appears more than once in the packed signatures.
  • FailedCall — one of the internal calls reverted.
  • InsufficientBalance(balance, needed) — the multisig contract lacks enough ETH for the value transfers.
  • ReentrancyGuardReentrantCall — a reentrancy attempt was detected during execution.

Dernière mise à jour le