Skip to content

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:

StepPurposeTransaction
Register packageApprove a package/version/hash tuple when registry policy requires it.REGISTER_CONTRACT_PACKAGE
Deploy WASMStore bytecode by codeHash.DEPLOY_CONTRACT_CODE
InstantiateCreate a contract instance from code plus manifest.INSTANTIATE_CONTRACT
Grant bucket accessGrant bucket permissions to the deployed contract principal.ADD_BUCKET_ACCESS_RULE
Call methodInvoke 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:admin authority 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:

powershell
npm --prefix apps/noos-contract-sdk-as install
npm --prefix apps/noos-contract-sdk-as run build:typed-example

The compiled WASM is:

text
apps/noos-contract-sdk-as/build/typed-report-contract.wasm

The tutorial manifest is:

text
apps/noos-contract-sdk-as/examples/typed-report-contract/package/manifest.json

For 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:

powershell
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:

text
contract-packages/report-store-0.1.0/
  noos-contract.json
  manifest.json
  build/
    typed-report-contract.wasm

noos-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:

powershell
$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:

powershell
npm run contracts:as-sdk:sign-package -- --package "contract-packages/report-store-0.1.0"

The helper writes:

text
contract-packages/report-store-0.1.0/provenance/signatures.json

Signing 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:

powershell
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:

text
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
FileUsed for
deploy-contract-code.jsonTransaction DEPLOY_CONTRACT_CODE.
instantiate-contract.jsonTransaction INSTANTIATE_CONTRACT; includes provenance when provenance/signatures.json exists.
register-contract-package.jsonTransaction REGISTER_CONTRACT_PACKAGE.
bucket-access-rules.jsonReview file containing all manifest-derived contract bucket grants.
add-bucket-access-rule-*.jsonOne 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:

powershell
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" --yes

This 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:

powershell
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" --yes

This 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:

powershell
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" --yes

This 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:

powershell
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" --yes

This 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:

powershell
@'
{
  "contractId": "contract-report-store",
  "method": "saveReport",
  "args": {
    "id": "report-001",
    "status": "submitted",
    "score": 97
  }
}
'@ | Set-Content "call-save-report.json"

Submit it:

powershell
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" --yes

This submits transaction CALL_CONTRACT.

After the call, inspect call results, events, readiness, and monitoring surfaces as needed.

After Instantiation

Check readiness before routing traffic:

powershell
npm run noos -- contracts readiness contract-report-store --refresh --json

Check monitoring after first calls:

powershell
$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 prometheus

The 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.

ts
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:

powershell
$env:NOOS_OPERATOR_TOKEN="<operator-token>"
npm run noos -- contracts runtime activation --json

If 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:

powershell
npm run noos -- contracts runtime rollout --json
npm run noos -- contracts runtime attestations --rollout-policy-id contract-runtime-rollout-v1 --json

For operator details, see Smart Contract Runtime Operations.

Rehearsals

Run the production rollout rehearsal before relying on the workflow:

powershell
npm run test:contract-production-rollout-rehearsal

It 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:

powershell
npm run test:contract-production-multinode-rehearsal

It 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:

powershell
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:

powershell
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" --yes

This submits transaction CREATE_CONTRACT_RELEASE.

Check the release plan:

powershell
$env:NOOS_OPERATOR_TOKEN="<operator-token>"
npm run noos -- contracts release plan --id "release-report-store-0.1.0" --json

For a new contract, the normal progression is:

text
draft -> package_approved -> deployed -> instantiated -> readiness_checked -> traffic_ready -> completed

For an upgrade, include --source-contract-id, perform migration, deactivate the old contract, and complete:

text
draft -> package_approved -> deployed -> instantiated -> readiness_checked -> migration_planned -> migration_completed -> traffic_ready -> source_deactivated -> completed

Upgrade Without State Migration

Contracts are immutable by code hash and manifest hash. Upgrade by publishing a new package and a new contract instance.

  1. Change the source.
  2. Build a new WASM.
  3. Update manifest version, ABI, bucket requirements, maxFuel, or invoke policy as needed.
  4. Package to a new folder, such as contract-packages/report-store-0.2.0.
  5. Sign the new package.
  6. Generate payloads with a new contract id, such as contract-report-store-v2.
  7. Register the new package if registry policy requires it.
  8. Deploy the new WASM.
  9. Instantiate the new contract.
  10. Submit bucket access rules for the new contract principal.
  11. 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:

text
contract-report-store-v2

Create a migration payload:

powershell
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:

powershell
--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:

powershell
npm run noos -- contracts migration dry-run --payload-file "migration-report-store-v1-to-v2.json"

Submit it:

powershell
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" --yes

This submits transaction MIGRATE_CONTRACT_STATE.

Read the receipt:

powershell
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:

StepTransaction
Optional proposalPROPOSE_CONTRACT_STATE_MIGRATION
Optional approvalAPPROVE_CONTRACT_STATE_MIGRATION
Create jobCREATE_CONTRACT_STATE_MIGRATION_JOB
Execute batchEXECUTE_CONTRACT_STATE_MIGRATION_BATCH
Finalize jobFINALIZE_CONTRACT_STATE_MIGRATION_JOB

Create a proposal payload:

powershell
@'
{
  "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:

powershell
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" --yes

This submits transaction PROPOSE_CONTRACT_STATE_MIGRATION.

powershell
@'
{
  "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" --yes

This submits transaction APPROVE_CONTRACT_STATE_MIGRATION.

Create a job payload:

json
{
  "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:

powershell
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" --yes

This submits transaction CREATE_CONTRACT_STATE_MIGRATION_JOB.

Execute batches until the job is complete:

powershell
@'
{
  "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" --yes

This submits transaction EXECUTE_CONTRACT_STATE_MIGRATION_BATCH.

Finalize the job:

powershell
@'
{
  "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" --yes

This 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:

json
{
  "migrations": [
    {
      "id": "report-v1-to-v2",
      "entrypoint": "migrateReportV1ToV2",
      "fromVersion": "0.1.0",
      "toVersion": "0.2.0",
      "maxBatchSize": 50,
      "maxFuel": 5000000
    }
  ]
}

AssemblyScript example:

ts
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 migrationId fails;
  • 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;
  • codeHash matches the WASM;
  • manifestHash matches 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:

powershell
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

Next Steps

Audience-first NOOSChain documentation.