Reference integration for signing Midnight Network unshielded transactions with a Turnkey-held secp256k1 Schnorr key.
A developer can custody a user's Midnight unshielded signing key inside a Turnkey enclave, derive the corresponding Midnight address, and route unshielded transaction signing through Turnkey — without exposing the secret key. The standard Midnight wallet SDK is unchanged; a single provider swap delivers Turnkey's policy engine, audit log, key lifecycle, and enclave-backed signing to any Midnight application.
This repository contains:
- A working Midnight CLI that flips between local-key and Turnkey-key signing.
- A drop-in
UnshieldedKeystoreimplementation backed by Turnkey. - Reproducible end-to-end scripts that prove Turnkey signatures verify under the same Midnight verifying key.
- A long-form integration note for the Midnight team at
docs/signing.md.
npm install
cp .env.example .env # fill in Turnkey credentials + signing identity
npm run turnkey:e2e # standalone proof: Turnkey-signed payload, local verify
npm run turnkey:smoke # exercise the same wiring the CLI usesExpected tail of npm run turnkey:e2e:
signWith : bc1p…
x-only pubkey (32B) : c11b4f48…0706f87
Midnight addr (hex) : 86eac943…967615f4
Midnight bech32 : mn_addr_undeployed1sm4vjsu…zmqpe3
signature (64B) : 031e3c9c…cf1914f
schnorr.verify : true
Midnight unshielded signing is Schnorr over secp256k1: a 32-byte x-only
verifying key, a 64-byte r‖s signature, applied to the SHA-256 digest of
the transaction payload. A Turnkey secp256k1 Schnorr-capable account
produces exactly that signature shape via signRawPayload with
HASH_FUNCTION_NO_OP. Deriving the Midnight verifying key from the same
public key Turnkey publishes for the signing identity keeps the two sides
aligned.
sequenceDiagram
participant App as Developer app
participant Wallet as Midnight Wallet SDK
participant Signer as TurnkeyNightSigner
participant TK as Turnkey enclave
participant MN as Midnight node
App->>Wallet: build unshielded transaction
Wallet->>Signer: signData(payload)
Signer->>Signer: SHA-256(payload)
Signer->>TK: signRawPayload(digest, NO_OP)
TK-->>Signer: { r, s }
Signer-->>Wallet: 64-byte r‖s signature
Wallet->>MN: submit signed transaction
Full architectural detail, sequence diagrams, key-alignment notes, hash-
function table, and recommended follow-ups for the Midnight team:
docs/signing.md.
- Provision a Turnkey Midnight signing key.
- Derive the Midnight unshielded address from the Turnkey-published public key.
- Sign Midnight unshielded transactions and arbitrary messages with Turnkey.
- Validate the signature locally against Midnight's verifying key.
- Route the Midnight wallet SDK through a Turnkey-backed
UnshieldedKeystorevia a single environment flag (MIDNIGHT_NIGHT_SIGNER=turnkey). - Combine the Midnight account with Ethereum, Solana, Bitcoin, and other Turnkey-supported chain accounts in the same wallet.
Shielded (Zswap) and Dust signing remain local. Those secrets are consumed
inside zero-knowledge proofs, which Turnkey does not expose a primitive for
today. The hybrid model — local shielded + Turnkey unshielded — is the
production-ready posture and is what this repository demonstrates.
docs/signing.md lists the
small wallet-SDK additions that would extend Turnkey custody to the
shielded/Dust legs.
src/
cli.ts Working Midnight CLI; resolves signer from MIDNIGHT_NIGHT_SIGNER.
night-signer.ts localNightSigner + turnkeyNightSigner (UnshieldedKeystore impls).
setup.ts End-to-end devnet bootstrap.
deploy.ts Compiles and deploys the bundled contract.
network.ts Network resolution, seed persistence, deploy records.
check-balance.ts Wallet balance helper.
scripts/
turnkey-night-e2e.ts Standalone Turnkey-sign / local-verify proof.
cli-night-smoke.ts Exercises the CLI's resolver path without the proof server.
e2e-check.ts Full devnet contract round-trip (proof server required).
contracts/
hello-world.compact Minimal Compact contract used by the CLI demo.
docs/
signing.md Integration note for the Midnight team.
- Node 22+
- A Turnkey organization and API key pair (Settings → API Keys)
- A
CURVE_SECP256K1Turnkey signing identity, ideally as a P2TR wallet account so the bech32m form encodes the x-only verifying key
For the full Midnight CLI demo (npm run cli) you additionally need:
- Docker with Compose v2
- The Compact compiler version pinned by
create-mn-app
The standalone Turnkey scripts (turnkey:e2e, turnkey:smoke) do not need
Docker or the Compact compiler.
MIT