Files
Andrew Miller f22fb29964 python + typescript + bash: mirror the durability fixes from go/
Parity pass on the other three language templates. Same guarantees as
go/: survive server restart, client restart, half-open TCP, and long
outages; rejoin and drain prime-side backlog on reconnect, without
the user writing any of this in process.*.

python/main.py:
- grpc.keepalive_time_ms=10000, keepalive_timeout_ms=3000,
  keepalive_permit_without_calls=1 on the channel. Half-open TCP is
  detected within ~13s instead of the OS default ~2h.
- Exponential backoff with jitter; max_backoff_seconds config ceiling
  (default 120). Attempts counter resets after a session runs
  healthy for 60s so transient restarts don't escalate the delay.
- chain_id added as a required config field and sent as the
  x-chain-id gRPC metadata header (prime rejects streams without it).

typescript/src/main.ts:
- Same keepalive options on the @grpc/grpc-js client.
- Same exponential backoff + jitter logic.
- chain_id added to Config + metadata.

bash/:
- Config + README updated. The bash template uses Python's main.py
  as its runtime, so the behavioural changes above flow through
  without a separate main per language.

Docs: each README gains a "Durability guarantees" section so contract
authors see the invariants without reading the runtime code.
2026-04-19 21:32:24 -04:00
..

TypeScript Smart Contract Template

A TypeScript/JavaScript-based smart contract client for Dragonchain Prime that connects via gRPC.

Prerequisites

  • Node.js 18 or later
  • npm

Quick Start

  1. Copy this template to create your smart contract:

    cp -r typescript /path/to/my-smart-contract
    cd /path/to/my-smart-contract
    
  2. Install dependencies:

    npm install
    
  3. Configure your connection by editing config.yaml:

    server_address: "your-dragonchain-server:50051"
    chain_id: "your-chain-public-id"
    smart_contract_id: "your-smart-contract-id"
    api_key: "your-api-key"
    
  4. Implement your smart contract logic in src/process.ts by modifying the processTransaction function.

  5. Build and run:

    npm run build
    npm start -- --config config.yaml
    

    Or run in development mode:

    npm run dev -- --config config.yaml
    

Configuration

Field Description Default
server_address gRPC server address Required
chain_id Public chain id the SC is registered on (sent as x-chain-id metadata) Required
smart_contract_id Your smart contract ID Required
api_key API key for authentication Required
use_tls Enable TLS encryption false
tls_cert_path Path to TLS certificate -
num_workers Concurrent transaction processors 10
reconnect_delay_seconds Base delay for exponential backoff between reconnect attempts 3
max_backoff_seconds Ceiling for the exponential backoff 120
max_reconnect_attempts Max reconnect attempts (0 = infinite, recommended) 0

Durability guarantees (provided by src/main.ts, no work for you)

  • Server restart, update, crash, or network blip → the client auto-reconnects and resumes processing. Transactions observed while the stream was down stay queued on the Dragonchain Prime side and are delivered (oldest first) on reconnect.
  • Client restart or long outage → when this process comes back up (minutes, hours, months later), it rejoins the stream and prime re-delivers every still-pending transaction that should have invoked it.
  • Half-open TCP (silent peer, resumed laptop, corporate NAT dropping idle flows) is detected within ~13 seconds via gRPC keepalive and triggers a reconnect. No dangling ghost streams.
  • Reconnect storms are avoided: exponential backoff with jitter means many clients reconnecting after a server restart don't all slam accept() at the same instant. The timer resets after a stream has been healthy for 60 seconds.

These are invariants of the template — you do not add any of this in src/process.ts.

Implementing Your Smart Contract

Edit the processTransaction function in src/process.ts:

export async function processTransaction(
  txJson: string,
  envVars: Record<string, string>,
  secrets: Record<string, string>
): Promise<ProcessResult> {
  // Parse the transaction
  const tx: Transaction = JSON.parse(txJson);

  // Access transaction data
  const txnId = tx.header.txn_id;
  const txnType = tx.header.txn_type;
  const payload = tx.payload;

  // Access environment variables
  const scName = envVars["SMART_CONTRACT_NAME"];
  const dcId = envVars["DRAGONCHAIN_ID"];

  // Access secrets
  const mySecret = secrets["SC_SECRET_MY_SECRET"];

  // Implement your logic here
  const result = {
    status: "success",
    data: "your result data",
  };

  return {
    data: result,
    outputToChain: true,  // Set to true to persist result on chain
  };
}

Transaction Structure

The Transaction interface in src/process.ts matches the Dragonchain transaction format:

interface Transaction {
  version: string;
  header: TransactionHeader;
  payload: Record<string, unknown>;
}

interface TransactionHeader {
  tag: string;
  dc_id: string;
  txn_id: string;
  block_id: string;
  txn_type: string;
  timestamp: string;
  invoker: string;
}

Available Environment Variables

Variable Description
TZ Timezone
ENVIRONMENT Deployment environment
INTERNAL_ID Internal identifier
DRAGONCHAIN_ID Dragonchain ID
DRAGONCHAIN_ENDPOINT Dragonchain API endpoint
SMART_CONTRACT_ID This smart contract's ID
SMART_CONTRACT_NAME This smart contract's name
SC_ENV_* Custom environment variables

Secrets

Secrets are provided in the secrets object with keys prefixed by SC_SECRET_.

Project Structure

.
├── src/
│   ├── main.ts          # Client infrastructure (do not modify)
│   └── process.ts       # Your smart contract logic (modify this)
├── proto/
│   └── remote_sc.proto  # gRPC service definition
├── config.yaml          # Configuration file
├── package.json         # Node.js dependencies
├── tsconfig.json        # TypeScript configuration
├── Makefile             # Build commands
└── README.md            # This file

File Descriptions

  • src/process.ts - Contains the processTransaction function where you implement your smart contract logic. This is the only file you need to modify for most use cases.
  • src/main.ts - Contains the gRPC client infrastructure, connection handling, and worker pool. You typically don't need to modify this file.

NPM Scripts

npm install        # Install dependencies
npm run build      # Compile TypeScript to JavaScript
npm start          # Run the compiled application
npm run dev        # Run with ts-node (development)
npm run proto      # Generate TypeScript types from proto
npm run clean      # Remove build artifacts
npm run lint       # Lint the code
npm run format     # Format code with prettier

Make Commands

make setup   # Install dependencies
make proto   # Generate TypeScript types from proto
make build   # Build TypeScript
make run     # Build and run
make dev     # Run in development mode
make clean   # Remove build artifacts
make lint    # Lint code
make format  # Format code

Concurrent Processing

The client uses async/await with a concurrency limit to process multiple transactions simultaneously. The number of concurrent workers is configurable via num_workers in the config file.

Error Handling

  • Return errors from the processTransaction function via ProcessResult.error to report failures
  • The client automatically handles reconnection on connection failures
  • Logs are captured and sent back with the response

Docker

Example Dockerfile:

FROM node:20-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

CMD ["node", "dist/main.js", "--config", "config.yaml"]

Using with JavaScript

If you prefer plain JavaScript instead of TypeScript:

  1. Write your code in src/process.js instead of src/process.ts
  2. Skip the build step and run directly:
    node src/main.js --config config.yaml
    

License

[Your License Here]