Low-level signing
The two-phase sign API: prepare, submit, poll.
Without the crypto feature, mpckit exposes the on-chain signing
flow as three direct methods. You drive the centralized signature
math from your own crypto pipeline and feed bytes back to the backend.
let prepare = api.sign_prepare(/* args */).await?;
let submit = api.sign_submit(/* args */).await?;
let req = api.get_sign_request(&submit.sign_request_id).await?;Stages
Prepare
let prepare = api.sign_prepare(SignPrepareArgs {
dwallet_id: dwallet_id.into(),
curve: Curve::Secp256k1,
signature_algorithm: SignatureAlgorithm::EcdsaSecp256k1,
hash_scheme: Hash::Sha256,
}).await?;The backend mints a presign (off-chain attestation) and returns the session id plus the bytes you need for the centralized math.
Submit
let submit = api.sign_submit(SignSubmitArgs {
sign_session_id: prepare.sign_session_id.clone(),
user_message_bytes: /* output of your centralized sign step */,
message: message.to_vec(),
}).await?;The backend submits the on-chain sign session and returns the
sign_request_id.
Poll
use mpckit::SignRequestStatus;
loop {
let req = api.get_sign_request(&submit.sign_request_id).await?;
match req.status {
SignRequestStatus::Completed => break req.signature.expect("signed"),
SignRequestStatus::Failed => return Err(/* surface error */),
_ => tokio::time::sleep(std::time::Duration::from_millis(500)).await,
}
}Once status is Completed, req.signature holds the raw signature
bytes. Encoding to a chain-native format is your responsibility.
Idempotency
SignSubmitArgs accepts an idempotency_key: Option<String>. Pass a
UUID-v7 (use new_idempotency_key()) so a
retried sign_submit returns the original sign_request_id instead
of triggering a second on-chain session.