FAQ
Short answers for common authoring, runtime, provider, and testnet questions.
Smart contract call failure
If broadcast returns a mandatory-script-verify-flag-failed error, one or more inputs failed script verification.
Common causes:
| Error shape | Likely cause |
|---|---|
Script evaluated without error but finished with a false/empty top stack element | A predicate returned false — signature mismatch, wrong preimage, failed timelock, or output covenant mismatch. |
Signature must be zero for failed CHECK(MULTI)SIG operation | Invalid signature for checkSig / checkMultiSig, wrong sighash, or wrong key material in invoke. |
OP_VERIFY failed | An explicit assert(...) boundary or fused verify helper rejected the path. |
Start with How to Debug a Contract:
- Inspect
bound.artifactand method ABI. - Run
methods.<name>.verify(...)locally. - Only then move to MemoryProvider or testnet deploy/call.
Double-spend and missing-input errors
You may see mempool or missing-input errors when retrying spends against stale UTXOs.
| Error | Typical meaning |
|---|---|
txn-mempool-conflict | You tried to spend a UTXO that is already being spent by another unconfirmed transaction. |
missing-inputs / Transaction invalid: missing-inputs | The UTXO was already confirmed in another spend. |
For developers
Providers can return UTXO sets that lag behind recent broadcasts, especially under heavy testnet traffic. Wait a few seconds and retry, or insert a short delay between consecutive calls:
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
await deployed.methods.increase.call({}, { provider, signer, invoke });
await sleep(2_000);
await deployedAgain.methods.increase.call({}, { provider, signer, invoke });
Persist deployed outpoints (txid, vout, satoshis`) and refresh them after every successful spend. See Interact with a Deployed Contract.
For dApp users
If two users spend the same contract instance concurrently, one transaction wins and the other fails against stale state. Refresh the latest UTXO and successor state before signing again.
TESTNET_WIF / private key errors
Deploy and testnet scripts require a funded WIF in the environment.
| Symptom | Fix |
|---|---|
Set TESTNET_WIF in your .env file before deploying. | Export a testnet key and add it to .env. |
Input string too short | The WIF variable is empty or malformed. |
No sufficient utxos | Fund the WIF address on OP_CAT testnet before deploying. |
Follow How to Deploy & Call for the guarded testnet flow. Never commit .env or hard-code WIFs in browser code.
Artifact or script mismatch after upgrade
If verification succeeds locally but the on-chain script hash does not match:
- Confirm you are using the same
@opcat-labs/lambitversion that produced the published artifact. - Re-run
lambit compileand compareartifact.hexand ABI metadata. - For stateful contracts, confirm constructor args and current state match the UTXO you attached.
Stateful successor state surprises
methods.<name>.next(state, args?) is pure. Use it to preview the successor state before you broadcast:
const next = counter.methods.increase.next({ count: 2n });
// { count: 3n }
If the spend fails after a successful next, check output covenants, sighash mode, and whether the method returns { outputs, check? } instead of { next, check? }. See Stateful Contracts and Sighash Types.
lambit compile / lambit artifact security
Both commands execute the target module to collect contract exports. Only compile trusted code in CI or on your own machine.
CLI test discovery outside this repo
lambit test wraps Mocha. Outside the Lambit package checkout, prefer an explicit config:
lambit test --config ./.mocharc.json
Use --no-config when you want Mocha config discovery disabled. See Installation.
Guarded testnet E2E
lambit test --testnet and npm run test:testnet are opt-in. Set LAMBIT_RUN_TESTNET_E2E=1 and a funded TESTNET_WIF only when you intend to spend testnet coins.
Porting from sCrypt / scrypt-ts-opcat
Translate class/decorator contracts into functional contract() definitions. Start with Basics for stateless ports and Stateful Contracts for @prop(true) state fields. The main shifts are immutable state returns ({ next, check? }), explicit prop schemas, and provider-driven deploy/call instead of mutable this writes.