Skip to main content
What you’ll be able to do
  • Request a validator signature for a document approval.
  • Verify the signature against the approved message and network signing key.
The FrostRequests queue is not on Metal Testnet yet. Follow this guide on a network that has code at its address.
A threshold signature is a single signature created jointly by validators. Each validator holds a share of the signing key, and a minimum number of them, called the threshold, must participate. The result can be verified with one public key for the group. This distributes signing authority across validators. Applications can use these signatures to send a document approval to another service or authorize a transaction on a chain that supports the chosen signature format. FrostRequests is Metal’s contract for queuing these signing requests. An application contract admitted by the chain authority validates the action and submits its message to the queue. Validators sign it after the request’s block finalizes. This guide requests a signature for a document approval, retrieves it, and verifies it against the group’s public key.

Obtain network and requester access

Obtain the network’s signing configuration, Ed25519 group public key, and HTTPS endpoint for signing results from the network operator. Use the signing result service to retrieve signatures, since public observer nodes do not retain them. Check for code at the queue, 0x000000000000000000000000000000000000f057, and arrange authority approval for your requester contract. Verify the group key, its network, and its signing suite through an authenticated operator channel, and use that channel to verify later key changes. This guide uses suite 1, named ed25519, with a 32-byte group key and a 64-byte Ed25519 signature. Suite 0, named secp256k1-tr, uses BIP-340 and requires a different verifier.

Authorize and bind the message

The DocumentApproval requester records one document approval per application action ID. Its owner approves a nonzero document hash once, and a repeated signing request uses that same approval. Adapt the owner’s authorization rule to your application’s requirements before the contract is admitted to the queue.
Deploy the contract, then have the authority admit its address. Read allowedRequesters(application) on the queue and require true before submitting an approval transaction. The queue rejects messages outside 1–4096 bytes and unknown suite IDs. If queue admission fails, the whole authorize transaction reverts, including its approval state. The signed message binds an application version, chain ID, requester address, action ID, and document hash. A consumer checks those fields and records the action ID when it applies the approval, preventing the same approval from being applied twice.

Submit and record the request

Assign a unique actionId and compute documentHash from the exact document bytes to approve. Use the SDK signing procedure to prepare authorize(actionId, documentHash), with the estimated fee. Persist its signed transaction hash and bytes before broadcast. After a successful receipt, follow canonical finality verification and settlement semantics. Decode SignatureRequested only from the queue’s address and Authorized only from the admitted requester. Compare the exact message bytes with the locally encoded message:
application is the admitted requester address. Require the queue event’s suite to equal 1, its message to equal message, and its ID to equal requestId. Read approvedDocument(actionId) and messageFor(actionId) at the receipt block and compare them with the saved inputs. Once the request is finalized, poll for its signature.

Poll the result service

Send this JSON-RPC request to the operator-provided endpoint for signing results, replacing the ID with the saved 32-byte request ID:
A completed response contains suite, signature, and groupKey in result, with bytes hex-encoded. Match the response to the RPC request ID and verify its signature against the message saved for that request. result: null means the service has no stored result, either because signing is pending or because the result is unavailable. Set a polling deadline and leave the application approval pending if it expires. Keep the same request ID through RPC errors and restarts. The method frost_groupKey accepts ["ed25519"] and returns the service’s key or null. Compare that key with the authenticated operator configuration to check that the service uses the expected key.

Verify the exact bytes and trusted key

Use Node.js’s Ed25519 verifier with trustedGroupKey from the authenticated operator configuration. It checks the result’s suite and key, then verifies the signature over the saved message bytes.
Pass the raw ABI-encoded message bytes to verification. Record the authenticated key identifier, suite, request ID, message, signature, and finalized request transaction. Apply the approval only after all checks pass.

Check duplicates and recovery

Call requestAgain(actionId) to test a duplicate request. It emits another queue event for the same suite and message, so the request ID stays the same and validators can return the existing signature. Verify that approvedDocument stays unchanged and only the original Authorized event exists. A different suite or message produces a different ID and requires separate application authorization. If submission times out, query the saved transaction hash to recover its receipt. Resume signature polling with the saved request ID after an interruption. Test a wrong key, changed message, malformed signature, pending result, and duplicate action, then verify a network signature against the saved message and authenticated group key. For a payment on another chain, the signed message must follow that chain’s transaction format and authorization rules. Apply its replay, broadcast, and finality rules when submitting the transaction.