TypeScript helpers for working with deployed Midnight Compact contracts.
This package wraps the lower-level Midnight JS contract APIs with a small, typed interface for:
- deploying a compiled contract
- joining an already deployed contract
- calling transaction circuits dynamically
- observing public and private contract state together
- building compiled contract objects from a contract class, witnesses, and compiled assets
npm install nite-apiPeer runtime expectations:
- Node.js 18+
- a Midnight-compatible provider setup
- a compiled Compact contract and its assets
DynamicContractAPI is the main wrapper around Midnight's deployed contract APIs.
It provides:
DynamicContractAPI.deploy(...)DynamicContractAPI.join(...)api.callTx(...)api.contractStateapi.deployedContractAddress
Helper for creating a CompiledContract from:
- a contract tag
- a contract class
- witness implementations
- a compiled assets path
import { DynamicContractAPI, utils } from 'nite-api';
const compiledContract = utils.createCompiledContract('counter', CounterContract, witnesses, './artifacts/counter');
const api = await DynamicContractAPI.deploy({
providers,
compiledContract,
privateStateId,
initialPrivateState,
args: [],
logger,
});
await api.callTx('increment' as any, txContext, 1n);import { utils } from 'nite-api';
const compiledContract = utils.createCompiledContract(
'my-contract',
MyContract,
witnesses, // Pass an empty object if no witness is used by your contract
'./artifacts/my-contract',
);NB: The type for your providers should be constructed as follow DynamicProviders<TContractType, typeof yourPrivateStateId>
import { DynamicProviders } from 'nite-api';
const walletAndMidnightProvider = await createWalletAndMidnightProvider(walletCtx);
const zkConfigProvider = new NodeZkConfigProvider<TCircuitId>("./path to your compiled artifact");
const providers: DynamicProviders<TContractType, typeof yourPrivateStateId> = {
privateStateProvider: levelPrivateStateProvider<typeof yourPrivateStateId>({
privateStateStoreName: contractConfig.privateStateStoreName,
walletProvider: walletAndMidnightProvider,
}),
publicDataProvider: indexerPublicDataProvider(config.indexer, config.indexerWS),
zkConfigProvider,
proofProvider: httpClientProofProvider(config.proofServer, zkConfigProvider),
walletProvider: walletAndMidnightProvider,
midnightProvider: walletAndMidnightProvider,
};import { DynamicContractAPI } from 'nite-api';
const api = await DynamicContractAPI.deploy<TContractType, typeof yourPrivateStateId>({
providers,
compiledContract,
privateStateId,
initialPrivateState,
args: [],
logger,
});
console.log(api.deployedContractAddress);If your contract does not require private state during deployment:
const api = await DynamicContractAPI.deploy<TContractType, typeof yourPrivateStateId>({
providers,
compiledContract,
args: [],
});const api = await DynamicContractAPI.join<TContractType, typeof yourPrivateStateId>({
providers,
compiledContract,
contractAddress: '...',
privateStateId,
initialPrivateState,
logger,
});callTx forwards to the underlying Midnight deployedContract.callTx map. It also automatically detects a union of all callable contract circuit names and their arguments, to be passed sequentially.
await api.callTx('increment', 1n);
await api.callTx('transfer', recipient, amount);
await api.callTx('ping');
await api.callTx('ping', txContext, user); //Allowed to pass a transaction contextThe exact arguments depend on the generated circuit function types from your Midnight contract bindings. If a circuit takes no parameters, call it with just the circuit name.
contractState is an RxJS observable that emits:
[publicContractState, privateStateOrNull];Example:
import ledger from '/path to your compiled contract';
const subscription = api.contractState.subscribe(([publicState, privateState]) => {
console.log('public', publicState);
/* Format contract state using ledger() generated as artifact for compiled contract */
const ledgerState = ledger(publicState.data);
console.log('private', privateState);
});
subscription.unsubscribe();If no privateStateId is supplied, the second tuple item is null.
Deploys a new contract instance and returns a DynamicContractAPI.
Important fields:
providers: Midnight provider setcompiledContract: compiled contract objectprivateStateId: optional private state identifierinitialPrivateState: optional initial private stateargs: optional contract initialization argumentslogger: optionalpinologger
Attaches to an already deployed contract and returns a DynamicContractAPI.
Important fields:
providers: Midnight provider setcompiledContract: compiled contract objectcontractAddress: deployed contract addressprivateStateId: optional private state identifierinitialPrivateState: optional initial private statelogger: optionalpinologger
Calls a transaction circuit from the underlying deployed contract.
Behavior:
- throws if
circuitNameis not found - forwards all arguments to the underlying circuit function
- allows zero arguments for circuits that do not accept parameters
RxJS Observable<[ContractState, PrivateState | null]>
Behavior:
- subscribes to public contract state updates
- resolves private state once when
privateStateIdis present - emits
nullfor private state when noprivateStateIdis provided - shaped/formated using the
ledger()provided in compiled contract artifact
Install dependencies:
npm installBuild:
npm run buildRun tests:
npm testWatch tests:
npm run test:watchBefore publishing to npm, verify:
package.jsonhas correctname,version,description,repository,homepage,bugs,keywords, andauthor- the build output in
dist/is current README.mdreflects the published API- tests pass
- exported types and paths are correct
Recommended publish flow:
npm test
npm run build
npm version patch
npm publishNotes:
npm version patchrequires a clean git working tree- use
npm version minorornpm version majorwhen appropriate - if you need to bump the version without creating a git tag or commit, use
npm version patch --no-git-tag-version
- This package is ESM-only.
- It is a thin wrapper over Midnight SDK contracts, so consumers still need the relevant Midnight runtime setup.
callTxargument types come from the generated Midnight contract bindings, not from this package alone.
MIT