Publishing
This page shows the end-to-end path for publishing a NOOSChain smart contract: build the WASM, package it, sign it, generate transaction payloads, register the package when required, deploy code, instantiate the contract, grant bucket access, and call a method.
Use it after you understand Manifests, Authorization, Package Provenance, and the Contract Registry.
Publishing Flow
The core manual flow uses generated JSON payloads and normal transaction submission:
| Step | Purpose | Transaction |
|---|---|---|
| Register package | Approve a package/version/hash tuple when registry policy requires it. | REGISTER_CONTRACT_PACKAGE |
| Deploy WASM | Store bytecode by codeHash. | DEPLOY_CONTRACT_CODE |
| Instantiate | Create a contract instance from code plus manifest. | INSTANTIATE_CONTRACT |
| Grant bucket access | Grant bucket permissions to the deployed contract principal. | ADD_BUCKET_ACCESS_RULE |
| Call method | Invoke a manifest method. | CALL_CONTRACT |
Production releases may add release records, runtime gates, monitoring checks, and migration transactions. Keep those controls, but do not let them obscure the basic publish order above.
Prerequisites
Before publishing, make sure you have:
- a compiled WASM contract;
- a reviewed manifest;
- a user key that can sign transactions;
- package signer keys when provenance is required;
chain:adminauthority for registry, runtime, release, and migration transactions;- bucket ids and access policy decisions, if the contract uses buckets.
For AssemblyScript contracts, the starter docs are AssemblyScript SDK and Templates.
1. Build The Contract
For the bundled typed AssemblyScript example:
npm --prefix apps/noos-contract-sdk-as install
npm --prefix apps/noos-contract-sdk-as run build:typed-exampleThe compiled WASM is:
apps/noos-contract-sdk-as/build/typed-report-contract.wasmThe tutorial manifest is:
apps/noos-contract-sdk-as/examples/typed-report-contract/package/manifest.jsonFor your own contract, replace those paths with your build output and manifest. If you change either the WASM or the manifest, package and sign again.
2. Package The Contract
Run the generic package helper from the repository root:
npm run contracts:as-sdk:package -- --manifest "apps/noos-contract-sdk-as/examples/typed-report-contract/package/manifest.json" --wasm "apps/noos-contract-sdk-as/build/typed-report-contract.wasm" --out "contract-packages/report-store-0.1.0" --package-name "report-store" --package-version "0.1.0" --description "Stores report metadata and emits report events." --author-name "NOOS" --author-organization-id "org-noos" --license "UNLICENSED" --keyword "reports" --metadata-json '{"environment":"tutorial"}'The helper writes:
contract-packages/report-store-0.1.0/
noos-contract.json
manifest.json
build/
typed-report-contract.wasmnoos-contract.json is the package descriptor. It records package identity, runtime, WASM path, manifest path, WASM SHA-256 hash, and canonical manifest hash. That descriptor is what provenance signatures cover.
3. Sign The Package
Set package signer environment variables:
$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY = Get-Content ".secrets/user-admin.public.pem" -Raw
$env:NOOS_CONTRACT_PACKAGE_SIGNER_PRIVATE_KEY_PEM = Get-Content ".secrets/user-admin.private.pem" -Raw
$env:NOOS_CONTRACT_PACKAGE_SIGNER_TYPE = "user"
$env:NOOS_CONTRACT_PACKAGE_SIGNER_ID = "user-admin"Sign the descriptor:
npm run contracts:as-sdk:sign-package -- --package "contract-packages/report-store-0.1.0"The helper writes:
contract-packages/report-store-0.1.0/provenance/signatures.jsonSigning is required when the active provenance policy requires it. Even on optional networks, signing gives reviewers and operators an audit trail.
4. Generate Transaction Payloads
Generate unsigned transaction payload JSON:
npm run contracts:as-sdk:payloads -- --package "contract-packages/report-store-0.1.0" --contract-id "contract-report-store" --owner-user-id "user-admin" --organization-id "org-noos" --registry-id "pkg-report-store-0.1.0" --metadata-json '{"name":"report-store","version":"0.1.0"}' --approval-metadata-json '{"approvedFor":"tutorial"}'The helper writes:
contract-packages/report-store-0.1.0/payloads/
deploy-contract-code.json
instantiate-contract.json
register-contract-package.json
bucket-access-rules.json
add-bucket-access-rule-1.json| File | Used for |
|---|---|
deploy-contract-code.json | Transaction DEPLOY_CONTRACT_CODE. |
instantiate-contract.json | Transaction INSTANTIATE_CONTRACT; includes provenance when provenance/signatures.json exists. |
register-contract-package.json | Transaction REGISTER_CONTRACT_PACKAGE. |
bucket-access-rules.json | Review file containing all manifest-derived contract bucket grants. |
add-bucket-access-rule-*.json | One transaction ADD_BUCKET_ACCESS_RULE payload per generated bucket grant. |
If the manifest declares no buckets, bucket-access-rules.json contains an empty array and no bucket grant needs to be submitted for this contract.
5. Register The Package
Skip this step only when registry policy is optional and your environment does not require registry approval. Required registry policy needs accepted provenance and a matching registry entry.
Submit the generated registry payload:
npm run noos -- tx build-and-submit --type REGISTER_CONTRACT_PACKAGE --payload-file "contract-packages/report-store-0.1.0/payloads/register-contract-package.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction REGISTER_CONTRACT_PACKAGE.
Only chain:admin actors can register or update registry entries.
6. Deploy The WASM Code
Submit the generated deployment payload:
npm run noos -- tx build-and-submit --type DEPLOY_CONTRACT_CODE --payload-file "contract-packages/report-store-0.1.0/payloads/deploy-contract-code.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction DEPLOY_CONTRACT_CODE.
Deployment validates the module against the protocol-pinned Wasmtime runtime. Malformed WASM, forbidden imports, imported memory, and runtime mismatch fail before code is stored.
7. Instantiate The Contract
Submit the generated instantiate payload:
npm run noos -- tx build-and-submit --type INSTANTIATE_CONTRACT --payload-file "contract-packages/report-store-0.1.0/payloads/instantiate-contract.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction INSTANTIATE_CONTRACT.
Instantiation checks:
- deployed code exists for
codeHash; - manifest hash matches the manifest body;
- package provenance satisfies active provenance policy;
- registry approval satisfies registry policy;
- contract id does not already exist;
- owner user and organization are valid.
8. Grant Bucket Access
If the manifest declares buckets, submit each generated bucket access payload:
npm run noos -- tx build-and-submit --type ADD_BUCKET_ACCESS_RULE --payload-file "contract-packages/report-store-0.1.0/payloads/add-bucket-access-rule-1.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction ADD_BUCKET_ACCESS_RULE.
For bucket-sensitive methods to succeed:
- the manifest must declare the bucket and permission;
- the contract principal must have the bucket permission;
- the caller must satisfy method
invoke.allow; - the caller must have bucket permission where the host operation requires it.
9. Call The Contract
Create a call payload:
@'
{
"contractId": "contract-report-store",
"method": "saveReport",
"args": {
"id": "report-001",
"status": "submitted",
"score": 97
}
}
'@ | Set-Content "call-save-report.json"Submit it:
npm run noos -- tx build-and-submit --type CALL_CONTRACT --payload-file "call-save-report.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction CALL_CONTRACT.
After the call, inspect call results, events, readiness, and monitoring surfaces as needed.
After Instantiation
Check readiness before routing traffic:
npm run noos -- contracts readiness contract-report-store --refresh --jsonCheck monitoring after first calls:
$env:NOOS_OPERATOR_TOKEN="<operator-token>"
npm run noos -- contracts monitoring snapshot --json
npm run noos -- contracts monitoring alerts --json
npm run noos -- contracts monitoring prometheusThe snapshot is the dashboard payload. The alert feed is for alert managers. The Prometheus output is the scrape payload for call counts, failures, runtime errors, fuel exhaustion, readiness, sidecar status, and release status.
TypeScript SDK Alternative
Use the manual JSON payload path when operators, governance, or release automation need inspectable artifacts before submission. Use the TypeScript SDK path when application code or deployment automation should load, sign, and submit the package directly.
import { readFileSync } from "node:fs";
import {
NooschainClient,
PemTransactionSigner,
loadContractPackageFromDirectory,
} from "@nooschain/client";
const client = new NooschainClient({ baseUrl: "http://127.0.0.1:3000" });
const signer = new PemTransactionSigner({
publicKey: readFileSync(".secrets/user-admin.public.pem", "utf8"),
privateKeyPem: readFileSync(".secrets/user-admin.private.pem", "utf8"),
});
const contractPackage = await loadContractPackageFromDirectory(
"contract-packages/report-store-0.1.0",
);
await client.contracts.publishPackage({
signer,
nonce: "auto",
submit: true,
package: contractPackage,
contractId: "contract-report-store",
ownerUserId: "user-admin",
organizationId: "org-noos",
bucketAccess: [{
bucketId: "bucket-reports",
permissions: ["bucket:read_metadata"],
}],
signaturePolicy: {
requireSignature: true,
allowedSignerIds: ["user-admin"],
},
});When the package contains provenance/signatures.json, the SDK includes provenance in the transaction INSTANTIATE_CONTRACT payload.
Production Gates
For local development, the core workflow above is enough. For production, check runtime activation, rehearse the rollout, and keep release evidence.
Runtime Activation
Production networks can disable smart-contract deployment and calls through the runtime activation gate:
$env:NOOS_OPERATOR_TOKEN="<operator-token>"
npm run noos -- contracts runtime activation --jsonIf the status is disabled, you can still stage package and registry approval, but transactions DEPLOY_CONTRACT_CODE, INSTANTIATE_CONTRACT, and CALL_CONTRACT will fail. Governance must submit transaction SET_CONTRACT_RUNTIME_ACTIVATION with status active after runtime preflight passes.
Some networks also require rollout policy and validator attestations:
npm run noos -- contracts runtime rollout --json
npm run noos -- contracts runtime attestations --rollout-policy-id contract-runtime-rollout-v1 --jsonFor operator details, see Smart Contract Runtime Operations.
Rehearsals
Run the production rollout rehearsal before relying on the workflow:
npm run test:contract-production-rollout-rehearsalIt exercises package provenance, registry approval, readiness repair, calls, events, return values, C2C, migration, rollback, replay verification, and chain verification.
Before promoting to a multi-validator environment, run:
npm run test:contract-production-multinode-rehearsalIt repeats publish, instantiate, call, migrate, deactivate, rollback, and replay across two validator databases.
Release Records
Release records are a production release-control layer. They are not required for a minimal development publish, but production operators should create them before moving a package through launch or upgrade stages.
Create a release payload:
npm run noos -- contracts release create-payload --id "release-report-store-0.1.0" --release-type new --package-registry-id "pkg-report-store-0.1.0" --package-name "report-store" --package-version "0.1.0" --code-hash "<CODE_HASH>" --manifest-hash "<MANIFEST_HASH>" --descriptor-hash "<DESCRIPTOR_HASH>" --target-contract-id "contract-report-store" --audit-metadata-json '{"audit":"internal-pass"}' --approval-metadata-json '{"changeControlId":"CHG-1234"}' --out "contract-packages/report-store-0.1.0/payloads/create-release.json"Submit it:
npm run noos -- tx build-and-submit --type CREATE_CONTRACT_RELEASE --payload-file "contract-packages/report-store-0.1.0/payloads/create-release.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction CREATE_CONTRACT_RELEASE.
Check the release plan:
$env:NOOS_OPERATOR_TOKEN="<operator-token>"
npm run noos -- contracts release plan --id "release-report-store-0.1.0" --jsonFor a new contract, the normal progression is:
draft -> package_approved -> deployed -> instantiated -> readiness_checked -> traffic_ready -> completedFor an upgrade, include --source-contract-id, perform migration, deactivate the old contract, and complete:
draft -> package_approved -> deployed -> instantiated -> readiness_checked -> migration_planned -> migration_completed -> traffic_ready -> source_deactivated -> completedUpgrade Without State Migration
Contracts are immutable by code hash and manifest hash. Upgrade by publishing a new package and a new contract instance.
- Change the source.
- Build a new WASM.
- Update manifest
version, ABI, bucket requirements,maxFuel, or invoke policy as needed. - Package to a new folder, such as
contract-packages/report-store-0.2.0. - Sign the new package.
- Generate payloads with a new contract id, such as
contract-report-store-v2. - Register the new package if registry policy requires it.
- Deploy the new WASM.
- Instantiate the new contract.
- Submit bucket access rules for the new contract principal.
- Move callers to the new contract id.
Do not overwrite old package folders. Old blocks must remain replayable against the old code hash, manifest hash, and runtime version.
Direct State Migration
Use transaction MIGRATE_CONTRACT_STATE when a new contract id should receive state from an older contract id in one admin/governance transaction. Contracts cannot call this themselves and cannot write into another contract's isolated state namespace.
First publish and instantiate the new contract id, for example:
contract-report-store-v2Create a migration payload:
npm run noos -- contracts migration plan --id "migration-report-store-v1-to-v2" --source "contract-report-store" --target "contract-report-store-v2" --keys all --mode copy --overwrite reject --expected-source-code-hash "<V1_CODE_HASH>" --expected-target-code-hash "<V2_CODE_HASH>" --expected-source-manifest-hash "<V1_MANIFEST_HASH>" --expected-target-manifest-hash "<V2_MANIFEST_HASH>" --metadata-json '{"reason":"upgrade-to-v2"}' --out "migration-report-store-v1-to-v2.json"Use --keys all to copy every source key, or pass a comma-separated list:
--keys "lastReport,report:001,report:002"Use --mode copy to leave source state intact. Use --mode move only when the old contract should lose migrated keys after the target write succeeds. Use --overwrite reject for the safe default.
Dry-run the payload:
npm run noos -- contracts migration dry-run --payload-file "migration-report-store-v1-to-v2.json"Submit it:
npm run noos -- tx build-and-submit --type MIGRATE_CONTRACT_STATE --payload-file "migration-report-store-v1-to-v2.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction MIGRATE_CONTRACT_STATE.
Read the receipt:
npm run noos -- contracts migration receipt --id "migration-report-store-v1-to-v2"Keep the receipt with the old package, new package, manifests, and release record.
Resumable Migration Jobs
Use resumable jobs for larger upgrades. Jobs keep a sorted key-set hash, progress cursor, batch receipts, and finalization state.
The transaction sequence is:
| Step | Transaction |
|---|---|
| Optional proposal | PROPOSE_CONTRACT_STATE_MIGRATION |
| Optional approval | APPROVE_CONTRACT_STATE_MIGRATION |
| Create job | CREATE_CONTRACT_STATE_MIGRATION_JOB |
| Execute batch | EXECUTE_CONTRACT_STATE_MIGRATION_BATCH |
| Finalize job | FINALIZE_CONTRACT_STATE_MIGRATION_JOB |
Create a proposal payload:
@'
{
"proposalId": "proposal-report-store-v1-to-v2",
"id": "proposal-payload-report-store-v1-to-v2",
"sourceContractId": "contract-report-store",
"targetContractId": "contract-report-store-v2",
"stateKeys": ["lastReport"],
"kind": "schema-transform",
"mode": "copy",
"overwrite": "reject",
"batchSize": 50,
"transforms": [
{ "operation": "rename", "fromKey": "lastReport", "toKey": "reports:last" },
{ "operation": "set", "fromKey": "lastReport", "toKey": "reports:last", "path": "/formatVersion", "value": 2 }
],
"auditMetadata": { "review": "internal-upgrade-review" }
}
'@ | Set-Content "proposal-report-store-v1-to-v2.json"Submit and approve it:
npm run noos -- tx build-and-submit --type PROPOSE_CONTRACT_STATE_MIGRATION --payload-file "proposal-report-store-v1-to-v2.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction PROPOSE_CONTRACT_STATE_MIGRATION.
@'
{
"proposalId": "proposal-report-store-v1-to-v2",
"approvalMetadata": { "approvedFor": "production-upgrade" }
}
'@ | Set-Content "approve-report-store-v1-to-v2.json"
npm run noos -- tx build-and-submit --type APPROVE_CONTRACT_STATE_MIGRATION --payload-file "approve-report-store-v1-to-v2.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction APPROVE_CONTRACT_STATE_MIGRATION.
Create a job payload:
{
"id": "job-report-store-v1-to-v2",
"proposalId": "proposal-report-store-v1-to-v2",
"sourceContractId": "contract-report-store",
"targetContractId": "contract-report-store-v2",
"stateKeys": ["lastReport"],
"kind": "schema-transform",
"mode": "copy",
"overwrite": "reject",
"batchSize": 50,
"transforms": [
{ "operation": "rename", "fromKey": "lastReport", "toKey": "reports:last" },
{ "operation": "set", "fromKey": "lastReport", "toKey": "reports:last", "path": "/formatVersion", "value": 2 }
]
}Submit it:
npm run noos -- tx build-and-submit --type CREATE_CONTRACT_STATE_MIGRATION_JOB --payload-file "migration-job-report-store-v1-to-v2.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction CREATE_CONTRACT_STATE_MIGRATION_JOB.
Execute batches until the job is complete:
@'
{
"jobId": "job-report-store-v1-to-v2"
}
'@ | Set-Content "migration-job-batch.json"
npm run noos -- tx build-and-submit --type EXECUTE_CONTRACT_STATE_MIGRATION_BATCH --payload-file "migration-job-batch.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction EXECUTE_CONTRACT_STATE_MIGRATION_BATCH.
Finalize the job:
@'
{
"jobId": "job-report-store-v1-to-v2"
}
'@ | Set-Content "migration-job-finalize.json"
npm run noos -- tx build-and-submit --type FINALIZE_CONTRACT_STATE_MIGRATION_JOB --payload-file "migration-job-finalize.json" --signer-public-key "$env:NOOS_CONTRACT_PACKAGE_SIGNER_PUBLIC_KEY" --signer-private-key-path ".secrets/user-admin.private.pem" --yesThis submits transaction FINALIZE_CONTRACT_STATE_MIGRATION_JOB.
Supported transform operations are copy, rename, delete, setDefault, set, remove, and moveJsonPointer. Transform paths use JSON Pointer syntax such as /formatVersion or /metadata/status.
Contract-Defined WASM Migrations
Use a contract-defined migration when the new contract version needs WASM code to reshape state. The target contract declares the migration entrypoint in its manifest, and the migration job references that migrationId.
Manifest example:
{
"migrations": [
{
"id": "report-v1-to-v2",
"entrypoint": "migrateReportV1ToV2",
"fromVersion": "0.1.0",
"toVersion": "0.2.0",
"maxBatchSize": 50,
"maxFuel": 5000000
}
]
}AssemblyScript example:
import { MigrationOutput } from "@nooschain/contract-sdk-as";
export function migrateReportV1ToV2(ptr: i32, len: i32): i32 {
const inputJson = String.UTF8.decodeUnsafe(ptr, len);
const output = new MigrationOutput();
output.writeJson("reports:last", `{"formatVersion":2,"source":${inputJson}}`);
output.deleteSource("lastReport");
output.eventJson("migration.report.v2", "{\"formatVersion\":2}");
output.commit();
return 0;
}The entrypoint receives a JSON payload with migrationId, source/target contract ids, and rows. It must return MigrationOutput; direct storage writes are rejected for migration jobs. Create, execute, and finalize the job with the same resumable migration transactions above.
Failure behavior is deterministic:
- missing or undeclared
migrationIdfails; - traps and fuel exhaustion fail the batch transaction;
- malformed return values are rejected;
- direct storage or encrypted-record writes are rejected;
- delete requests outside the current batch are rejected;
- failed batches do not advance the cursor or apply partial writes.
For manifest details, see Manifest Migrations.
Checklists
Before submitting transactions:
- package descriptor has the expected package name and version;
codeHashmatches the WASM;manifestHashmatches the reviewed manifest;- provenance signatures are present when required;
- registry payload matches package name, version, code hash, manifest hash, and descriptor hash;
- signer has the permissions required for each transaction.
After instantiation:
- readiness passes on target nodes;
- bucket access rules exist for the contract principal;
- first call result and events match expectations;
- monitoring shows no readiness, runtime, fuel, or failure alerts.
Before migration:
- old and new packages are archived;
- source and target contract ids are correct;
- expected source/target code and manifest hashes are pinned;
- migration mode and overwrite policy are intentional;
- dry-run result is reviewed;
- rollback or pause plan is documented.
Verify Locally
Useful checks:
npm run test:contract-publishing-helpers
npm run test:contract-package-format
npm run contracts:as-sdk:test
npm run test:contract-state-migration
npm run test:contract-state-migration-jobs
npm run test:contract-defined-state-migration
npm run test:contract-production-monitoring-integration