Initial commit: smart contract templates for bash, go, python, and typescript

This commit is contained in:
2026-03-17 19:59:47 -04:00
commit 0634e66469
35 changed files with 3794 additions and 0 deletions

28
typescript/.gitignore vendored Executable file
View File

@@ -0,0 +1,28 @@
# Dependencies
node_modules/
# Build output
dist/
# Generated proto files
src/proto/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Test config (contains credentials)
test-config.yaml
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# OS
.DS_Store
Thumbs.db

38
typescript/Makefile Executable file
View File

@@ -0,0 +1,38 @@
.PHONY: proto build run clean setup test
# Generate TypeScript types from proto files
proto:
npm run proto
# Install dependencies
setup:
npm install
# Build the TypeScript code
build:
npm run build
# Run the smart contract (production)
run: build
npm start -- --config config.yaml
# Run in development mode (with ts-node)
dev:
npm run dev -- --config config.yaml
# Clean build artifacts
clean:
npm run clean
rm -rf node_modules
# Run tests
test:
npm test
# Lint code
lint:
npm run lint
# Format code
format:
npm run format

220
typescript/README.md Executable file
View File

@@ -0,0 +1,220 @@
# 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:
```bash
cp -r typescript /path/to/my-smart-contract
cd /path/to/my-smart-contract
```
2. **Install dependencies**:
```bash
npm install
```
3. **Configure your connection** by editing `config.yaml`:
```yaml
server_address: "your-dragonchain-server:50051"
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**:
```bash
npm run build
npm start -- --config config.yaml
```
Or run in development mode:
```bash
npm run dev -- --config config.yaml
```
## Configuration
| Field | Description | Default |
|-------|-------------|---------|
| `server_address` | gRPC server address | 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` | Delay between reconnection attempts | `5` |
| `max_reconnect_attempts` | Max reconnect attempts (0 = infinite) | `0` |
## Implementing Your Smart Contract
Edit the `processTransaction` function in `src/process.ts`:
```typescript
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:
```typescript
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
```bash
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
```bash
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`:
```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:
```bash
node src/main.js --config config.yaml
```
## License
[Your License Here]

24
typescript/config.yaml Executable file
View File

@@ -0,0 +1,24 @@
# Smart Contract Client Configuration
# Copy this file and fill in your values
# The gRPC server address to connect to
server_address: "localhost:50051"
# Your smart contract ID (provided by Dragonchain)
smart_contract_id: "your-smart-contract-id"
# API key for authentication (provided by Dragonchain)
api_key: "your-api-key"
# Whether to use TLS for the connection
use_tls: false
# Path to TLS certificate (required if use_tls is true)
# tls_cert_path: "/path/to/cert.pem"
# Number of concurrent workers for processing transactions
num_workers: 10
# Reconnect settings
reconnect_delay_seconds: 5
max_reconnect_attempts: 0 # 0 = infinite retries

603
typescript/package-lock.json generated Executable file
View File

@@ -0,0 +1,603 @@
{
"name": "smart-contract",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "smart-contract",
"version": "1.0.0",
"dependencies": {
"@grpc/grpc-js": "^1.9.0",
"@grpc/proto-loader": "^0.7.0",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"ts-node": "^10.9.0",
"typescript": "^5.3.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@grpc/grpc-js": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
"integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
"license": "Apache-2.0",
"dependencies": {
"@grpc/proto-loader": "^0.8.0",
"@js-sdsl/ordered-map": "^4.4.2"
},
"engines": {
"node": ">=12.10.0"
}
},
"node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
"integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
"license": "Apache-2.0",
"dependencies": {
"lodash.camelcase": "^4.3.0",
"long": "^5.0.0",
"protobufjs": "^7.5.3",
"yargs": "^17.7.2"
},
"bin": {
"proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@grpc/proto-loader": {
"version": "0.7.15",
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
"integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
"license": "Apache-2.0",
"dependencies": {
"lodash.camelcase": "^4.3.0",
"long": "^5.0.0",
"protobufjs": "^7.2.5",
"yargs": "^17.7.2"
},
"bin": {
"proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@js-sdsl/ordered-map": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/js-sdsl"
}
},
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/base64": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/eventemitter": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/fetch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0"
}
},
"node_modules/@protobufjs/float": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/path": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/pool": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
"node_modules/@tsconfig/node10": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
"integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/js-yaml": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/diff": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
"integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
"license": "MIT"
},
"node_modules/long": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
"license": "Apache-2.0"
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
"license": "ISC"
},
"node_modules/protobufjs": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"license": "MIT"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
}
}
}

29
typescript/package.json Executable file
View File

@@ -0,0 +1,29 @@
{
"name": "smart-contract",
"version": "1.0.0",
"description": "Dragonchain Smart Contract Client",
"main": "dist/main.js",
"scripts": {
"build": "tsc",
"start": "node dist/main.js",
"dev": "ts-node src/main.ts",
"proto": "proto-loader-gen-types --grpcLib=@grpc/grpc-js --outDir=src/proto proto/remote_sc.proto",
"clean": "rm -rf dist",
"lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts"
},
"dependencies": {
"@grpc/grpc-js": "^1.9.0",
"@grpc/proto-loader": "^0.7.0",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"typescript": "^5.3.0",
"ts-node": "^10.9.0"
},
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -0,0 +1,48 @@
syntax = "proto3";
package remote_sc;
// SmartContractService defines the bi-directional streaming service for remote
// smart contract execution. External workers connect to this service to receive
// execution tasks and send back results.
service SmartContractService {
// Run establishes a bi-directional stream. The server sends SmartContractRequest
// messages to the client for execution, and the client sends back
// SmartContractResponse messages with results.
rpc Run(stream SmartContractResponse) returns (stream SmartContractRequest);
}
// SmartContractRequest is sent from the server to the connected worker
// to request execution of a smart contract.
message SmartContractRequest {
// Unique identifier for this execution request, used to correlate responses
string transaction_id = 1;
// Full transaction JSON to be processed by the smart contract
string transaction_json = 2;
// Environment variables to set for the smart contract execution
map<string, string> env_vars = 3;
// Secrets to be made available to the smart contract
map<string, string> secrets = 4;
}
// SmartContractResponse is sent from the worker back to the server
// with the results of smart contract execution.
message SmartContractResponse {
// The transaction_id from the original request, for correlation
string transaction_id = 1;
// The result data from the smart contract execution as JSON
string result_json = 2;
// Logs captured during smart contract execution
string logs = 3;
// Whether to persist the output to the chain
bool output_to_chain = 4;
// Error message if execution failed
string error = 5;
}

369
typescript/src/main.ts Executable file
View File

@@ -0,0 +1,369 @@
/**
* Dragonchain Smart Contract Client
*
* A gRPC client that connects to Dragonchain Prime server to process
* smart contract transactions.
*
* Do not modify this file unless you need to customize the client behavior.
* Implement your smart contract logic in process.ts instead.
*/
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
import * as fs from "fs";
import * as path from "path";
import * as yaml from "js-yaml";
import { ProcessResult, processTransaction } from "./process";
// Load proto definition
const PROTO_PATH = path.join(__dirname, "../proto/remote_sc.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: false,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const SmartContractService = protoDescriptor.remote_sc.SmartContractService;
// =============================================================================
// Configuration and Client Infrastructure
// Do not modify this file unless you need to customize the client behavior.
// Implement your smart contract logic in process.ts instead.
// =============================================================================
interface Config {
serverAddress: string;
smartContractId: string;
apiKey: string;
useTls: boolean;
tlsCertPath?: string;
numWorkers: number;
reconnectDelaySeconds: number;
maxReconnectAttempts: number;
}
interface SmartContractRequest {
transactionId: string;
transactionJson: string;
envVars: Record<string, string>;
secrets: Record<string, string>;
}
interface SmartContractResponse {
transactionId: string;
resultJson: string;
logs: string;
outputToChain: boolean;
error: string;
}
class SmartContractClient {
private config: Config;
private client: any;
private running: boolean = false;
private workQueue: SmartContractRequest[] = [];
private processing: Set<string> = new Set();
private stream: any;
constructor(config: Config) {
this.config = config;
}
/**
* Connect to the gRPC server.
*/
connect(): boolean {
try {
let credentials: grpc.ChannelCredentials;
if (this.config.useTls) {
if (!this.config.tlsCertPath) {
console.error("[SC-Client] TLS enabled but no certificate path provided");
return false;
}
const rootCert = fs.readFileSync(this.config.tlsCertPath);
credentials = grpc.credentials.createSsl(rootCert);
} else {
credentials = grpc.credentials.createInsecure();
}
this.client = new SmartContractService(
this.config.serverAddress,
credentials
);
console.log(`[SC-Client] Connected to server at ${this.config.serverAddress}`);
return true;
} catch (e) {
console.error(`[SC-Client] Failed to connect: ${e}`);
return false;
}
}
/**
* Close the gRPC connection.
*/
close(): void {
if (this.stream) {
this.stream.end();
this.stream = null;
}
if (this.client) {
grpc.closeClient(this.client);
this.client = null;
}
}
/**
* Process a single request.
*/
private async processRequest(request: SmartContractRequest): Promise<SmartContractResponse> {
const logs = "";
try {
const result = await processTransaction(
request.transactionJson,
request.envVars,
request.secrets
);
const response: SmartContractResponse = {
transactionId: request.transactionId,
resultJson: result.data ? JSON.stringify(result.data) : "{}",
logs,
outputToChain: result.outputToChain,
error: result.error || "",
};
if (result.error) {
console.error(
`[SC-Client] Error processing transaction ${request.transactionId}: ${result.error}`
);
} else {
console.log(
`[SC-Client] Successfully processed transaction ${request.transactionId}`
);
}
return response;
} catch (e) {
console.error(
`[SC-Client] Exception processing transaction ${request.transactionId}: ${e}`
);
return {
transactionId: request.transactionId,
resultJson: "",
logs,
outputToChain: false,
error: String(e),
};
}
}
/**
* Run the client and process incoming requests.
*/
async run(): Promise<boolean> {
if (!this.client) {
console.error("[SC-Client] Not connected to server");
return false;
}
this.running = true;
// Create metadata for authentication
const metadata = new grpc.Metadata();
metadata.add("x-api-key", this.config.apiKey);
metadata.add("x-smart-contract-id", this.config.smartContractId);
return new Promise((resolve) => {
// Establish bi-directional stream
this.stream = this.client.Run(metadata);
console.log(
`[SC-Client] Stream established, ready to process requests (workers: ${this.config.numWorkers})`
);
// Handle incoming requests
this.stream.on("data", async (request: SmartContractRequest) => {
if (!this.running) return;
console.log(
`[SC-Client] Received request: transaction_id=${request.transactionId}`
);
// Process with concurrency limit
if (this.processing.size >= this.config.numWorkers) {
this.workQueue.push(request);
} else {
this.startProcessing(request);
}
});
this.stream.on("end", () => {
console.log("[SC-Client] Server closed the stream");
this.running = false;
resolve(true);
});
this.stream.on("error", (err: grpc.ServiceError) => {
console.error(`[SC-Client] Stream error: ${err.code} - ${err.message}`);
this.running = false;
resolve(false);
});
});
}
/**
* Start processing a request with concurrency tracking.
*/
private async startProcessing(request: SmartContractRequest): Promise<void> {
this.processing.add(request.transactionId);
try {
const response = await this.processRequest(request);
if (this.stream && this.running) {
this.stream.write(response);
}
} finally {
this.processing.delete(request.transactionId);
// Process next queued request if any
if (this.workQueue.length > 0 && this.running) {
const next = this.workQueue.shift()!;
this.startProcessing(next);
}
}
}
/**
* Stop the client gracefully.
*/
stop(): void {
console.log("[SC-Client] Stopping client...");
this.running = false;
}
}
// =============================================================================
// Configuration Loading
// =============================================================================
interface RawConfig {
server_address: string;
smart_contract_id: string;
api_key: string;
use_tls?: boolean;
tls_cert_path?: string;
num_workers?: number;
reconnect_delay_seconds?: number;
max_reconnect_attempts?: number;
}
function loadConfig(configPath: string): Config {
const content = fs.readFileSync(configPath, "utf8");
const raw = yaml.load(content) as RawConfig;
// Validate required fields
const required = ["server_address", "smart_contract_id", "api_key"];
for (const field of required) {
if (!(field in raw) || !raw[field as keyof RawConfig]) {
throw new Error(`Missing required config field: ${field}`);
}
}
return {
serverAddress: raw.server_address,
smartContractId: raw.smart_contract_id,
apiKey: raw.api_key,
useTls: raw.use_tls ?? false,
tlsCertPath: raw.tls_cert_path,
numWorkers: raw.num_workers ?? 10,
reconnectDelaySeconds: raw.reconnect_delay_seconds ?? 5,
maxReconnectAttempts: raw.max_reconnect_attempts ?? 0,
};
}
// =============================================================================
// Main Entry Point
// =============================================================================
async function main(): Promise<void> {
// Parse command line arguments
const args = process.argv.slice(2);
let configPath = "config.yaml";
for (let i = 0; i < args.length; i++) {
if (args[i] === "--config" || args[i] === "-c") {
configPath = args[i + 1];
i++;
}
}
// Load configuration
let config: Config;
try {
config = loadConfig(configPath);
} catch (e) {
console.error(`[SC-Client] Failed to load config: ${e}`);
process.exit(1);
}
// Create client
const client = new SmartContractClient(config);
// Setup signal handling for graceful shutdown
const shutdown = () => {
console.log("[SC-Client] Received shutdown signal...");
client.stop();
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
// Connection loop with reconnection logic
let attempts = 0;
while (true) {
if (client.connect()) {
attempts = 0;
const success = await client.run();
if (!success) {
// Check if it was a graceful shutdown
client.close();
break;
}
}
client.close();
attempts++;
if (
config.maxReconnectAttempts > 0 &&
attempts >= config.maxReconnectAttempts
) {
console.error(
`[SC-Client] Max reconnection attempts (${config.maxReconnectAttempts}) reached`
);
break;
}
const delay = config.reconnectDelaySeconds;
console.log(
`[SC-Client] Reconnecting in ${delay} seconds (attempt ${attempts})...`
);
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
}
console.log("[SC-Client] Client shut down");
}
main().catch((e) => {
console.error(`[SC-Client] Fatal error: ${e}`);
process.exit(1);
});

112
typescript/src/process.ts Executable file
View File

@@ -0,0 +1,112 @@
/**
* Smart Contract Processing Logic
*
* This file contains the transaction processing logic for your smart contract.
* Modify the `processTransaction` function to implement your business logic.
*/
// =============================================================================
// SMART CONTRACT IMPLEMENTATION - MODIFY THIS FILE
// =============================================================================
/**
* Transaction header metadata from Dragonchain.
*/
export interface TransactionHeader {
tag: string;
dc_id: string;
txn_id: string;
block_id: string;
txn_type: string;
timestamp: string;
invoker: string;
}
/**
* Parsed transaction from the server.
* Customize this interface to match your transaction payload structure.
*/
export interface Transaction {
version: string;
header: TransactionHeader;
payload: Record<string, unknown>;
}
/**
* Result from the processTransaction function.
*/
export interface ProcessResult {
data?: Record<string, unknown>;
outputToChain: boolean;
error?: string;
}
/**
* Process an incoming transaction.
*
* Implement your smart contract logic here.
*
* @param txJson - Raw transaction JSON string
* @param envVars - Environment variables passed from the server
* @param secrets - Secrets passed from the server (e.g., API keys, credentials)
* @returns ProcessResult containing the result data, whether to output to chain, and any error
*/
export async function processTransaction(
txJson: string,
envVars: Record<string, string>,
secrets: Record<string, string>
): Promise<ProcessResult> {
// Parse the transaction JSON
let tx: Transaction;
try {
tx = JSON.parse(txJson);
} catch (e) {
return {
outputToChain: false,
error: `Failed to parse transaction: ${e}`,
};
}
// ==========================================================================
// TODO: Implement your smart contract logic here
// ==========================================================================
//
// Example: Access transaction data
// const txnId = tx.header.txn_id;
// const txnType = tx.header.txn_type;
// const payload = tx.payload;
//
// Example: Access environment variables
// const scName = envVars["SMART_CONTRACT_NAME"];
// const dcId = envVars["DRAGONCHAIN_ID"];
//
// Example: Access secrets
// const apiKey = secrets["SC_SECRET_MY_API_KEY"];
//
// Example: Process based on payload action
// const action = tx.payload.action as string;
// switch (action) {
// case "create":
// // Handle create operation
// break;
// case "update":
// // Handle update operation
// break;
// default:
// return { outputToChain: false, error: `Unknown action: ${action}` };
// }
// Default implementation: echo back the transaction
const result = {
status: "processed",
transaction_id: tx.header.txn_id,
txn_type: tx.header.txn_type,
payload: tx.payload,
message: "Transaction processed successfully",
};
return {
data: result,
outputToChain: true,
};
}

19
typescript/tsconfig.json Executable file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}