diff --git a/.github/actions/setup-api-tools/action.yml b/.github/actions/setup-api-tools/action.yml index 5d5cedc6b..23e04113d 100644 --- a/.github/actions/setup-api-tools/action.yml +++ b/.github/actions/setup-api-tools/action.yml @@ -1,5 +1,11 @@ name: 'Setup API Tools' -description: 'Setup Rust, Bun, and Python dependencies for API tools' +description: 'Setup Rust and Python dependencies for API tools' + +inputs: + setup_algokit: + description: 'Whether to install and start algokit localnet' + required: false + default: 'false' runs: using: 'composite' @@ -9,19 +15,20 @@ runs: toolchain: 1.85.0 components: rustfmt - - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - name: Install uv uses: astral-sh/setup-uv@v5 - - name: Install bun dependencies - working-directory: api - shell: bash - run: bun install - - name: Install uv dependencies working-directory: api/oas_generator shell: bash - run: uv sync --extra dev \ No newline at end of file + run: uv sync --extra dev + + - name: Optionally install algokit + if: ${{ inputs.setup_algokit == 'true' }} + shell: bash + run: uv tool install algokit + + - name: Optionally start localnet + if: ${{ inputs.setup_algokit == 'true' }} + shell: bash + run: algokit localnet start diff --git a/.github/workflows/api_ci.yml b/.github/workflows/api_ci.yml index f1cc50a6c..5ceaa6e4f 100644 --- a/.github/workflows/api_ci.yml +++ b/.github/workflows/api_ci.yml @@ -12,6 +12,7 @@ on: - "tools/api_tools/**" - "crates/algod_client/**" - "crates/indexer_client/**" + - "packages/typescript/**" - ".github/workflows/api_ci.yml" pull_request: branches: @@ -21,6 +22,7 @@ on: - "tools/api_tools/**" - "crates/algod_client/**" - "crates/indexer_client/**" + - "packages/typescript/**" - ".github/workflows/api_ci.yml" workflow_dispatch: @@ -33,10 +35,10 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-api-tools - + - name: Run OAS generator linting run: cargo api lint-oas - + - name: Run OAS generator tests run: cargo api test-oas @@ -54,10 +56,10 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-api-tools - + - name: Generate ${{ matrix.client }} API client run: cargo api generate-${{ matrix.client }} - + - name: Check for output stability run: | git add -N ${{ matrix.output_dir }} @@ -66,6 +68,76 @@ jobs: echo "🔧 To fix: Run 'cargo api generate-${{ matrix.client }}' locally and commit the changes" exit 1 fi - + - name: Verify generated code compiles run: cargo check --manifest-path Cargo.toml -p ${{ matrix.client }}_client + + ts_api_client_generation: + runs-on: ubuntu-latest + needs: oas_lint_and_test + strategy: + matrix: + client: [algod, indexer] + include: + - client: algod + package_dir: packages/typescript/algod_client + package_subdir: algod_client + workspace: "@algorandfoundation/algod-client" + algokit_utils_test_filter: tests/algod + - client: indexer + package_dir: packages/typescript/indexer_client + package_subdir: indexer_client + workspace: "@algorandfoundation/indexer-client" + algokit_utils_test_filter: tests/indexer + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-api-tools + with: + setup_algokit: "true" + - uses: actions/setup-node@v5 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: packages/typescript/package-lock.json + + - name: Install TypeScript workspace dependencies + working-directory: packages/typescript + run: npm ci + + - name: Generate ${{ matrix.client }} TypeScript API client + run: cargo api generate-ts-${{ matrix.client }} + + - name: Check output stability + run: | + git add -N ${{ matrix.package_dir }} + if ! git diff --exit-code --minimal ${{ matrix.package_dir }}; then + echo "❌ Generated ${{ matrix.client }} TypeScript client differs from committed version." + echo "🔧 To fix: run 'cargo api generate-ts-${{ matrix.client }}' locally and commit the changes." + exit 1 + fi + + - name: Prettier check + working-directory: packages/typescript + run: npx --yes prettier --check ${{ matrix.package_subdir }} + + - name: Lint generated client + working-directory: packages/typescript + run: npm run lint --workspace ${{ matrix.workspace }} + + - name: Build all TypeScript packages + working-directory: packages/typescript + run: | + npm run build --workspace @algorandfoundation/algokit-common + npm run build --workspace @algorandfoundation/algokit-abi + npm run build --workspace @algorandfoundation/algokit-transact + npm run build --workspace ${{ matrix.workspace }} + + - name: Update package links after build + working-directory: packages/typescript + run: | + # Re-install to ensure dist folders are properly linked + npm install --prefer-offline --no-audit --no-fund + + - name: Test generated client + working-directory: packages/typescript + run: npm test --workspace @algorandfoundation/algokit-utils -- ${{ matrix.algokit_utils_test_filter }} diff --git a/Cargo.lock b/Cargo.lock index 8612e5898..874ba050f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,7 @@ dependencies = [ "clap", "color-eyre", "duct", + "once_cell", "shlex", ] diff --git a/api/README.md b/api/README.md index 4fe8c2f45..277afa34c 100644 --- a/api/README.md +++ b/api/README.md @@ -7,7 +7,7 @@ This package contains tools for working with the Algorand API specifications and - [Python 3.12+](https://www.python.org/) - Required for the custom OAS generator - [uv](https://docs.astral.sh/uv/) - Python package manager - [Rust](https://rustup.rs/) - Required for compiling generated clients and running API tools -- [Bun](https://bun.sh/) - JavaScript runtime (only for convert-openapi script) +- [Node.js](https://nodejs.org/) - JavaScript runtime (only for convert-openapi script) ## Setup @@ -18,7 +18,7 @@ uv install # Install JavaScript dependencies (only needed for convert-openapi) cd ../ -bun install +npm install ``` ## Available Scripts @@ -44,6 +44,7 @@ cargo api convert-indexer ``` The converted specs will be available at: + - `specs/algod.oas3.json` - `specs/indexer.oas3.json` @@ -66,9 +67,33 @@ cargo api generate-indexer ``` The generated Rust clients will be available at: + - `../crates/algod_client/` - `../crates/indexer_client/` +### Generate TypeScript API Clients + +Generate both TypeScript API clients using the TypeScript generator: + +```bash +cargo api generate-ts-all +``` + +Generate individual clients: + +```bash +# Generate algod client only +cargo api generate-ts-algod + +# Generate indexer client only +cargo api generate-ts-indexer +``` + +The generated TypeScript clients will be available at: + +- `../packages/typescript/algod_client/` +- `../packages/typescript/indexer_client/` + ### Development Scripts ```bash diff --git a/api/bun.lock b/api/bun.lock deleted file mode 100644 index a42257ae0..000000000 --- a/api/bun.lock +++ /dev/null @@ -1,60 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "name": "api", - "devDependencies": { - "@apidevtools/swagger-parser": "^11.0.0", - "@types/bun": "latest", - "@types/node": "^20.10.0", - "prettier": "^3.5.3", - }, - "peerDependencies": { - "typescript": "^5", - }, - }, - }, - "packages": { - "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@13.0.2", "", { "dependencies": { "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-ThpknSFmb1zJXU16ba8cFbDRL3WRs6WETW323gOhj7Gwdj9GUqNpA5JFhdAINxINyAz03gqgF5Y4UydAjE3Pdg=="], - - "@apidevtools/openapi-schemas": ["@apidevtools/openapi-schemas@2.1.0", "", {}, "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ=="], - - "@apidevtools/swagger-methods": ["@apidevtools/swagger-methods@3.0.2", "", {}, "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg=="], - - "@apidevtools/swagger-parser": ["@apidevtools/swagger-parser@11.0.1", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "13.0.2", "@apidevtools/openapi-schemas": "^2.1.0", "@apidevtools/swagger-methods": "^3.0.2", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "call-me-maybe": "^1.0.2" }, "peerDependencies": { "openapi-types": ">=7" } }, "sha512-0OzWjKPUr7dvXOgQi6hsNLpwgQRtPgyQoYMuaIB+Zj50Qjbwxph/nu4BndwOA446FtQUTwkR3BxLnORpVYLHYw=="], - - "@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="], - - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - - "@types/node": ["@types/node@20.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q=="], - - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], - - "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="], - - "call-me-maybe": ["call-me-maybe@1.0.2", "", {}, "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], - - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], - - "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - - "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - } -} diff --git a/api/oas_generator/pyproject.toml b/api/oas_generator/pyproject.toml index c166a6003..dbba76c71 100644 --- a/api/oas_generator/pyproject.toml +++ b/api/oas_generator/pyproject.toml @@ -130,5 +130,5 @@ disallow_any_generics = false implicit_reexport = false show_error_codes = true -[tool.uv] -dev-dependencies = ["mypy>=1.14.1", "pytest>=8.3.5", "ruff>=0.12.0"] +[dependency-groups] +dev = ["mypy>=1.14.1", "pytest>=8.3.5", "ruff>=0.12.0"] diff --git a/api/oas_generator/ts_oas_generator/__init__.py b/api/oas_generator/ts_oas_generator/__init__.py new file mode 100644 index 000000000..4628e566b --- /dev/null +++ b/api/oas_generator/ts_oas_generator/__init__.py @@ -0,0 +1,11 @@ +"""Top-level package metadata for ``ts_oas_generator``.""" + +from ts_oas_generator import cli, constants, generator, parser, utils + +__all__ = [ + "cli", + "constants", + "generator", + "parser", + "utils", +] diff --git a/api/oas_generator/ts_oas_generator/cli.py b/api/oas_generator/ts_oas_generator/cli.py new file mode 100644 index 000000000..d26f26007 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/cli.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""Command-line interface for the TypeScript OAS Generator (Phase 2).""" + +from __future__ import annotations + +import argparse +import contextlib +import json +import shutil +import sys +import tempfile +import traceback +from collections.abc import Generator +from pathlib import Path + +from ts_oas_generator import constants +from ts_oas_generator.generator.template_engine import CodeGenerator +from ts_oas_generator.utils.file_utils import write_files_to_disk + +# Exit codes for better error reporting +EXIT_SUCCESS = 0 +EXIT_FILE_NOT_FOUND = 1 +EXIT_INVALID_JSON = 2 +EXIT_GENERATION_ERROR = 3 + + +def parse_command_line_args(args: list[str] | None = None) -> argparse.Namespace: + """Create and configure the command line argument parser for TS generator.""" + parser = argparse.ArgumentParser( + description="Generate TypeScript client from OpenAPI specification", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s ../specs/algod.oas3.json --output ./packages/algod_client --package-name algod_client + %(prog)s ../specs/indexer.oas3.json -o ./packages/indexer_client -p indexer_client + """, + ) + + parser.add_argument( + "spec_file", + type=Path, + help="Path to OpenAPI specification file (JSON or YAML)", + metavar="SPEC_FILE", + ) + parser.add_argument( + "--output", + "-o", + type=Path, + default=Path(constants.DEFAULT_OUTPUT_DIR), + help="Output directory for generated files (default: %(default)s)", + dest="output_dir", + ) + parser.add_argument( + "--package-name", + "-p", + default=constants.DEFAULT_PACKAGE_NAME, + help="Name for the generated TypeScript package (default: %(default)s)", + dest="package_name", + ) + parser.add_argument( + "--template-dir", + "-t", + type=Path, + help="Custom template directory (optional)", + dest="template_dir", + ) + parser.add_argument( + "--verbose", + "-v", + action="store_true", + help="Enable verbose output", + ) + parser.add_argument( + "--description", + "-d", + help="Custom description for the generated package (overrides spec description)", + dest="custom_description", + ) + + parsed_args = parser.parse_args(args) + + # Validate inputs + if not parsed_args.spec_file.exists(): + parser.error(f"Specification file not found: {parsed_args.spec_file!s}") + + return parsed_args + + +def print_generation_summary(*, file_count: int, files: dict[Path, str], output_dir: Path) -> None: + """Print summary of generated files.""" + print(f"Generated {file_count} files:") + for file_path in sorted(files.keys()): + print(f" {file_path!s}") + print(f"\nTypeScript client generated successfully in {output_dir!s}") + + +@contextlib.contextmanager +def backup_and_prepare_output_dir(output_dir: Path) -> Generator[None, None, None]: + """Backup and ensure the output directory exists before generation.""" + backup_dir: Path | None = None + + # Create a backup of the existing directory if it exists and is non-empty + if output_dir.exists() and any(output_dir.iterdir()): + backup_dir = Path(tempfile.mkdtemp(prefix=constants.BACKUP_DIR_PREFIX)) + shutil.copytree(output_dir, backup_dir, dirs_exist_ok=True) + + # Ensure directory exists + output_dir.mkdir(parents=True, exist_ok=True) + + try: + yield + except Exception: + if backup_dir: + print( + "Error: Generation failed. Restoring original content.", + file=sys.stderr, + ) + # Restore backup + if output_dir.exists(): + shutil.rmtree(output_dir) + shutil.copytree(backup_dir, output_dir, dirs_exist_ok=True) + raise + finally: + if backup_dir and backup_dir.exists(): + shutil.rmtree(backup_dir) + + +def main(args: list[str] | None = None) -> int: + parsed_args = parse_command_line_args(args) + + try: + with backup_and_prepare_output_dir(parsed_args.output_dir): + generator = CodeGenerator(template_dir=parsed_args.template_dir) + + generated_files = generator.generate( + parsed_args.spec_file, + parsed_args.output_dir, + parsed_args.package_name, + custom_description=parsed_args.custom_description, + ) + + # Write files to disk (overwrite safely) + write_files_to_disk(generated_files) + + if parsed_args.verbose: + print_generation_summary( + file_count=len(generated_files), + files=generated_files, + output_dir=parsed_args.output_dir, + ) + else: + print(f"TypeScript client generated successfully in {parsed_args.output_dir!s}") + + return EXIT_SUCCESS + + except FileNotFoundError: + print( + f"Error: Specification file not found: {parsed_args.spec_file!s}", + file=sys.stderr, + ) + return EXIT_FILE_NOT_FOUND + except json.JSONDecodeError as e: + print(f"Error: Invalid JSON in specification file: {e!s}", file=sys.stderr) + return EXIT_INVALID_JSON + except Exception as e: + print(f"Error: {e!s}", file=sys.stderr) + if parsed_args.verbose: + traceback.print_exc() + return EXIT_GENERATION_ERROR + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/api/oas_generator/ts_oas_generator/constants.py b/api/oas_generator/ts_oas_generator/constants.py new file mode 100644 index 000000000..513e6a4e4 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/constants.py @@ -0,0 +1,219 @@ +"""Constants for the TypeScript OAS generator using Enums for better organization.""" + +from __future__ import annotations + +from enum import StrEnum +from typing import Final + + +class MediaType(StrEnum): + """Content types for request/response handling.""" + + JSON = "application/json" + MSGPACK = "application/msgpack" + TEXT = "text/plain" + BINARY = "application/x-binary" + OCTET_STREAM = "application/octet-stream" + + +class ParamLocation(StrEnum): + """OpenAPI parameter locations.""" + + PATH = "path" + QUERY = "query" + HEADER = "header" + + +class TypeScriptType(StrEnum): + """TypeScript type literals.""" + + UINT8ARRAY = "Uint8Array" + VOID = "void" + STRING = "string" + NUMBER = "number" + BIGINT = "bigint" + BOOLEAN = "boolean" + ANY = "any" + NULL = "null" + OBJECT = "object" + NUMBER_OR_BIGINT = "number | bigint" + NEVER = "never" + + +class SchemaKey(StrEnum): + """OpenAPI schema object keys.""" + + TYPE = "type" + PROPERTIES = "properties" + REQUIRED = "required" + ADDITIONAL_PROPERTIES = "additionalProperties" + ALL_OF = "allOf" + ONE_OF = "oneOf" + ANY_OF = "anyOf" + ITEMS = "items" + ENUM = "enum" + NULLABLE = "nullable" + FORMAT = "format" + COMPONENTS = "components" + COMPONENTS_SCHEMAS = "schemas" + PATHS = "paths" + MAXIMUM = "maximum" + MINIMUM = "minimum" + + +class OperationKey(StrEnum): + """OpenAPI operation object keys.""" + + OPERATION_ID = "operationId" + PARAMETERS = "parameters" + REQUEST_BODY = "requestBody" + RESPONSES = "responses" + TAGS = "tags" + CONTENT = "content" + IN = "in" + NAME = "name" + DESCRIPTION = "description" + + +class DirectoryName(StrEnum): + """Generated code directory structure.""" + + SRC = "src" + MODELS = "models" + APIS = "apis" + CORE = "core" + + +class HttpMethod(StrEnum): + """HTTP methods supported by the generator.""" + + GET = "get" + POST = "post" + PUT = "put" + DELETE = "delete" + PATCH = "patch" + HEAD = "head" + OPTIONS = "options" + + +# TypeScript reserved words (kept as frozenset for performance in lookups) +TS_RESERVED_WORDS: Final[frozenset[str]] = frozenset( + [ + "abstract", + "any", + "as", + "boolean", + "break", + "case", + "catch", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "else", + "enum", + "export", + "extends", + "false", + "finally", + "for", + "from", + "function", + "if", + "implements", + "import", + "in", + "instanceof", + "interface", + "let", + "new", + "null", + "number", + "package", + "private", + "protected", + "public", + "return", + "static", + "string", + "super", + "switch", + "symbol", + "this", + "throw", + "true", + "try", + "type", + "typeof", + "undefined", + "var", + "void", + "while", + "with", + "yield", + "await", + "async", + "constructor", + ] +) + +# Builtin TypeScript types (derived from enum for consistency) +TS_BUILTIN_TYPES: Final[frozenset[str]] = frozenset( + [ + TypeScriptType.UINT8ARRAY, + TypeScriptType.VOID, + TypeScriptType.STRING, + TypeScriptType.NUMBER, + TypeScriptType.BIGINT, + TypeScriptType.BOOLEAN, + TypeScriptType.NEVER, + ] +) + +# HTTP methods as frozenset (for performance in lookups) +HTTP_METHODS: Final[frozenset[str]] = frozenset(m.value for m in HttpMethod) + +# Default values +DEFAULT_OUTPUT_DIR: Final[str] = "./generated_ts" +DEFAULT_PACKAGE_NAME: Final[str] = "api_ts_client" +DEFAULT_TEMPLATE_DIR: Final[str] = "templates" + +# File names +INDEX_FILE: Final[str] = "index.ts" +API_SERVICE_FILE: Final[str] = "api.service.ts" +MODEL_FILE_EXTENSION: Final[str] = ".ts" + +# Template file names +MODEL_TEMPLATE: Final[str] = "models/model.ts.j2" +MODELS_INDEX_TEMPLATE: Final[str] = "models/index.ts.j2" +API_SERVICE_TEMPLATE: Final[str] = "apis/service.ts.j2" +APIS_INDEX_TEMPLATE: Final[str] = "apis/index.ts.j2" + +# Status code prefixes +SUCCESS_STATUS_PREFIX: Final[str] = "2" + +# Special parameter values +FORMAT_PARAM_NAME: Final[str] = "format" + +# Default values for operations +DEFAULT_TAG: Final[str] = "default" +DEFAULT_API_TAG: Final[str] = "api" + + +# Vendor extensions +X_ALGOKIT_FIELD_RENAME: Final[str] = "x-algokit-field-rename" + +# Backup directory prefix +BACKUP_DIR_PREFIX: Final[str] = "tsgen_bak_" + +# Custom extension +X_ALGOKIT_BIGINT: Final[str] = "x-algokit-bigint" +X_ALGOKIT_SIGNED_TXN: Final[str] = "x-algokit-signed-txn" +X_ALGOKIT_BYTES_BASE64: Final[str] = "x-algokit-bytes-base64" + +# Template configuration +TEMPLATE_TRIM_BLOCKS: Final[bool] = True +TEMPLATE_LSTRIP_BLOCKS: Final[bool] = True diff --git a/api/oas_generator/ts_oas_generator/generator/__init__.py b/api/oas_generator/ts_oas_generator/generator/__init__.py new file mode 100644 index 000000000..3ece13219 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/generator/__init__.py @@ -0,0 +1,5 @@ +"""Generation utilities for the TypeScript OAS generator.""" + +from ts_oas_generator.generator.template_engine import CodeGenerator + +__all__ = ["CodeGenerator"] diff --git a/api/oas_generator/ts_oas_generator/generator/filters.py b/api/oas_generator/ts_oas_generator/generator/filters.py new file mode 100644 index 000000000..50c1fcdfe --- /dev/null +++ b/api/oas_generator/ts_oas_generator/generator/filters.py @@ -0,0 +1,443 @@ +"""TypeScript-specific Jinja2 filters and helpers. + +Phase 2 adds OpenAPI -> TS type mapping and naming utilities. +""" + +from __future__ import annotations + +import re +from collections.abc import Callable, Iterable, Mapping +from functools import cache +from typing import Any + +from ts_oas_generator import constants +from ts_oas_generator.constants import MediaType, OperationKey, SchemaKey, TypeScriptType + +type Schema = Mapping[str, Any] +type Schemas = Mapping[str, Schema] + +_IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") + + +def ts_doc_comment(text: str | None) -> str: + """Format text as a TypeScript doc comment.""" + if not text: + return "" + lines = [line.strip() for line in str(text).strip().splitlines()] + body = "\n".join(f" * {line}" if line else " *" for line in lines) + return f"/**\n{body}\n */" + + +def ts_string_literal(text: str) -> str: + """Escape to a valid TypeScript string literal using backticks.""" + escaped = str(text).replace("`", "\\`").replace("\\", "\\\\") + return f"`{escaped}`" + + +def ts_optional(type_str: str) -> str: + """Return a TS optional type representation.""" + return f"{type_str} | undefined" + + +def ts_array(type_str: str) -> str: + """Return a TS array type representation.""" + return f"Array<{type_str}>" + + +_WORD_BOUNDARY_RE = re.compile(r"([a-z0-9])([A-Z])") +_NON_ALNUM_RE = re.compile(r"[^A-Za-z0-9]+") + +_U32_MAX_VALUE = 4294967295 +_SMALL_INTEGER_MAX = 100 +_ENUM_KEYWORDS = ( + "value `1`", + "value `2`", + "value 1", + "value 2", + "refers to", + "type.", + "action.", + "enum", +) + + +@cache +def _split_words(name: str) -> tuple[str, ...]: + """Split name into words for case conversion.""" + normalized = _NON_ALNUM_RE.sub(" ", _WORD_BOUNDARY_RE.sub(r"\1 \2", name)).strip() + parts = tuple(part for part in normalized.split() if part) + return parts or (name,) + + +def ts_pascal_case(name: str) -> str: + """Convert name to PascalCase.""" + return "".join(part.capitalize() for part in _split_words(name)) + + +def ts_camel_case(name: str) -> str: + """Convert name to camelCase.""" + pascal = ts_pascal_case(name) + return pascal[:1].lower() + pascal[1:] if pascal else pascal + + +def ts_kebab_case(name: str) -> str: + """Convert name to kebab-case.""" + return "-".join(part.lower() for part in _split_words(name)) + + +def ts_property_name(name: str) -> str: + """Return a safe TS property name, quoting if necessary.""" + return name if _IDENTIFIER_RE.match(name) else f"'{name}'" + + +# ---------- OpenAPI -> TS type mapping ---------- + + +def _extract_ref_name(ref_string: str) -> str: + return ref_string.split("/")[-1] + + +def _union(types: Iterable[str]) -> str: + """Create TypeScript union type from list of types.""" + uniqued = tuple(dict.fromkeys(t for t in types if t)) + return " | ".join(uniqued) if uniqued else TypeScriptType.NEVER + + +def _intersection(types: Iterable[str]) -> str: + """Create TypeScript intersection type from list of types.""" + parts = [t for t in types if t and t != TypeScriptType.ANY] + return " & ".join(parts) if parts else TypeScriptType.ANY + + +def _nullable(type_str: str, schema: Schema, schemas: Schemas | None) -> str: + # OpenAPI 3.0 nullable flag + if schema.get(SchemaKey.NULLABLE) is True: + return _union((type_str, TypeScriptType.NULL)) + + # OpenAPI 3.1 union type with null + t = schema.get(SchemaKey.TYPE) + if isinstance(t, list) and TypeScriptType.NULL in t: + non_nulls = [x for x in t if x != TypeScriptType.NULL] + # If there's exactly one non-null type, union with null + if len(non_nulls) == 1: + return _union((ts_type({SchemaKey.TYPE: non_nulls[0]}, schemas), TypeScriptType.NULL)) + # Else, build a union of all non-nulls + null + inner = _union(ts_type({SchemaKey.TYPE: n}, schemas) for n in non_nulls) + return _union((inner, TypeScriptType.NULL)) + + return type_str + + +def _inline_object(schema: Schema, schemas: Schemas | None) -> str: + properties: dict[str, Any] = schema.get(SchemaKey.PROPERTIES, {}) or {} + required = set(schema.get(SchemaKey.REQUIRED, []) or []) + parts: list[str] = [] + + for prop_name, prop_schema in properties.items(): + canonical_name = prop_schema.get(constants.X_ALGOKIT_FIELD_RENAME) or prop_name + # Add property description as doc comment + description = prop_schema.get("description") + if description: + doc_comment = ts_doc_comment(description) + indented_doc = "\n ".join(doc_comment.split("\n")) + parts.append(f"\n {indented_doc}") + + # Generate camelCase TS property names for better DX + ts_name = ts_camel_case(canonical_name) + ts_t = ts_type(prop_schema, schemas) + opt = "" if prop_name in required else "?" + parts.append(f"{ts_name}{opt}: {ts_t};") + + # additionalProperties -> index signature + if "additionalProperties" in schema: + addl = schema["additionalProperties"] + if addl is True: + parts.append("[key: string]: unknown;") + elif isinstance(addl, dict): + parts.append(f"[key: string]: {ts_type(addl, schemas)};") + + if parts: + # Format with proper indentation + formatted_parts = [] + for part in parts: + if part.startswith("\n"): + formatted_parts.append(part) + else: + formatted_parts.append(f" {part}") + return "{\n" + "\n".join(formatted_parts) + "\n}" + return "Record" + + +def _map_primitive(schema_type: str, schema_format: str | None, schema: Schema) -> str: + """Map OpenAPI primitive types to TypeScript types.""" + if schema_type == "integer": + schema_format = schema.get(SchemaKey.FORMAT) + is_declared_bigint = schema.get(constants.X_ALGOKIT_BIGINT) is True + is_signed_32_bit = schema_format == "int32" + coerced_to_number = False + + if not is_declared_bigint and not is_signed_32_bit: + maximum: int | None = schema.get(SchemaKey.MAXIMUM) + minimum: int | None = schema.get(SchemaKey.MINIMUM) + description = str(schema.get("description", "")).lower() + + if ( + (maximum is not None and maximum <= _U32_MAX_VALUE) + or (minimum is not None and minimum >= 0 and maximum is not None and maximum <= _SMALL_INTEGER_MAX) + or any(keyword in description for keyword in _ENUM_KEYWORDS) + ): + coerced_to_number = True + + result = ( + TypeScriptType.BIGINT + if is_declared_bigint + else TypeScriptType.NUMBER + if is_signed_32_bit or coerced_to_number + else TypeScriptType.BIGINT + ) + elif schema_type == "number": + result = TypeScriptType.NUMBER + elif schema_type == "string": + is_byte = schema_format == "byte" or schema.get(constants.X_ALGOKIT_BYTES_BASE64) is True + result = TypeScriptType.UINT8ARRAY if is_byte else TypeScriptType.STRING + elif schema_type == "boolean": + result = TypeScriptType.BOOLEAN + else: + result = TypeScriptType.ANY + + return result + + +def ts_enum_type(schema: Schema) -> str | None: + if SchemaKey.ENUM not in schema: + return None + + if schema.get(constants.X_ALGOKIT_BIGINT) is True: + # For bigint-marked enums, use bigint type directly + return TypeScriptType.BIGINT + + type_val = schema.get(SchemaKey.TYPE) + values = schema.get(SchemaKey.ENUM, []) + + if type_val == "string": + return " | ".join([f"'{v!s}'" for v in values]) + + if type_val == "integer": + # Integers used as enum discriminators are small; map to number + return " | ".join([str(v) for v in values]) + + # Fallback: treat as string literals + return " | ".join([f"'{v!s}'" for v in values]) + + +def ts_type(schema: Schema | None, schemas: Schemas | None = None) -> str: + """Map OpenAPI schema to a TypeScript type string.""" + if not schema: + return TypeScriptType.ANY + + if isinstance(schema, dict) and schema.get(constants.X_ALGOKIT_SIGNED_TXN) is True: + return "SignedTransaction" + + if "$ref" in schema: + ref_name = _extract_ref_name(schema["$ref"]) + return ts_pascal_case(ref_name) + + return _ts_type_inner(schema, schemas) + + +def _ts_type_inner(schema: Schema, schemas: Schemas | None) -> str: + processors: list[tuple[str, _TypeProcessor]] = [ + (SchemaKey.ALL_OF, _process_all_of), + (SchemaKey.ONE_OF, _process_one_of), + (SchemaKey.ANY_OF, _process_any_of), + ] + + for key, handler in processors: + if key in schema: + return handler(schema, schemas) + + enum_type = ts_enum_type(schema) + if enum_type: + return enum_type + + return _map_non_composite(schema, schemas) + + +def _map_non_composite(schema: Schema, schemas: Schemas | None) -> str: + schema_type = schema.get(SchemaKey.TYPE) + + if schema_type == "array": + items_schema = schema.get(SchemaKey.ITEMS, {}) + is_signed_txn = isinstance(items_schema, dict) and (items_schema.get(constants.X_ALGOKIT_SIGNED_TXN) is True) + items_type = "SignedTransaction" if is_signed_txn else ts_type(items_schema, schemas) + return f"{items_type}[]" + + if schema_type == TypeScriptType.OBJECT or ( + not schema_type and (SchemaKey.PROPERTIES in schema or SchemaKey.ADDITIONAL_PROPERTIES in schema) + ): + object_type = _inline_object(schema, schemas) + return _nullable(object_type, schema, schemas) + + primitive_type = _map_primitive(str(schema_type), schema.get(SchemaKey.FORMAT), schema) + return _nullable(primitive_type, schema, schemas) + + +_TypeProcessor = Callable[[Schema, Schemas | None], str] + + +def _process_all_of(schema: Schema, schemas: Schemas | None) -> str: + parts = schema.get(SchemaKey.ALL_OF, []) + return _intersection(ts_type(part, schemas) for part in parts) + + +def _process_one_of(schema: Schema, schemas: Schemas | None) -> str: + options = schema.get(SchemaKey.ONE_OF, []) + return _union(ts_type(option, schemas) for option in options) + + +def _process_any_of(schema: Schema, schemas: Schemas | None) -> str: + options = schema.get(SchemaKey.ANY_OF, []) + return _union(ts_type(option, schemas) for option in options) + + +# ---------- Response helpers ---------- + + +def has_msgpack_2xx(responses: Schema) -> bool: + for status, resp in (responses or {}).items(): + if not str(status).startswith(constants.SUCCESS_STATUS_PREFIX): + continue + content = (resp or {}).get(OperationKey.CONTENT, {}) + if any(ct in (content or {}) for ct in (MediaType.MSGPACK, MediaType.BINARY)): + return True + return False + + +def response_content_types(responses: Schema) -> list[str]: + content_types: set[str] = set() + for status, resp in (responses or {}).items(): + if not str(status).startswith(constants.SUCCESS_STATUS_PREFIX): + continue + content = (resp or {}).get(OperationKey.CONTENT, {}) + content_types.update(content) + return sorted(content_types) + + +def collect_schema_refs(schema: Schema, current_schema_name: str | None = None) -> list[str]: + """Collect referenced schema names, excluding self-references.""" + refs: set[str] = set() + target_name = ts_pascal_case(current_schema_name) if current_schema_name else None + stack: list[Any] = [schema] + + while stack: + node = stack.pop() + if not isinstance(node, dict): + continue + if "$ref" in node: + ref_name = ts_pascal_case(_extract_ref_name(node["$ref"])) + if target_name is None or ref_name != target_name: + refs.add(ref_name) + continue + + props = node.get(SchemaKey.PROPERTIES) + if isinstance(props, dict): + stack.extend(props.values()) + + items = node.get(SchemaKey.ITEMS) + if isinstance(items, dict): + stack.append(items) + + for key in (SchemaKey.ALL_OF, SchemaKey.ONE_OF, SchemaKey.ANY_OF): + collection = node.get(key) + if isinstance(collection, list): + stack.extend(child for child in collection if isinstance(child, dict)) + + additional = node.get(SchemaKey.ADDITIONAL_PROPERTIES) + if isinstance(additional, dict): + stack.append(additional) + + return sorted(refs) + + +def schema_uses_signed_txn(schema: Schema) -> bool: + """Detect if a schema (recursively) uses the x-algokit-signed-txn vendor extension.""" + stack: list[Any] = [schema] + + while stack: + node = stack.pop() + if not isinstance(node, dict): + continue + if node.get(constants.X_ALGOKIT_SIGNED_TXN) is True: + return True + if "$ref" in node: + continue + + props = node.get(constants.SchemaKey.PROPERTIES) + if isinstance(props, dict): + stack.extend(props.values()) + + items = node.get(constants.SchemaKey.ITEMS) + if isinstance(items, dict): + stack.append(items) + + for key in (constants.SchemaKey.ALL_OF, constants.SchemaKey.ONE_OF, constants.SchemaKey.ANY_OF): + collection = node.get(key) + if isinstance(collection, list): + stack.extend(child for child in collection if isinstance(child, dict)) + + addl = node.get(constants.SchemaKey.ADDITIONAL_PROPERTIES) + if isinstance(addl, dict): + stack.append(addl) + + return False + + +# ---------- Type string helpers for templates ---------- + + +def ts_is_array_type(type_str: str) -> bool: + t = (type_str or "").strip() + return t.endswith("[]") or (t.startswith("Array<") and t.endswith(">")) + + +def ts_array_item_type(type_str: str) -> str: + t = (type_str or "").strip() + if t.endswith("[]"): + return t[:-2] + if t.startswith("Array<") and t.endswith(">"): + return t[len("Array<") : -1] + return t + + +def ts_is_builtin_or_primitive(type_str: str) -> bool: + t = (type_str or "").strip() + return t in constants.TS_BUILTIN_TYPES or t in {TypeScriptType.ANY, TypeScriptType.NULL, TypeScriptType.OBJECT} + + +def ts_is_model_type(type_str: str) -> bool: + t = (type_str or "").strip() + if ts_is_array_type(t): + t = ts_array_item_type(t) + # Treat PascalCase identifiers as model types and exclude TS builtins + return bool(re.match(r"^[A-Z][A-Za-z0-9_]*$", t)) and not ts_is_builtin_or_primitive(t) + + +FILTERS: dict[str, Any] = { + "ts_doc_comment": ts_doc_comment, + "ts_string_literal": ts_string_literal, + "ts_optional": ts_optional, + "ts_array": ts_array, + "ts_type": ts_type, + "ts_pascal_case": ts_pascal_case, + "ts_camel_case": ts_camel_case, + "ts_kebab_case": ts_kebab_case, + "ts_property_name": ts_property_name, + "has_msgpack_2xx": has_msgpack_2xx, + "response_content_types": response_content_types, + "collect_schema_refs": collect_schema_refs, + "schema_uses_signed_txn": schema_uses_signed_txn, + "ts_is_array_type": ts_is_array_type, + "ts_array_item_type": ts_array_item_type, + "ts_is_builtin_or_primitive": ts_is_builtin_or_primitive, + "ts_is_model_type": ts_is_model_type, +} diff --git a/api/oas_generator/ts_oas_generator/generator/models.py b/api/oas_generator/ts_oas_generator/generator/models.py new file mode 100644 index 000000000..4f22a4b1e --- /dev/null +++ b/api/oas_generator/ts_oas_generator/generator/models.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +Schema = dict[str, Any] +Operation = dict[str, Any] +TemplateContext = dict[str, Any] +FileMap = dict[Path, str] + + +@dataclass +class Parameter: + """Represents an API parameter.""" + + name: str + var_name: str + location: str + required: bool + ts_type: str + description: str | None = None + stringify_bigint: bool = False + + +@dataclass +class RequestBody: + """Represents a request body specification.""" + + media_type: str + ts_type: str + required: bool + supports_msgpack: bool = False + supports_json: bool = False + + +@dataclass +class OperationContext: + """Complete context for an API operation.""" + + operation_id: str + method: str + path: str + description: str | None + parameters: list[Parameter] + request_body: RequestBody | None + response_type: str + import_types: set[str] + tags: list[str] | None = None + returns_msgpack: bool = False + has_format_param: bool = False + format_var_name: str | None = None + error_types: list[ErrorDescriptor] | None = None + + def to_dict(self) -> dict[str, Any]: + """Convert to dictionary for template rendering.""" + return { + "operationId": self.operation_id, + "method": self.method, + "path": self.path, + "description": self.description, + "parameters": [self._param_to_dict(p) for p in self.parameters], + "pathParameters": [self._param_to_dict(p) for p in self.parameters if p.location == "path"], + "otherParameters": [self._param_to_dict(p) for p in self.parameters if p.location in {"query", "header"}], + "requestBody": self._request_body_to_dict(self.request_body) if self.request_body else None, + "responseTsType": self.response_type, + "returnsMsgpack": self.returns_msgpack, + "hasFormatParam": self.has_format_param, + "formatVarName": self.format_var_name, + "errorTypes": [self._error_to_dict(e) for e in (self.error_types or [])], + } + + @staticmethod + def _error_to_dict(error: ErrorDescriptor) -> dict[str, Any]: + return { + "errorName": error.error_name, + "statusCodes": error.status_codes, + "errorType": error.error_type, + "description": error.description, + "isArray": error.is_array, + "arrayItemType": error.array_item_type, + } + + @staticmethod + def _param_to_dict(param: Parameter) -> dict[str, Any]: + return { + "name": param.name, + "varName": param.var_name, + "in": param.location, + "required": param.required, + "tsType": param.ts_type, + "description": param.description, + "stringifyBigInt": param.stringify_bigint, + } + + @staticmethod + def _request_body_to_dict(body: RequestBody) -> dict[str, Any]: + return { + "mediaType": body.media_type, + "tsType": body.ts_type, + "required": body.required, + "supportsMsgpack": body.supports_msgpack, + "supportsJson": body.supports_json, + } + + +@dataclass +class FieldDescriptor: + """Descriptor for a single model field used by templates.""" + + name: str + wire_name: str + ts_type: str + is_array: bool + ref_model: str | None + is_bytes: bool + is_bigint: bool + is_signed_txn: bool + is_optional: bool + is_nullable: bool + + +@dataclass +class ErrorDescriptor: + """Descriptor for error response handling with structured error types.""" + + error_name: str + status_codes: list[str] + error_type: str + description: str | None = None + is_array: bool = False + array_item_type: str | None = None + + +@dataclass +class ModelDescriptor: + """Descriptor for a schema model including field metadata.""" + + model_name: str + fields: list[FieldDescriptor] + is_object: bool + is_array: bool = False + array_item_ref: str | None = None + array_item_is_bytes: bool = False + array_item_is_bigint: bool = False + array_item_is_signed_txn: bool = False diff --git a/api/oas_generator/ts_oas_generator/generator/template_engine.py b/api/oas_generator/ts_oas_generator/generator/template_engine.py new file mode 100644 index 000000000..519c342d9 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/generator/template_engine.py @@ -0,0 +1,723 @@ +from __future__ import annotations + +import re +from collections.abc import Mapping +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from jinja2 import Environment, FileSystemLoader, select_autoescape + +from ts_oas_generator import constants +from ts_oas_generator.generator.filters import FILTERS, ts_camel_case, ts_kebab_case, ts_pascal_case, ts_type +from ts_oas_generator.generator.models import ( + FieldDescriptor, + ModelDescriptor, + OperationContext, + Parameter, + RequestBody, +) +from ts_oas_generator.parser.oas_parser import OASParser + +# Type aliases for clarity +type Schema = dict[str, Any] +type Schemas = Mapping[str, Schema] +type TemplateContext = dict[str, Any] +type FileMap = dict[Path, str] + +_TYPE_TOKEN_RE = re.compile(r"\b[A-Z][A-Za-z0-9_]*\b") + + +@dataclass +class OperationInput: + """Inputs required to build an `OperationContext`.""" + + operation_id: str + method: str + path: str + operation: Schema + path_params: list[Schema] + spec: Schema + + +class TemplateRenderer: + """Handles template rendering operations.""" + + def __init__(self, template_dir: Path | None = None) -> None: + if template_dir is None: + template_dir = Path(__file__).parent.parent / constants.DEFAULT_TEMPLATE_DIR + + self.template_dir = Path(template_dir) + self.env = self._create_environment() + + def _create_environment(self) -> Environment: + env = Environment( + loader=FileSystemLoader(str(self.template_dir)), + autoescape=select_autoescape(["html", "xml"]), + trim_blocks=constants.TEMPLATE_TRIM_BLOCKS, + lstrip_blocks=constants.TEMPLATE_LSTRIP_BLOCKS, + ) + env.filters.update(FILTERS) + return env + + def render(self, template_name: str, context: TemplateContext) -> str: + template = self.env.get_template(template_name) + return template.render(**context) + + def render_batch(self, template_map: dict[Path, tuple[str, TemplateContext]]) -> FileMap: + return {path: self.render(template, context) for path, (template, context) in template_map.items()} + + +class SchemaProcessor: + """Processes OpenAPI schemas and generates TypeScript models.""" + + def __init__(self, renderer: TemplateRenderer) -> None: + self.renderer = renderer + self._wire_to_canonical: dict[str, str] = {} + self._camel_to_wire: dict[str, str] = {} + + def generate_models(self, output_dir: Path, schemas: Schemas) -> FileMap: + models_dir = output_dir / constants.DirectoryName.SRC / constants.DirectoryName.MODELS + files: FileMap = {} + + # Generate individual model files + for name, schema in schemas.items(): + descriptor = self._build_model_descriptor(name, schema, schemas) + context = self._create_model_context(name, schema, schemas, descriptor) + content = self.renderer.render(constants.MODEL_TEMPLATE, context) + file_name = f"{ts_kebab_case(name)}{constants.MODEL_FILE_EXTENSION}" + files[models_dir / file_name] = content + + files[models_dir / constants.INDEX_FILE] = self.renderer.render( + constants.MODELS_INDEX_TEMPLATE, + {"schemas": schemas}, + ) + + return files + + def _create_model_context( + self, name: str, schema: Schema, all_schemas: Schemas, descriptor: ModelDescriptor + ) -> TemplateContext: + is_object = self._is_object_schema(schema) + properties = self._extract_properties(schema) if is_object else [] + + return { + "schema_name": name, + "schema": schema, + "schemas": all_schemas, + "is_object": is_object, + "properties": properties, + "has_additional_properties": schema.get(constants.SchemaKey.ADDITIONAL_PROPERTIES) is not None, + "additional_properties_type": schema.get(constants.SchemaKey.ADDITIONAL_PROPERTIES), + "descriptor": descriptor, + } + + @staticmethod + def _is_object_schema(schema: Schema) -> bool: + is_type_object = schema.get(constants.SchemaKey.TYPE) == constants.TypeScriptType.OBJECT + has_properties = constants.SchemaKey.PROPERTIES in schema + has_composition = any( + k in schema for k in [constants.SchemaKey.ALL_OF, constants.SchemaKey.ONE_OF, constants.SchemaKey.ANY_OF] + ) + return (is_type_object or has_properties) and not has_composition + + def _extract_properties(self, schema: Schema) -> list[dict[str, Any]]: + properties = [] + required_fields = set(schema.get(constants.SchemaKey.REQUIRED, [])) + + for prop_name, prop_schema in (schema.get(constants.SchemaKey.PROPERTIES) or {}).items(): + self._register_rename(prop_name, prop_schema) + properties.append( + { + "name": prop_name, + "schema": prop_schema, + "is_required": prop_name in required_fields, + } + ) + + return properties + + def _register_rename(self, wire_name: str, schema: Schema) -> None: + rename_value = schema.get(constants.X_ALGOKIT_FIELD_RENAME) + if not isinstance(rename_value, str) or not rename_value: + return + + # Preserve first occurrence to avoid accidental overrides from conflicting specs + self._wire_to_canonical.setdefault(wire_name, rename_value) + self._camel_to_wire.setdefault(ts_camel_case(rename_value), wire_name) + + @property + def rename_mappings(self) -> tuple[dict[str, str], dict[str, str]]: + return self._wire_to_canonical, self._camel_to_wire + + def _build_model_descriptor(self, name: str, schema: Schema, all_schemas: Schemas) -> ModelDescriptor: + """Build a per-model descriptor from OAS schema and vendor extensions.""" + model_name = ts_pascal_case(name) + + # Top-level array schema support + if isinstance(schema, dict) and schema.get(constants.SchemaKey.TYPE) == "array": + items = schema.get(constants.SchemaKey.ITEMS, {}) or {} + ref_model = None + if isinstance(items, dict) and "$ref" in items: + ref = items["$ref"].split("/")[-1] + ref_model = ts_pascal_case(ref) + fmt = items.get(constants.SchemaKey.FORMAT) + is_bytes = fmt == "byte" or items.get(constants.X_ALGOKIT_BYTES_BASE64) is True + is_bigint = bool(items.get(constants.X_ALGOKIT_BIGINT) is True) + is_signed_txn = bool(items.get(constants.X_ALGOKIT_SIGNED_TXN) is True) + return ModelDescriptor( + model_name=model_name, + fields=[], + is_object=False, + is_array=True, + array_item_ref=ref_model, + array_item_is_bytes=is_bytes, + array_item_is_bigint=is_bigint, + array_item_is_signed_txn=is_signed_txn, + ) + + # Object schema descriptor + fields: list[FieldDescriptor] = [] + is_object = self._is_object_schema(schema) + required_fields = set(schema.get(constants.SchemaKey.REQUIRED, []) or []) + props = schema.get(constants.SchemaKey.PROPERTIES) or {} + for prop_name, prop_schema in props.items(): + wire_name = prop_name + canonical = prop_schema.get(constants.X_ALGOKIT_FIELD_RENAME) or prop_name + name_camel = ts_camel_case(canonical) + + ts_t = ts_type(prop_schema, all_schemas) + is_array = prop_schema.get(constants.SchemaKey.TYPE) == "array" + items = prop_schema.get(constants.SchemaKey.ITEMS, {}) if is_array else None + ref_model = None + signed_txn = False + bytes_flag = False + bigint_flag = False + if is_array and isinstance(items, dict): + if "$ref" in items: + ref_model = ts_pascal_case(items["$ref"].split("/")[-1]) + fmt = items.get(constants.SchemaKey.FORMAT) + bytes_flag = fmt == "byte" or items.get(constants.X_ALGOKIT_BYTES_BASE64) is True + bigint_flag = bool(items.get(constants.X_ALGOKIT_BIGINT) is True) + signed_txn = bool(items.get(constants.X_ALGOKIT_SIGNED_TXN) is True) + else: + if "$ref" in (prop_schema or {}): + ref_model = ts_pascal_case(prop_schema["$ref"].split("/")[-1]) + fmt = prop_schema.get(constants.SchemaKey.FORMAT) + bytes_flag = fmt == "byte" or prop_schema.get(constants.X_ALGOKIT_BYTES_BASE64) is True + bigint_flag = bool(prop_schema.get(constants.X_ALGOKIT_BIGINT) is True) + signed_txn = bool(prop_schema.get(constants.X_ALGOKIT_SIGNED_TXN) is True) + + is_optional = prop_name not in required_fields + # Nullable per OpenAPI + is_nullable = bool(prop_schema.get(constants.SchemaKey.NULLABLE) is True) + + fields.append( + FieldDescriptor( + name=name_camel, + wire_name=wire_name, + ts_type=ts_t, + is_array=is_array, + ref_model=ref_model, + is_bytes=bytes_flag, + is_bigint=bigint_flag, + is_signed_txn=signed_txn, + is_optional=is_optional, + is_nullable=is_nullable, + ) + ) + + return ModelDescriptor(model_name=model_name, fields=fields, is_object=is_object) + + +class OperationProcessor: + """Processes OpenAPI operations and generates API services.""" + + def __init__(self, renderer: TemplateRenderer) -> None: + self.renderer = renderer + self._model_names: set[str] = set() + self._synthetic_models: dict[str, Schema] = {} + + def process_spec(self, spec: Schema) -> tuple[dict[str, list[OperationContext]], set[str], dict[str, Schema]]: + """Process entire OpenAPI spec and return operations by tag.""" + self._initialize_model_names(spec) + + operations_by_tag: dict[str, list[OperationContext]] = {} + tags: set[str] = set() + + paths = spec.get(constants.SchemaKey.PATHS, {}) + for path, path_item in paths.items(): + if not isinstance(path_item, dict): + continue + + operations = self._process_path_operations(path, path_item, spec) + for op in operations: + for tag in op.tags: + tags.add(tag) + operations_by_tag.setdefault(tag, []).append(op) + + # Sort operations by ID for stability + for ops in operations_by_tag.values(): + ops.sort(key=lambda o: o.operation_id) + + return operations_by_tag, tags, self._synthetic_models + + def generate_service( + self, + output_dir: Path, + operations_by_tag: dict[str, list[OperationContext]], + tags: set[str], + service_class_name: str, + ) -> FileMap: + """Generate API service files.""" + apis_dir = output_dir / constants.DirectoryName.SRC / constants.DirectoryName.APIS + files: FileMap = {} + + # Collect unique operations + all_operations = self._collect_unique_operations(operations_by_tag, tags) + + # Convert to template context + operations_context = [op.to_dict() for op in all_operations] + import_types = set().union(*(op.import_types for op in all_operations)) + + # Generate service file + files[apis_dir / constants.API_SERVICE_FILE] = self.renderer.render( + constants.API_SERVICE_TEMPLATE, + { + "tag_name": constants.DEFAULT_API_TAG, + "operations": operations_context, + "import_types": sorted(import_types), + "service_class_name": service_class_name, + }, + ) + + # Generate barrel export + files[apis_dir / constants.INDEX_FILE] = self.renderer.render( + constants.APIS_INDEX_TEMPLATE, {"service_class_name": service_class_name} + ) + + return files + + def _initialize_model_names(self, spec: Schema) -> None: + """Initialize set of model names from spec.""" + + components = spec.get(constants.SchemaKey.COMPONENTS, {}) + schemas = components.get(constants.SchemaKey.COMPONENTS_SCHEMAS, {}) + self._model_names = {ts_pascal_case(name) for name in schemas} + + def _process_path_operations(self, path: str, path_item: Schema, spec: Schema) -> list[OperationContext]: + """Process all operations for a given path.""" + + operations = [] + path_params = path_item.get(constants.OperationKey.PARAMETERS, []) + + for method, operation in path_item.items(): + if method.lower() not in constants.HTTP_METHODS or not isinstance(operation, dict): + continue + + # Generate operation ID if missing + operation_id = operation.get(constants.OperationKey.OPERATION_ID) or ts_camel_case( + f"{method.lower()}_{path}" + ) + + op_input = OperationInput( + operation_id=operation_id, + method=method.upper(), + path=path, + operation=operation, + path_params=path_params, + spec=spec, + ) + + # Process operation + context = self._create_operation_context(op_input) + + context.tags = operation.get(constants.OperationKey.TAGS, [constants.DEFAULT_TAG]) + operations.append(context) + + return operations + + def _create_operation_context(self, op_input: OperationInput) -> OperationContext: + """Create complete operation context.""" + # Merge path and operation parameters + all_params = [*op_input.path_params, *op_input.operation.get(constants.OperationKey.PARAMETERS, [])] + + # Process components + parameters = self._process_parameters(all_params, op_input.spec) + request_body = self._process_request_body( + op_input.operation.get(constants.OperationKey.REQUEST_BODY), op_input.spec + ) + response_type, returns_msgpack = self._process_responses( + op_input.operation.get(constants.OperationKey.RESPONSES, {}), op_input.operation_id, op_input.spec + ) + + # Build context + context = OperationContext( + operation_id=op_input.operation_id, + method=op_input.method, + path=op_input.path, + description=op_input.operation.get(constants.OperationKey.DESCRIPTION), + parameters=parameters, + request_body=request_body, + response_type=response_type, + import_types=set(), + tags=[], + returns_msgpack=returns_msgpack, + ) + + # Compute additional properties + self._compute_format_param(context) + self._compute_import_types(context) + + return context + + def _process_parameters(self, params: list[Schema], spec: Schema) -> list[Parameter]: + """Process operation parameters.""" + + parameters = [] + used_names: set[str] = set() + schemas = self._get_schemas(spec) + + for param_def in params: + # Resolve $ref if present + param = self._resolve_ref(param_def, spec) if "$ref" in param_def else param_def + + # Extract parameter details + raw_name = str(param.get("name")) + var_name = self._sanitize_variable_name(ts_camel_case(raw_name), used_names) + used_names.add(var_name) + + schema = param.get("schema", {}) + ts_type_str = ts_type(schema, schemas) + + # Handle bigint ergonomics + if ts_type_str == constants.TypeScriptType.BIGINT: + ts_type_str = constants.TypeScriptType.NUMBER_OR_BIGINT + stringify_bigint = True + else: + stringify_bigint = constants.TypeScriptType.BIGINT in ts_type_str + + location = param.get(constants.OperationKey.IN, constants.ParamLocation.QUERY) + required = param.get(constants.SchemaKey.REQUIRED, False) or location == constants.ParamLocation.PATH + + parameters.append( + Parameter( + name=raw_name, + var_name=var_name, + location=location, + required=required, + ts_type=ts_type_str, + description=param.get(constants.OperationKey.DESCRIPTION), + stringify_bigint=stringify_bigint, + ) + ) + + return parameters + + def _process_request_body(self, request_body: Schema | None, spec: Schema) -> RequestBody | None: + """Process request body specification.""" + + if not isinstance(request_body, dict): + return None + + content = request_body.get("content", {}) + schemas = self._get_schemas(spec) + + # Check content type support + supports_msgpack = constants.MediaType.MSGPACK in content + supports_json = constants.MediaType.JSON in content + required = request_body.get(constants.SchemaKey.REQUIRED, False) + + # Determine media type and TypeScript type + if supports_json or supports_msgpack: + media_type = ( + constants.MediaType.MSGPACK if supports_msgpack and not supports_json else constants.MediaType.JSON + ) + schema = content.get(media_type, {}).get("schema", {}) + ts_type_str = ts_type(schema, schemas) + elif constants.MediaType.TEXT in content: + media_type = constants.MediaType.TEXT + schema = content[constants.MediaType.TEXT].get("schema", {}) + ts_type_str = ts_type(schema, schemas) + supports_msgpack = supports_json = False + elif constants.MediaType.BINARY in content or constants.MediaType.OCTET_STREAM in content: + media_type = ( + constants.MediaType.BINARY + if constants.MediaType.BINARY in content + else constants.MediaType.OCTET_STREAM + ) + ts_type_str = constants.TypeScriptType.UINT8ARRAY + supports_msgpack = supports_json = False + else: + return None + + return RequestBody( + media_type=media_type, + ts_type=ts_type_str, + required=required, + supports_msgpack=supports_msgpack, + supports_json=supports_json, + ) + + def _process_responses(self, responses: Schema, operation_id: str, spec: Schema) -> tuple[str, bool]: + """Process response specifications.""" + + return_types: list[str] = [] + returns_msgpack = False + schemas = self._get_schemas(spec) + + for status, response in responses.items(): + if not str(status).startswith(constants.SUCCESS_STATUS_PREFIX): + continue + + content = (response or {}).get("content", {}) + if constants.MediaType.MSGPACK in content: + returns_msgpack = True + + for _, media_details in content.items(): + schema = (media_details or {}).get("schema") + if not schema: + continue + + type_name = self._resolve_response_type(schema, operation_id, schemas) + if type_name: + return_types.append(type_name) + + # Determine final response type + if return_types: + response_type = " | ".join(dict.fromkeys(return_types)) + elif returns_msgpack: + response_type = constants.TypeScriptType.UINT8ARRAY + else: + response_type = constants.TypeScriptType.VOID + + return response_type, returns_msgpack + + def _compute_format_param(self, context: OperationContext) -> None: + """Detect and set format parameter for content negotiation.""" + for param in context.parameters: + if param.location == constants.ParamLocation.QUERY and param.name == constants.FORMAT_PARAM_NAME: + context.has_format_param = True + context.format_var_name = param.var_name + break + + def _compute_import_types(self, context: OperationContext) -> None: + """Collect model types that need importing.""" + builtin_types = constants.TS_BUILTIN_TYPES + + def extract_types(type_str: str) -> set[str]: + if not type_str: + return set() + tokens = set(_TYPE_TOKEN_RE.findall(type_str)) + types: set[str] = {tok for tok in tokens if tok in self._model_names and tok not in builtin_types} + # Include synthetic models that aren't part of _model_names + if "AlgokitSignedTransaction" in tokens: + types.add("AlgokitSignedTransaction") + return types + + # Collect from all type references + context.import_types = extract_types(context.response_type) + + if context.request_body: + context.import_types |= extract_types(context.request_body.ts_type) + + for param in context.parameters: + context.import_types |= extract_types(param.ts_type) + + def _collect_unique_operations( + self, operations_by_tag: dict[str, list[OperationContext]], tags: set[str] + ) -> list[OperationContext]: + """Collect unique operations across all tags.""" + seen_keys: set[tuple[str, str]] = set() + unique_operations = [] + + for tag in sorted(tags): + for op in operations_by_tag.get(tag, []): + key = (op.method, op.path) + if key not in seen_keys: + seen_keys.add(key) + unique_operations.append(op) + + return sorted(unique_operations, key=lambda o: o.operation_id) + + @staticmethod + def _should_synthesize_model(schema: Schema) -> bool: + """Check if schema should become a synthetic model.""" + return ( + isinstance(schema, dict) + and "$ref" not in schema + and (schema.get("type") == "object" or "properties" in schema or "additionalProperties" in schema) + ) + + def _resolve_response_type(self, schema: Schema, operation_id: str, schemas: Schema) -> str: + """Ensure response schemas are represented by concrete TypeScript types.""" + if "$ref" in schema: + return ts_type(schema, schemas) + + if self._should_synthesize_model(schema): + base_name = ts_pascal_case(operation_id) + if base_name in self._model_names and base_name not in self._synthetic_models: + return base_name + if base_name in self._synthetic_models: + return base_name + model_name = self._allocate_synthetic_model_name(operation_id) + if model_name not in self._synthetic_models: + self._synthetic_models[model_name] = schema + self._model_names.add(model_name) + return model_name + + return ts_type(schema, schemas) + + def _allocate_synthetic_model_name(self, operation_id: str) -> str: + """Generate a unique model name for an inline response schema.""" + + base_name = ts_pascal_case(operation_id) + candidate = base_name + + if candidate in self._model_names or candidate in self._synthetic_models: + candidate = f"{candidate}Response" + + counter = 2 + while candidate in self._model_names or candidate in self._synthetic_models: + candidate = f"{base_name}Response{counter}" + counter += 1 + + return candidate + + @staticmethod + def _sanitize_variable_name(base_name: str, used_names: set[str]) -> str: + """Ensure variable name is valid and unique.""" + # Handle reserved words + if base_name in constants.TS_RESERVED_WORDS: + base_name = f"{base_name}_" + + # Ensure uniqueness + if base_name not in used_names: + return base_name + + counter = 2 + while f"{base_name}{counter}" in used_names: + counter += 1 + return f"{base_name}{counter}" + + @staticmethod + def _resolve_ref(ref_obj: Schema, spec: Schema) -> Schema: + """Resolve a $ref pointer in the spec.""" + ref = ref_obj.get("$ref", "") + parts = ref.split("/")[1:] # Skip leading # + + node = spec + for part in parts: + node = node.get(part, {}) + return node + + @staticmethod + def _get_schemas(spec: Schema) -> Schema: + """Extract schemas from spec components.""" + components = spec.get(constants.SchemaKey.COMPONENTS, {}) + return components.get(constants.SchemaKey.COMPONENTS_SCHEMAS, {}) + + +class CodeGenerator: + """Main code generator orchestrating the generation process.""" + + def __init__(self, template_dir: Path | None = None) -> None: + self.renderer = TemplateRenderer(template_dir) + self.schema_processor = SchemaProcessor(self.renderer) + self.operation_processor = OperationProcessor(self.renderer) + + def generate( + self, + spec_path: Path, + output_dir: Path, + package_name: str, + *, + custom_description: str | None = None, + ) -> FileMap: + """Generate complete TypeScript client from OpenAPI spec.""" + # Parse specification + parser = OASParser() + parser.parse_file(spec_path) + spec = parser.spec_data or {} + + # Extract class names + client_class, service_class = self._extract_class_names(package_name) + + # Generate base runtime + files = self._generate_runtime(output_dir, package_name, client_class, service_class, custom_description) + + # Process operations and schemas + ops_by_tag, tags, synthetic_models = self.operation_processor.process_spec(spec) + + # Merge schemas + components = spec.get(constants.SchemaKey.COMPONENTS, {}) + base_schemas = components.get(constants.SchemaKey.COMPONENTS_SCHEMAS, {}) + all_schemas = {**base_schemas, **synthetic_models} + + # Generate components + files.update(self.schema_processor.generate_models(output_dir, all_schemas)) + files.update(self.operation_processor.generate_service(output_dir, ops_by_tag, tags, service_class)) + files.update(self._generate_client_files(output_dir, client_class, service_class)) + + return files + + def _generate_runtime( + self, + output_dir: Path, + package_name: str, + client_class: str, + service_class: str, + custom_description: str | None, + ) -> FileMap: + """Generate runtime support files.""" + src_dir = output_dir / constants.DirectoryName.SRC + core_dir = src_dir / constants.DirectoryName.CORE + + context = { + "package_name": package_name, + "custom_description": custom_description, + "client_class_name": client_class, + "service_class_name": service_class, + } + + template_map = { + # Core runtime + core_dir / "client-config.ts": ("base/src/core/client-config.ts.j2", context), + core_dir / "base-http-request.ts": ("base/src/core/base-http-request.ts.j2", context), + core_dir / "fetch-http-request.ts": ("base/src/core/fetch-http-request.ts.j2", context), + core_dir / "api-error.ts": ("base/src/core/api-error.ts.j2", context), + core_dir / "request.ts": ("base/src/core/request.ts.j2", context), + core_dir / "serialization.ts": ("base/src/core/serialization.ts.j2", context), + core_dir / "codecs.ts": ("base/src/core/codecs.ts.j2", context), + core_dir / "model-runtime.ts": ("base/src/core/model-runtime.ts.j2", context), + # Project files + src_dir / "index.ts": ("base/src/index.ts.j2", context), + } + + return self.renderer.render_batch(template_map) + + def _generate_client_files(self, output_dir: Path, client_class: str, service_class: str) -> FileMap: + """Generate client wrapper files.""" + src_dir = output_dir / constants.DirectoryName.SRC + + template_map = { + src_dir / "client.ts": ( + "client.ts.j2", + { + "service_class_name": service_class, + "client_class_name": client_class, + }, + ), + } + + return self.renderer.render_batch(template_map) + + # Centralized transformers/maps removed; per-model codecs handle all transforms. + + @staticmethod + def _extract_class_names(package_name: str) -> tuple[str, str]: + """Extract client and service class names from package name.""" + + base_name = ts_pascal_case(package_name) + base_core = base_name[:-6] if base_name.lower().endswith("client") else base_name + return f"{base_core}Client", f"{base_core}Api" diff --git a/api/oas_generator/ts_oas_generator/parser/__init__.py b/api/oas_generator/ts_oas_generator/parser/__init__.py new file mode 100644 index 000000000..886084a9e --- /dev/null +++ b/api/oas_generator/ts_oas_generator/parser/__init__.py @@ -0,0 +1,5 @@ +"""Parser utilities exposed by ``ts_oas_generator.parser``.""" + +from ts_oas_generator.parser.oas_parser import OASParser + +__all__ = ["OASParser"] diff --git a/api/oas_generator/ts_oas_generator/parser/oas_parser.py b/api/oas_generator/ts_oas_generator/parser/oas_parser.py new file mode 100644 index 000000000..109dca6cd --- /dev/null +++ b/api/oas_generator/ts_oas_generator/parser/oas_parser.py @@ -0,0 +1,46 @@ +"""Lightweight OpenAPI Specification Parser for TypeScript Client Generation. + +This module provides a minimal parser that loads OpenAPI 3.x specifications +for use by the TypeScript generator. It focuses only on loading the spec +data, leaving all processing to the template engine. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + + +class OASParser: + """Simple parser for OpenAPI 3.x specifications.""" + + def __init__(self) -> None: + """Initialize the parser with no loaded data.""" + self.spec_data: dict[str, Any] | None = None + + def parse_file(self, file_path: str | Path) -> None: + """Parse OpenAPI specification from a JSON file. + + Args: + file_path: Path to the OpenAPI specification file (JSON format) + + Raises: + FileNotFoundError: If the file doesn't exist + json.JSONDecodeError: If the file is not valid JSON + """ + path = Path(file_path) + if not path.exists(): + msg = f"OpenAPI spec file not found: {file_path}" + raise FileNotFoundError(msg) + + with path.open(encoding="utf-8") as f: + self.spec_data = json.load(f) + + def parse_dict(self, spec_dict: dict[str, Any]) -> None: + """Parse OpenAPI specification from a dictionary. + + Args: + spec_dict: The OpenAPI specification as a dictionary + """ + self.spec_data = spec_dict diff --git a/api/oas_generator/ts_oas_generator/templates/apis/index.ts.j2 b/api/oas_generator/ts_oas_generator/templates/apis/index.ts.j2 new file mode 100644 index 000000000..59555ee46 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/apis/index.ts.j2 @@ -0,0 +1,2 @@ +// Barrel file for services +export { {{ service_class_name }} } from './api.service'; diff --git a/api/oas_generator/ts_oas_generator/templates/apis/service.ts.j2 b/api/oas_generator/ts_oas_generator/templates/apis/service.ts.j2 new file mode 100644 index 000000000..a310c889c --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/apis/service.ts.j2 @@ -0,0 +1,152 @@ +import type { BaseHttpRequest, ApiRequestOptions } from '../core/base-http-request'; +import { AlgorandSerializer } from '../core/model-runtime'; +{% if import_types and import_types|length > 0 %} +{% set sorted = import_types | sort %} +import type { {{ sorted | join(', ') }} } from '../models/index'; +import { {% for t in sorted %}{{ t }}Meta{% if not loop.last %}, {% endif %}{% endfor %} } from '../models/index'; +{% endif %} + +{% macro field_type_meta(type_name) -%} +{%- if type_name in import_types -%} +{ kind: 'model', meta: () => {{ type_name }}Meta } +{%- elif type_name == 'SignedTransaction' -%} +{ kind: 'codec', codecKey: 'SignedTransaction' } +{%- elif type_name == 'Uint8Array' -%} +{ kind: 'scalar', isBytes: true } +{%- elif type_name == 'bigint' -%} +{ kind: 'scalar', isBigint: true } +{%- else -%} +null +{%- endif -%} +{%- endmacro %} + +{% macro array_meta(type_name) -%} +{%- set inner = type_name.strip() -%} +{%- if inner.endswith('[]') -%} + {%- set base = inner[:-2] -%} +{%- elif inner.startswith('Array<') and inner.endswith('>') -%} + {%- set base = inner[6:-1] -%} +{%- else -%} + {%- set base = None -%} +{%- endif -%} +{%- if base is none -%} +null +{%- else -%} + {%- set item = field_type_meta(base) -%} + {%- if item == 'null' -%} +null + {%- else -%} +({ name: '{{ base }}[]', kind: 'array', arrayItems: {{ item }} }) + {%- endif -%} +{%- endif -%} +{%- endmacro %} + +{% macro base_meta(type_name) -%} +{%- set t = type_name.strip() -%} +{%- if t.endswith('[]') or (t.startswith('Array<') and t.endswith('>')) -%} +{{ array_meta(type_name) }} +{%- else -%} + {%- if t in import_types -%} +{{ t }}Meta + {%- elif t == 'SignedTransaction' -%} +({ name: 'SignedTransaction', kind: 'passthrough', codecKey: 'SignedTransaction' }) + {%- elif t == 'Uint8Array' -%} +({ name: 'Uint8Array', kind: 'passthrough', passThrough: { kind: 'scalar', isBytes: true } }) + {%- elif t == 'bigint' -%} +({ name: 'bigint', kind: 'passthrough', passThrough: { kind: 'scalar', isBigint: true } }) + {%- else -%} +null + {%- endif -%} +{%- endif -%} +{%- endmacro %} + +{% macro meta_expr(type_name) -%} +{%- set meta = base_meta(type_name) -%} +{%- if meta == 'null' -%} +undefined +{%- else -%} +{{ meta }} +{%- endif -%} +{%- endmacro %} + +export class {{ service_class_name }} { + constructor(public readonly httpRequest: BaseHttpRequest) {} + +{% for op in operations %} + {% set is_raw_bytes_body = op.requestBody and op.requestBody.tsType == 'Uint8Array' %} + {{ op.description | ts_doc_comment }} + async {{ op.operationId | ts_camel_case }}( +{%- for p in op.pathParameters %} + {{ p.varName }}: {{ p.tsType }}, +{%- endfor %} +{%- if op.otherParameters|length > 0 or op.requestBody %} + params?: { +{%- for p in op.otherParameters %} + {{ p.varName }}{% if not p.required %}?{% endif %}: {{ p.tsType }}; +{%- endfor %} +{%- if op.requestBody %} + body{% if not op.requestBody.required %}?{% endif %}: {{ op.requestBody.tsType }}; +{%- endif %} + }, +{%- endif %} + requestOptions?: ApiRequestOptions + ): Promise<{{ op.responseTsType }}> { + const headers: Record = {}; + {% set supports_msgpack = op.returnsMsgpack or (op.requestBody and op.requestBody.supportsMsgpack) %} + const responseFormat: 'json' | 'msgpack' = {% if supports_msgpack %}{% if op.hasFormatParam and op.formatVarName %}(params?.{{ op.formatVarName }} as 'json' | 'msgpack' | undefined) ?? 'msgpack'{% else %}'msgpack'{% endif %}{% else %}'json'{% endif %}; + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack'; + + {% if op.requestBody %} + {% if is_raw_bytes_body %} + const serializedBody = params?.body ?? undefined; + const mediaType = 'application/msgpack'; + headers['Content-Type'] = mediaType; + {% else %} + const bodyMeta = {{ meta_expr(op.requestBody.tsType) }}; + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined; + if (mediaType) headers['Content-Type'] = mediaType; + const serializedBody = bodyMeta && params?.body !== undefined + ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) + : params?.body; + {% endif %} + {% else %} + const serializedBody = undefined; + const mediaType = undefined; + {% endif %} + + {% set query_params = op.otherParameters | selectattr('in', 'equalto', 'query') | list %} + {% set header_params = op.otherParameters | selectattr('in', 'equalto', 'header') | list %} + {% if header_params|length > 0 %} + for (const param of [{% for p in header_params %}{ name: '{{ p.name }}', value: params?.{{ p.varName }} }{% if not loop.last %}, {% endif %}{% endfor %}]) { + if (param.value !== undefined) headers[param.name] = String(param.value); + } + {% endif %} + + const payload = await this.httpRequest.request({ + method: '{{ op.method }}', + url: '{{ op.path }}', + path: { +{%- for p in op.pathParameters %} + '{{ p.name }}': {% if p.stringifyBigInt %}(typeof {{ p.varName }} === 'bigint' ? {{ p.varName }}.toString() : {{ p.varName }}){% else %}{{ p.varName }}{% endif %}, +{%- endfor %} + }, + query: { +{%- for p in query_params %} + '{{ p.name }}': {% if p.stringifyBigInt %}(typeof params?.{{ p.varName }} === 'bigint' ? (params!.{{ p.varName }} as bigint).toString() : params?.{{ p.varName }}){% else %}params?.{{ p.varName }}{% endif %}, +{%- endfor %} + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }); + + const responseMeta = {{ meta_expr(op.responseTsType) }}; + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat); + } + return payload as {{ op.responseTsType }}; + } + +{% endfor %} +} diff --git a/packages/typescript/algod_client/src/core/ApiError.ts b/api/oas_generator/ts_oas_generator/templates/base/src/core/api-error.ts.j2 similarity index 85% rename from packages/typescript/algod_client/src/core/ApiError.ts rename to api/oas_generator/ts_oas_generator/templates/base/src/core/api-error.ts.j2 index 17a9213f8..0adbbb200 100644 --- a/packages/typescript/algod_client/src/core/ApiError.ts +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/api-error.ts.j2 @@ -1,4 +1,4 @@ -export class ApiError extends Error { +export class ApiError extends Error { public readonly url: string; public readonly status: number; public readonly body: T | undefined; diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/base-http-request.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/base-http-request.ts.j2 new file mode 100644 index 000000000..9226ab8d8 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/base-http-request.ts.j2 @@ -0,0 +1,22 @@ +import type { ClientConfig } from './client-config'; + +export type QueryValue = string | number | bigint | boolean; +export type QueryParams = Record; + +export type BodyValue = Uint8Array | Record | unknown[] | string | number | boolean | null; + +export interface ApiRequestOptions { + method: string; + url: string; + path?: Record; + query?: QueryParams; + headers?: Record; + body?: BodyValue; + mediaType?: string; + responseHeader?: string; +} + +export abstract class BaseHttpRequest { + constructor(public readonly config: ClientConfig) {} + abstract request(options: ApiRequestOptions): Promise; +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/cancelable-promise.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/cancelable-promise.ts.j2 new file mode 100644 index 000000000..84a895c50 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/cancelable-promise.ts.j2 @@ -0,0 +1,58 @@ +export type OnCancel = (cancelHandler: () => void) => void; + +export class CancelError extends Error { + constructor() { + super('Request aborted'); + this.name = 'CancelError'; + } +} + +export class CancelablePromise implements Promise { + [Symbol.toStringTag] = 'CancelablePromise'; + + private readonly inner: Promise; + private isCancelled = false; + private cancelHandlers: Array<() => void> = []; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: unknown) => void, + onCancel: OnCancel, + ) => void, + ) { + const onCancel: OnCancel = (handler) => { + if (this.isCancelled) { + handler(); + } else { + this.cancelHandlers.push(handler); + } + }; + + this.inner = new Promise((resolve, reject) => executor(resolve, reject, onCancel)); + } + + public cancel(): void { + if (this.isCancelled) return; + this.isCancelled = true; + for (const handler of this.cancelHandlers) handler(); + this.cancelHandlers = []; + } + + then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, + ): Promise { + return this.inner.then(onFulfilled ?? undefined, onRejected ?? undefined); + } + + catch( + onRejected?: ((reason: unknown) => TResult | PromiseLike) | null, + ): Promise { + return this.inner.catch(onRejected ?? undefined); + } + + finally(onFinally?: (() => void) | null): Promise { + return this.inner.finally(onFinally ?? undefined); + } +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/client-config.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/client-config.ts.j2 new file mode 100644 index 000000000..e54ab5480 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/client-config.ts.j2 @@ -0,0 +1,15 @@ +/* Minimal client runtime config holder */ +export type BaseURL = string; + +export interface ClientConfig { + // Prefer idiomatic camelCase going forward + baseUrl: BaseURL; + credentials?: 'include' | 'omit' | 'same-origin'; + token?: string | (() => string | Promise); + apiToken?: string; + username?: string; + password?: string; + headers?: Record | (() => Record | Promise>); + encodePath?: (path: string) => string; +} + diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/codecs.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/codecs.ts.j2 new file mode 100644 index 000000000..ab63e9339 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/codecs.ts.j2 @@ -0,0 +1,13 @@ +import { encode as msgpackEncode, decode as msgpackDecode } from '@msgpack/msgpack'; + +export function encodeMsgPack(value: unknown): Uint8Array { + return msgpackEncode(value, { + sortKeys: true, + ignoreUndefined: true, + useBigInt64: true, + }); +} + +export function decodeMsgPack(buffer: Uint8Array): T { + return msgpackDecode(buffer, { useBigInt64: true }) as T; +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/fetch-http-request.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/fetch-http-request.ts.j2 new file mode 100644 index 000000000..8f02dcbc8 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/fetch-http-request.ts.j2 @@ -0,0 +1,8 @@ +import { BaseHttpRequest, type ApiRequestOptions } from './base-http-request'; +import { request } from './request'; + +export class FetchHttpRequest extends BaseHttpRequest { + async request(options: ApiRequestOptions): Promise { + return request(this.config, options); + } +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/model-runtime.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/model-runtime.ts.j2 new file mode 100644 index 000000000..14061fe01 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/model-runtime.ts.j2 @@ -0,0 +1,269 @@ +import { + encodeSignedTransaction as transactEncodeSignedTransaction, + decodeSignedTransaction as transactDecodeSignedTransaction, + type SignedTransaction, +} from '@algorandfoundation/algokit-transact'; +import { encodeMsgPack, decodeMsgPack } from './codecs'; +import { toBase64, fromBase64 } from './serialization'; + +export type BodyFormat = 'json' | 'msgpack'; + +export interface ScalarFieldType { + readonly kind: 'scalar'; + readonly isBytes?: boolean; + readonly isBigint?: boolean; +} + +export interface CodecFieldType { + readonly kind: 'codec'; + readonly codecKey: string; +} + +export interface ModelFieldType { + readonly kind: 'model'; + readonly meta: ModelMetadata | (() => ModelMetadata); +} + +export interface ArrayFieldType { + readonly kind: 'array'; + readonly item: FieldType; +} + +export interface RecordFieldType { + readonly kind: 'record'; + readonly value: FieldType; +} + +export type FieldType = ScalarFieldType | CodecFieldType | ModelFieldType | ArrayFieldType | RecordFieldType; + +export interface FieldMetadata { + readonly name: string; + readonly wireKey: string; + readonly optional: boolean; + readonly nullable: boolean; + readonly type: FieldType; +} + +export type ModelKind = 'object' | 'array' | 'passthrough'; + +export interface ModelMetadata { + readonly name: string; + readonly kind: ModelKind; + readonly fields?: readonly FieldMetadata[]; + readonly arrayItems?: FieldType; + readonly codecKey?: string; + readonly additionalProperties?: FieldType; + readonly passThrough?: FieldType; +} + +export interface TypeCodec { + encode(value: TValue, format: BodyFormat): unknown; + decode(value: unknown, format: BodyFormat): TValue; +} + +const codecRegistry = new Map>(); + +export function registerCodec(key: string, codec: TypeCodec): void { + codecRegistry.set(key, codec as TypeCodec); +} + +export function getCodec(key: string): TypeCodec | undefined { + return codecRegistry.get(key) as TypeCodec | undefined; +} + +export class AlgorandSerializer { + static encode(value: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): Uint8Array | string { + const wire = this.transform(value, meta, { direction: 'encode', format }); + if (format === 'msgpack') { + return wire instanceof Uint8Array ? wire : encodeMsgPack(wire); + } + return typeof wire === 'string' ? wire : JSON.stringify(wire); + } + + static decode(payload: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): T { + let wire: unknown = payload; + if (format === 'msgpack') { + if (payload instanceof Uint8Array) { + wire = decodeMsgPack(payload); + } + } else if (typeof payload === 'string') { + wire = JSON.parse(payload); + } + return this.transform(wire, meta, { direction: 'decode', format }) as T; + } + + private static transform(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + if (value === undefined || value === null) { + return value; + } + + if (meta.codecKey) { + return this.applyCodec(value, meta.codecKey, ctx); + } + + switch (meta.kind) { + case 'object': + return this.transformObject(value, meta, ctx); + case 'array': + return this.transformType(value, { kind: 'array', item: meta.arrayItems ?? { kind: 'scalar' } }, ctx); + case 'passthrough': + default: + return this.transformType(value, meta.passThrough ?? { kind: 'scalar' }, ctx); + } + } + + private static transformObject(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + const fields = meta.fields ?? []; + if (ctx.direction === 'encode') { + const src = value as Record; + const out: Record = {}; + for (const field of fields) { + const fieldValue = src[field.name]; + if (fieldValue === undefined) continue; + const encoded = this.transformType(fieldValue, field.type, ctx); + if (encoded === undefined && fieldValue === undefined) continue; + out[field.wireKey] = encoded; + } + if (meta.additionalProperties) { + for (const [key, val] of Object.entries(src)) { + if (fields.some((f) => f.name === key)) continue; + out[key] = this.transformType(val, meta.additionalProperties, ctx); + } + } + return out; + } + + const src = value as Record; + const out: Record = {}; + const fieldByWire = new Map(fields.map((field) => [field.wireKey, field])); + + for (const [wireKey, wireValue] of Object.entries(src)) { + const field = fieldByWire.get(wireKey); + if (field) { + const decoded = this.transformType(wireValue, field.type, ctx); + out[field.name] = decoded; + continue; + } + if (meta.additionalProperties) { + out[wireKey] = this.transformType(wireValue, meta.additionalProperties, ctx); + continue; + } + out[wireKey] = wireValue; + } + + return out; + } + + private static transformType(value: unknown, type: FieldType, ctx: TransformContext): unknown { + if (value === undefined || value === null) return value; + + switch (type.kind) { + case 'scalar': + return this.transformScalar(value, type, ctx); + case 'codec': + return this.applyCodec(value, type.codecKey, ctx); + case 'model': + return this.transform(value, typeof type.meta === 'function' ? type.meta() : type.meta, ctx); + case 'array': + if (!Array.isArray(value)) return value; + return value.map((item) => this.transformType(item, type.item, ctx)); + case 'record': + if (typeof value !== 'object' || value === null) return value; + return Object.fromEntries( + Object.entries(value as Record).map(([k, v]) => [k, this.transformType(v, type.value, ctx)]), + ); + default: + return value; + } + } + + private static transformScalar(value: unknown, meta: ScalarFieldType, ctx: TransformContext): unknown { + if (ctx.direction === 'encode') { + if (meta.isBytes && ctx.format === 'json') { + if (value instanceof Uint8Array) return toBase64(value); + } + if (meta.isBigint && ctx.format === 'json') { + if (typeof value === 'bigint') return value.toString(); + if (typeof value === 'number') return Math.trunc(value).toString(); + if (typeof value === 'string') return value; + } + return value; + } + + if (meta.isBytes && ctx.format === 'json' && typeof value === 'string') { + return fromBase64(value); + } + + if (meta.isBigint) { + if (typeof value === 'string') { + try { + return BigInt(value); + } catch { + return value; + } + } + if (typeof value === 'number' && Number.isInteger(value)) { + return BigInt(value); + } + } + + return value; + } + + private static applyCodec(value: unknown, codecKey: string, ctx: TransformContext): unknown { + const codec = codecRegistry.get(codecKey); + if (!codec) { + throw new Error(`Codec for "${codecKey}" is not registered`); + } + return ctx.direction === 'encode' + ? codec.encode(value, ctx.format) + : codec.decode(value, ctx.format); + } +} + +type TransformDirection = 'encode' | 'decode'; + +interface TransformContext { + readonly direction: TransformDirection; + readonly format: BodyFormat; +} + +const encodeSignedTransactionImpl = (value: unknown): Uint8Array => + transactEncodeSignedTransaction(value as SignedTransaction); +const decodeSignedTransactionImpl = (value: Uint8Array): SignedTransaction => + transactDecodeSignedTransaction(value); + +class SignedTransactionCodec implements TypeCodec { + encode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value; + if (format === 'json') { + if (value instanceof Uint8Array) return toBase64(value); + return toBase64(encodeSignedTransactionImpl(value)); + } + if (value instanceof Uint8Array) { + // Already canonical bytes; decode to structured map so parent encoding keeps map semantics + return decodeMsgPack(value); + } + // Convert signed transaction object into canonical map representation + return decodeMsgPack(encodeSignedTransactionImpl(value)); + } + + decode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value; + if (format === 'json') { + if (typeof value === 'string') return decodeSignedTransactionImpl(fromBase64(value)); + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value); + return value; + } + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value); + // Value is a decoded map; re-encode to bytes before handing to transact decoder + try { + return decodeSignedTransactionImpl(encodeMsgPack(value)); + } catch { + return value; + } + } +} + +registerCodec('SignedTransaction', new SignedTransactionCodec()); + diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/request.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/request.ts.j2 new file mode 100644 index 000000000..69db7dd46 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/request.ts.j2 @@ -0,0 +1,123 @@ +import type { ClientConfig } from './client-config'; +import { ApiError } from './api-error'; +import { decodeMsgPack, encodeMsgPack } from './codecs'; +import type { QueryParams, BodyValue } from './base-http-request'; + +const encodeURIPath = (path: string): string => encodeURI(path).replace(/%5B/g, '[').replace(/%5D/g, ']'); + +export async function request(config: ClientConfig, options: { + method: string; + url: string; + path?: Record; + query?: QueryParams; + headers?: Record; + body?: BodyValue; + mediaType?: string; + responseHeader?: string; +}): Promise { + let rawPath = options.url; + if (options.path) { + for (const [key, value] of Object.entries(options.path)) { + const raw = typeof value === 'bigint' ? value.toString() : String(value); + const replace = config.encodePath ? config.encodePath(raw) : encodeURIPath(raw); + rawPath = rawPath.replace(`{${key}}`, replace); + } + } + + const url = new URL(rawPath, config.baseUrl); + + if (options.query) { + for (const [key, value] of Object.entries(options.query)) { + if (value === undefined || value === null) continue; + if (Array.isArray(value)) { + for (const item of value) { + url.searchParams.append(key, item.toString()); + } + } else { + url.searchParams.append(key, value.toString()); + } + } + } + + const headers: Record = { + ...(typeof config.headers === 'function' ? await config.headers() : config.headers ?? {}), + ...(options.headers ?? {}), + }; + + const apiToken = config.apiToken; + if (apiToken) { + {% if client_class_name == 'IndexerClient' %} + headers['X-Indexer-API-Token'] = apiToken; + {% else %} + headers['X-Algo-API-Token'] = apiToken; + {% endif %} + } + + const token = typeof config.token === 'function' ? await config.token() : config.token; + if (token) headers['Authorization'] = `Bearer ${token}`; + if (!token && config.username && config.password) { + headers['Authorization'] = `Basic ${btoa(`${config.username}:${config.password}`)}`; + } + + let body: BodyValue | undefined = undefined; + if (options.body != null) { + if (options.body instanceof Uint8Array || typeof options.body === 'string') { + body = options.body; + } else if (options.mediaType?.includes('msgpack')) { + body = encodeMsgPack(options.body); + } else if (options.mediaType?.includes('json')) { + body = JSON.stringify(options.body); + } else { + body = options.body; + } + } + + const response = await fetch(url.toString(), { + method: options.method, + headers, + body, + credentials: config.credentials, + }); + + if (!response.ok) { + let errorBody: unknown; + try { + const ct = response.headers.get('content-type') ?? ''; + if (ct.includes('application/msgpack')) { + errorBody = decodeMsgPack(new Uint8Array(await response.arrayBuffer())); + } else if (ct.includes('application/json')) { + errorBody = JSON.parse(await response.text()); + } else { + errorBody = await response.text(); + } + } catch { + errorBody = undefined; + } + throw new ApiError(url.toString(), response.status, errorBody); + } + + if (options.responseHeader) { + const value = response.headers.get(options.responseHeader); + return value as unknown as T; + } + + const contentType = response.headers.get('content-type') ?? ''; + + if (contentType.includes('application/msgpack')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T; + } + + if (contentType.includes('application/octet-stream') || contentType.includes('application/x-binary')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T; + } + + if (contentType.includes('application/json')) { + return (await response.text()) as unknown as T; + } + + if (!contentType) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T; + } + + return (await response.text()) as unknown as T; +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/core/serialization.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/core/serialization.ts.j2 new file mode 100644 index 000000000..411a8bb6e --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/core/serialization.ts.j2 @@ -0,0 +1,26 @@ +export function toBase64(bytes: Uint8Array): string { + if (typeof Buffer !== 'undefined') { + return Buffer.from(bytes).toString('base64'); + } + const globalRef: Record = globalThis as unknown as Record; + const btoaFn = globalRef.btoa as ((value: string) => string) | undefined; + if (typeof btoaFn === 'function') { + return btoaFn(String.fromCharCode(...bytes)); + } + throw new Error('Base64 encoding not supported in this environment'); +} + +export function fromBase64(s: string): Uint8Array { + if (typeof Buffer !== 'undefined') { + return new Uint8Array(Buffer.from(s, 'base64')); + } + const globalRef: Record = globalThis as unknown as Record; + const atobFn = globalRef.atob as ((value: string) => string) | undefined; + if (typeof atobFn === 'function') { + const bin = atobFn(s); + const out = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; i += 1) out[i] = bin.charCodeAt(i); + return out; + } + throw new Error('Base64 decoding not supported in this environment'); +} diff --git a/api/oas_generator/ts_oas_generator/templates/base/src/index.ts.j2 b/api/oas_generator/ts_oas_generator/templates/base/src/index.ts.j2 new file mode 100644 index 000000000..6eb10e708 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/base/src/index.ts.j2 @@ -0,0 +1,12 @@ +export * from './core/client-config'; +export * from './core/base-http-request'; +export * from './core/fetch-http-request'; +export * from './core/api-error'; +export * from './core/serialization'; +export * from './core/codecs'; +export * from './core/model-runtime'; + +// Generated +export * from './models'; +export * from './apis'; +export * from './client'; diff --git a/api/oas_generator/ts_oas_generator/templates/client.ts.j2 b/api/oas_generator/ts_oas_generator/templates/client.ts.j2 new file mode 100644 index 000000000..85b877c7f --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/client.ts.j2 @@ -0,0 +1,10 @@ +import type { ClientConfig } from './core/client-config'; +import type { BaseHttpRequest } from './core/base-http-request'; +import { FetchHttpRequest } from './core/fetch-http-request'; +import { {{ service_class_name }} } from './apis/api.service'; + +export class {{ client_class_name }} extends {{ service_class_name }} { + constructor(config: ClientConfig, request?: BaseHttpRequest) { + super(request ?? new FetchHttpRequest(config)); + } +} diff --git a/api/oas_generator/ts_oas_generator/templates/models/index.ts.j2 b/api/oas_generator/ts_oas_generator/templates/models/index.ts.j2 new file mode 100644 index 000000000..582202076 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/models/index.ts.j2 @@ -0,0 +1,4 @@ +{% for name, _ in schemas.items() %} +export type { {{ name | ts_pascal_case }} } from './{{ name | ts_kebab_case }}'; +export { {{ name | ts_pascal_case }}Meta } from './{{ name | ts_kebab_case }}'; +{% endfor %} diff --git a/api/oas_generator/ts_oas_generator/templates/models/model.ts.j2 b/api/oas_generator/ts_oas_generator/templates/models/model.ts.j2 new file mode 100644 index 000000000..fc309e640 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/templates/models/model.ts.j2 @@ -0,0 +1,84 @@ +{% set modelName = schema_name | ts_pascal_case %} +{% set descriptor = descriptor %} +{% set isObject = descriptor.is_object %} +{% set isArray = descriptor.is_array %} +{% set refTypes = schema | collect_schema_refs(schema_name) %} +{% set uses_signed_txn = schema | schema_uses_signed_txn %} +{% set schemaSignedTxn = schema.get('x-algokit-signed-txn') is true %} +{% set schemaBytes = schema.get('format') == 'byte' or schema.get('x-algokit-bytes-base64') is true %} +{% set schemaBigint = schema.get('x-algokit-bigint') is true %} + +{% macro scalar_meta(is_bytes, is_bigint) -%} +{ kind: 'scalar'{% if is_bytes %}, isBytes: true{% endif %}{% if is_bigint %}, isBigint: true{% endif %} } +{%- endmacro %} + +{% macro base_type(ref_model, is_signed_txn, is_bytes, is_bigint) -%} +{%- if is_signed_txn -%} +{ kind: 'codec', codecKey: 'SignedTransaction' } +{%- elif ref_model -%} +{ kind: 'model', meta: () => {{ ref_model }}Meta } +{%- else -%} +{{ scalar_meta(is_bytes, is_bigint) }} +{%- endif -%} +{%- endmacro %} + +{% macro field_type(field) -%} +{%- if field.is_array -%} +{ kind: 'array', item: {{ base_type(field.ref_model, field.is_signed_txn, field.is_bytes, field.is_bigint) }} } +{%- else -%} +{{ base_type(field.ref_model, field.is_signed_txn, field.is_bytes, field.is_bigint) }} +{%- endif -%} +{%- endmacro %} + +{% macro array_item_meta(descriptor) -%} +{{ base_type(descriptor.array_item_ref, descriptor.array_item_is_signed_txn, descriptor.array_item_is_bytes, descriptor.array_item_is_bigint) }} +{%- endmacro %} + +import type { ModelMetadata } from '../core/model-runtime'; +{% if uses_signed_txn %} +import type { SignedTransaction } from '@algorandfoundation/algokit-transact'; +{% endif %} +{% for r in refTypes %} +import type { {{ r }} } from './{{ r | ts_kebab_case }}'; +import { {{ r }}Meta } from './{{ r | ts_kebab_case }}'; +{% endfor %} + +{{ schema.description | ts_doc_comment }} +{% if isObject and schema.get('allOf') is not defined and schema.get('oneOf') is not defined and schema.get('anyOf') is not defined %} +export interface {{ modelName }} { +{% for f in descriptor.fields %} + {{ f.name }}{{ '' if not f.is_optional else '?' }}: {{ f.ts_type }}; +{% endfor %} +} +{% else %} +export type {{ modelName }} = {{ schema | ts_type(schemas) }}; +{% endif %} + +export const {{ modelName }}Meta: ModelMetadata = { + name: '{{ modelName }}', + kind: {% if isObject %}'object'{% elif isArray %}'array'{% else %}'passthrough'{% endif %}, +{% if isObject %} + fields: [ +{% for f in descriptor.fields %} + { + name: '{{ f.name }}', + wireKey: '{{ f.wire_name }}', + optional: {{ 'true' if f.is_optional else 'false' }}, + nullable: {{ 'true' if f.is_nullable else 'false' }}, + type: {{ field_type(f) }}, + }, +{% endfor %} + ], +{% if has_additional_properties %} + additionalProperties: {{ scalar_meta(false, false) }}, +{% endif %} +{% elif isArray %} + arrayItems: {{ array_item_meta(descriptor) }}, +{% else %} +{% if schemaSignedTxn %} + codecKey: 'SignedTransaction', +{% else %} + passThrough: {{ scalar_meta(schemaBytes, schemaBigint) }}, +{% endif %} +{% endif %} +}; diff --git a/api/oas_generator/ts_oas_generator/utils/__init__.py b/api/oas_generator/ts_oas_generator/utils/__init__.py new file mode 100644 index 000000000..7d2b830b9 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/utils/__init__.py @@ -0,0 +1,5 @@ +"""Utility helpers exposed by ``ts_oas_generator.utils``.""" + +from ts_oas_generator.utils.file_utils import write_files_to_disk + +__all__ = ["write_files_to_disk"] diff --git a/api/oas_generator/ts_oas_generator/utils/file_utils.py b/api/oas_generator/ts_oas_generator/utils/file_utils.py new file mode 100644 index 000000000..005edf917 --- /dev/null +++ b/api/oas_generator/ts_oas_generator/utils/file_utils.py @@ -0,0 +1,18 @@ +"""File utilities for the TS OAS generator. + +Provides safe write operations similar to rust_oas_generator.utils.file_utils. +""" + +from __future__ import annotations + +from pathlib import Path + + +def write_files_to_disk(files: dict[Path, str]) -> None: + """Write generated files to disk, creating parent directories. + + Existing files are overwritten. Parent directories are created as needed. + """ + for path, content in files.items(): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") diff --git a/api/package-lock.json b/api/package-lock.json new file mode 100644 index 000000000..5593ee604 --- /dev/null +++ b/api/package-lock.json @@ -0,0 +1,775 @@ +{ + "name": "api", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api", + "devDependencies": { + "@apidevtools/swagger-parser": "^11.0.0", + "@types/node": "^20.10.0", + "prettier": "^3.5.3", + "tsx": "^4.19.2" + }, + "peerDependencies": { + "typescript": "^5" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-13.0.2.tgz", + "integrity": "sha512-ThpknSFmb1zJXU16ba8cFbDRL3WRs6WETW323gOhj7Gwdj9GUqNpA5JFhdAINxINyAz03gqgF5Y4UydAjE3Pdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-11.0.1.tgz", + "integrity": "sha512-0OzWjKPUr7dvXOgQi6hsNLpwgQRtPgyQoYMuaIB+Zj50Qjbwxph/nu4BndwOA446FtQUTwkR3BxLnORpVYLHYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "13.0.2", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz", + "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "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==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "license": "Apache-2.0", + "peer": true, + "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==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/api/package.json b/api/package.json index 49821e969..058b32583 100644 --- a/api/package.json +++ b/api/package.json @@ -3,11 +3,15 @@ "module": "index.ts", "type": "module", "private": true, + "scripts": { + "convert-openapi": "tsx scripts/convert-openapi.ts", + "gen:ts": "cargo api generate-ts-all" + }, "devDependencies": { "@apidevtools/swagger-parser": "^11.0.0", - "@types/bun": "latest", "@types/node": "^20.10.0", - "prettier": "^3.5.3" + "prettier": "^3.5.3", + "tsx": "^4.19.2" }, "peerDependencies": { "typescript": "^5" diff --git a/api/scripts/convert-openapi.ts b/api/scripts/convert-openapi.ts index 5a5c79740..50a76c36d 100644 --- a/api/scripts/convert-openapi.ts +++ b/api/scripts/convert-openapi.ts @@ -1,8 +1,9 @@ -#!/usr/bin/env bun +#!/usr/bin/env node import { writeFileSync, mkdirSync } from "fs"; import { join, dirname } from "path"; import SwaggerParser from "@apidevtools/swagger-parser"; +import { fileURLToPath } from "node:url"; // ===== TYPES ===== @@ -40,6 +41,11 @@ interface FieldTransform { addItems?: Record; // properties to add to the target property, e.g., {"x-custom": true} } +interface MsgpackOnlyEndpoint { + path: string; // Exact path to match (e.g., "/v2/blocks/{round}") + methods?: string[]; // HTTP methods to apply to (default: ["get"]) +} + interface ProcessingConfig { sourceUrl: string; outputPath: string; @@ -48,6 +54,7 @@ interface ProcessingConfig { vendorExtensionTransforms?: VendorExtensionTransform[]; requiredFieldTransforms?: RequiredFieldTransform[]; fieldTransforms?: FieldTransform[]; + msgpackOnlyEndpoints?: MsgpackOnlyEndpoint[]; } // ===== TRANSFORMATIONS ===== @@ -295,6 +302,9 @@ function fixBigInt(spec: OpenAPISpec): number { { fieldName: "last-round" }, { fieldName: "confirmed-round" }, { fieldName: "asset-id" }, + { fieldName: "created-application-index" }, + { fieldName: "created-asset-index" }, + { fieldName: "txn-index" }, { fieldName: "application-index" }, { fieldName: "asset-index" }, { fieldName: "current_round" }, @@ -467,6 +477,113 @@ function transformRequiredFields(spec: OpenAPISpec, requiredFieldTransforms: Req return transformedCount; } +/** + * Enforce msgpack-only format for specific endpoints by removing JSON support + * + * This function modifies endpoints to only support msgpack format, aligning with + * Go and JavaScript SDK implementations that hardcode these endpoints to msgpack. + */ +function enforceMsgpackOnlyEndpoints(spec: OpenAPISpec, endpoints: MsgpackOnlyEndpoint[]): number { + let modifiedCount = 0; + + if (!spec.paths || !endpoints?.length) { + return modifiedCount; + } + + for (const endpoint of endpoints) { + const pathObj = spec.paths[endpoint.path]; + if (!pathObj) { + console.warn(`⚠️ Path ${endpoint.path} not found in spec`); + continue; + } + + const methods = endpoint.methods || ["get"]; + + for (const method of methods) { + const operation = pathObj[method]; + if (!operation) { + continue; + } + + // Look for format parameter in query parameters + if (operation.parameters && Array.isArray(operation.parameters)) { + for (const param of operation.parameters) { + // Handle both inline parameters and $ref parameters + const paramObj = param.$ref ? resolveRef(spec, param.$ref) : param; + + if (paramObj && paramObj.name === "format" && paramObj.in === "query") { + // OpenAPI 3.0 has schema property containing the type information + const schemaObj = paramObj.schema || paramObj; + + // Check if it has an enum with both json and msgpack + if (schemaObj.enum && Array.isArray(schemaObj.enum)) { + if (schemaObj.enum.includes("json") && schemaObj.enum.includes("msgpack")) { + // Remove json from enum, keep only msgpack + schemaObj.enum = ["msgpack"]; + // Update default if it was json + if (schemaObj.default === "json") { + schemaObj.default = "msgpack"; + } + // Don't modify the description - preserve original documentation + modifiedCount++; + console.log(`ℹ️ Enforced msgpack-only for ${endpoint.path} (${method}) parameter`); + } + } else if (schemaObj.type === "string" && !schemaObj.enum) { + // If no enum is specified, add one with only msgpack + schemaObj.enum = ["msgpack"]; + schemaObj.default = "msgpack"; + // Don't modify the description - preserve original documentation + modifiedCount++; + console.log(`ℹ️ Enforced msgpack-only for ${endpoint.path} (${method}) parameter`); + } + } + } + } + + // Also check for format in response content types + if (operation.responses) { + for (const [statusCode, response] of Object.entries(operation.responses)) { + if (response && typeof response === "object") { + const responseObj = response as any; + + // If response has content with both json and msgpack, remove json + if (responseObj.content) { + if (responseObj.content["application/json"] && responseObj.content["application/msgpack"]) { + delete responseObj.content["application/json"]; + modifiedCount++; + console.log(`ℹ️ Removed JSON response content-type for ${endpoint.path} (${method}) - ${statusCode}`); + } + } + } + } + } + } + } + + return modifiedCount; +} + +/** + * Helper function to resolve $ref references in the spec + */ +function resolveRef(spec: OpenAPISpec, ref: string): any { + if (!ref.startsWith("#/")) { + return null; + } + + const parts = ref.substring(2).split("/"); + let current: any = spec; + + for (const part of parts) { + current = current?.[part]; + if (!current) { + return null; + } + } + + return current; +} + // ===== MAIN PROCESSOR ===== class OpenAPIProcessor { @@ -600,12 +717,12 @@ class OpenAPIProcessor { console.log(`ℹ️ Transformed ${transformedFieldsCount} required field states`); } - // 7. Transform properties if configured - let transformedPropertiesCount = 0; - if (this.config.fieldTransforms && this.config.fieldTransforms.length > 0) { - transformedPropertiesCount = transformProperties(spec, this.config.fieldTransforms); - console.log(`ℹ️ Applied ${transformedPropertiesCount} property transformations (additions/removals)`); - } + // 7. Transform properties if configured + let transformedPropertiesCount = 0; + if (this.config.fieldTransforms && this.config.fieldTransforms.length > 0) { + transformedPropertiesCount = transformProperties(spec, this.config.fieldTransforms); + console.log(`ℹ️ Applied ${transformedPropertiesCount} property transformations (additions/removals)`); + } // 8. Transform vendor extensions if configured if (this.config.vendorExtensionTransforms && this.config.vendorExtensionTransforms.length > 0) { @@ -622,6 +739,12 @@ class OpenAPIProcessor { } } + // 9. Enforce msgpack-only endpoints if configured + if (this.config.msgpackOnlyEndpoints && this.config.msgpackOnlyEndpoints.length > 0) { + const msgpackCount = enforceMsgpackOnlyEndpoints(spec, this.config.msgpackOnlyEndpoints); + console.log(`ℹ️ Enforced msgpack-only format for ${msgpackCount} endpoint parameters/responses`); + } + // Save the processed spec await SwaggerParser.validate(JSON.parse(JSON.stringify(spec))); console.log("✅ Specification is valid"); @@ -768,6 +891,16 @@ async function processAlgodSpec() { removeSource: true, }, ], + msgpackOnlyEndpoints: [ + // Align with Go and JS SDKs that hardcode these to msgpack + { path: "/v2/blocks/{round}", methods: ["get"] }, + { path: "/v2/transactions/pending", methods: ["get"] }, + { path: "/v2/transactions/pending/{txid}", methods: ["get"] }, + { path: "/v2/accounts/{address}/transactions/pending", methods: ["get"] }, + { path: "/v2/deltas/{round}", methods: ["get"] }, + { path: "/v2/deltas/txn/group/{id}", methods: ["get"] }, + { path: "/v2/deltas/{round}/txn/group", methods: ["get"] }, + ], }; await processAlgorandSpec(config); @@ -871,6 +1004,7 @@ async function main() { } // Run if this is the main module -if (import.meta.main) { - main(); +const isMain = process.argv[1] && process.argv[1] === fileURLToPath(import.meta.url); +if (isMain) { + void main(); } diff --git a/api/specs/algod.oas3.json b/api/specs/algod.oas3.json index c3350b35d..5d0a2c6b7 100644 --- a/api/specs/algod.oas3.json +++ b/api/specs/algod.oas3.json @@ -801,7 +801,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -811,31 +810,6 @@ "200": { "description": "A potentially truncated list of transactions currently in the node's transaction pool. You can compute whether or not the list is truncated if the number of elements in the **top-transactions** array is fewer than **total-transactions**.", "content": { - "application/json": { - "schema": { - "required": [ - "top-transactions", - "total-transactions" - ], - "type": "object", - "properties": { - "top-transactions": { - "type": "array", - "description": "An array of signed transaction objects.", - "items": { - "type": "object", - "properties": {}, - "x-algokit-signed-txn": true - } - }, - "total-transactions": { - "type": "integer", - "description": "Total number of transactions in the pool." - } - }, - "description": "PendingTransactions is an array of signed transactions exactly as they were submitted." - } - }, "application/msgpack": { "schema": { "required": [ @@ -866,11 +840,6 @@ "400": { "description": "Max must be a non-negative integer", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -881,11 +850,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -896,11 +860,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -911,11 +870,6 @@ "503": { "description": "Service Temporarily Unavailable", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -966,7 +920,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -976,28 +929,6 @@ "200": { "description": "Encoded block object.", "content": { - "application/json": { - "schema": { - "required": [ - "block" - ], - "type": "object", - "properties": { - "block": { - "type": "object", - "properties": {}, - "description": "Block header data.", - "x-algorand-format": "BlockHeader" - }, - "cert": { - "type": "object", - "properties": {}, - "description": "Optional certificate object. This is only included when the format is set to message pack.", - "x-algorand-format": "BlockCertificate" - } - } - } - }, "application/msgpack": { "schema": { "required": [ @@ -1025,11 +956,6 @@ "400": { "description": "Bad Request - Non integer number", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -1040,11 +966,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -1055,11 +976,6 @@ "404": { "description": "None existing block ", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -1070,11 +986,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -2964,7 +2875,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -2974,31 +2884,6 @@ "200": { "description": "A potentially truncated list of transactions currently in the node's transaction pool. You can compute whether or not the list is truncated if the number of elements in the **top-transactions** array is fewer than **total-transactions**.", "content": { - "application/json": { - "schema": { - "required": [ - "top-transactions", - "total-transactions" - ], - "type": "object", - "properties": { - "top-transactions": { - "type": "array", - "description": "An array of signed transaction objects.", - "items": { - "type": "object", - "properties": {}, - "x-algokit-signed-txn": true - } - }, - "total-transactions": { - "type": "integer", - "description": "Total number of transactions in the pool." - } - }, - "description": "PendingTransactions is an array of signed transactions exactly as they were submitted." - } - }, "application/msgpack": { "schema": { "required": [ @@ -3029,11 +2914,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3044,11 +2924,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3059,11 +2934,6 @@ "503": { "description": "Service Temporarily Unavailable", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3105,7 +2975,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -3115,11 +2984,6 @@ "200": { "description": "Given a transaction ID of a recently submitted transaction, it returns information about it. There are several cases when this might succeed:\n- transaction committed (committed round > 0)\n- transaction still in the pool (committed round = 0, pool error = \"\")\n- transaction removed from pool due to error (committed round = 0, pool error != \"\")\n\nOr the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error.", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PendingTransactionResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/PendingTransactionResponse" @@ -3130,11 +2994,6 @@ "400": { "description": "Bad Request", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3145,11 +3004,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3160,11 +3014,6 @@ "404": { "description": "Transaction Not Found", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3208,7 +3057,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -3218,11 +3066,6 @@ "200": { "description": "Contains ledger deltas", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LedgerStateDelta" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/LedgerStateDelta" @@ -3233,11 +3076,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3248,11 +3086,6 @@ "404": { "description": "Could not find a delta for round", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3263,11 +3096,6 @@ "408": { "description": "timed out on request", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3278,11 +3106,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3293,11 +3116,6 @@ "503": { "description": "Service Temporarily Unavailable", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3341,7 +3159,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -3351,22 +3168,6 @@ "200": { "description": "Response containing all ledger state deltas for transaction groups, with their associated Ids, in a single round.", "content": { - "application/json": { - "schema": { - "required": [ - "Deltas" - ], - "type": "object", - "properties": { - "Deltas": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LedgerStateDeltaForTransactionGroup" - } - } - } - } - }, "application/msgpack": { "schema": { "required": [ @@ -3388,11 +3189,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3403,11 +3199,6 @@ "404": { "description": "Could not find deltas for round", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3418,11 +3209,6 @@ "408": { "description": "timed out on request", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3433,11 +3219,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3448,11 +3229,6 @@ "501": { "description": "Not Implemented", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3494,7 +3270,6 @@ "schema": { "type": "string", "enum": [ - "json", "msgpack" ] } @@ -3504,11 +3279,6 @@ "200": { "description": "Response containing a ledger state delta for a single transaction group.", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LedgerStateDelta" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/LedgerStateDelta" @@ -3519,11 +3289,6 @@ "401": { "description": "Invalid API Token", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3534,11 +3299,6 @@ "404": { "description": "Could not find a delta for transaction ID or group ID", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3549,11 +3309,6 @@ "408": { "description": "timed out on request", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3564,11 +3319,6 @@ "500": { "description": "Internal Error", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -3579,11 +3329,6 @@ "501": { "description": "Not Implemented", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - }, "application/msgpack": { "schema": { "$ref": "#/components/schemas/ErrorResponse" @@ -6016,7 +5761,8 @@ "type": "string" }, "txn-index": { - "type": "integer" + "type": "integer", + "x-algokit-bigint": true }, "app-index": { "type": "integer", diff --git a/api/specs/indexer.oas3.json b/api/specs/indexer.oas3.json index c7ee698de..0b37fe3c7 100644 --- a/api/specs/indexer.oas3.json +++ b/api/specs/indexer.oas3.json @@ -4239,11 +4239,13 @@ }, "created-application-index": { "type": "integer", - "description": "Specifies an application index (ID) if an application was created with this transaction." + "description": "Specifies an application index (ID) if an application was created with this transaction.", + "x-algokit-bigint": true }, "created-asset-index": { "type": "integer", - "description": "Specifies an asset index (ID) if an asset was created with this transaction." + "description": "Specifies an asset index (ID) if an asset was created with this transaction.", + "x-algokit-bigint": true }, "fee": { "type": "integer", diff --git a/crates/indexer_client/src/models/holding_ref.rs b/crates/indexer_client/src/models/holding_ref.rs index 54d6a16f6..269ecd90b 100644 --- a/crates/indexer_client/src/models/holding_ref.rs +++ b/crates/indexer_client/src/models/holding_ref.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; /// HoldingRef names a holding by referring to an Address and Asset it belongs to. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))] pub struct HoldingRef { /// \[d\] Address in access list, or the sender of the transaction. #[serde(rename = "address")] diff --git a/crates/indexer_client/src/models/locals_ref.rs b/crates/indexer_client/src/models/locals_ref.rs index 3789b0bf5..cdee93f79 100644 --- a/crates/indexer_client/src/models/locals_ref.rs +++ b/crates/indexer_client/src/models/locals_ref.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; /// LocalsRef names a local state by referring to an Address and App it belongs to. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))] pub struct LocalsRef { /// \[d\] Address in access list, or the sender of the transaction. #[serde(rename = "address")] diff --git a/crates/indexer_client/src/models/resource_ref.rs b/crates/indexer_client/src/models/resource_ref.rs index bc34da9ea..122ab8955 100644 --- a/crates/indexer_client/src/models/resource_ref.rs +++ b/crates/indexer_client/src/models/resource_ref.rs @@ -16,6 +16,7 @@ use crate::models::LocalsRef; /// ResourceRef names a single resource. Only one of the fields should be set. #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "ffi_uniffi", derive(uniffi::Record))] pub struct ResourceRef { /// \[d\] Account whose balance record is accessible by the executing ApprovalProgram or ClearStateProgram. #[serde(rename = "address", skip_serializing_if = "Option::is_none")] diff --git a/crates/indexer_client/src/models/transaction.rs b/crates/indexer_client/src/models/transaction.rs index 75a0ec55e..eb9bb1aad 100644 --- a/crates/indexer_client/src/models/transaction.rs +++ b/crates/indexer_client/src/models/transaction.rs @@ -79,13 +79,13 @@ pub struct Transaction { rename = "created-application-index", skip_serializing_if = "Option::is_none" )] - pub created_application_index: Option, + pub created_application_index: Option, /// Specifies an asset index (ID) if an asset was created with this transaction. #[serde( rename = "created-asset-index", skip_serializing_if = "Option::is_none" )] - pub created_asset_index: Option, + pub created_asset_index: Option, /// \[fee\] Transaction fee. #[serde(rename = "fee")] pub fee: u64, diff --git a/packages/README.md b/packages/README.md index 838035c5f..c70eddd62 100644 --- a/packages/README.md +++ b/packages/README.md @@ -66,3 +66,28 @@ To install, follow the below: 4. Remove `@algorandfoundation:registry=https://npm.pkg.github.com` from `~/.npmrc`, so NPM hosted `@algorandfoundation` packages can be resolved. Note: You will need to perform steps 2-4 each time your either install a package for the first time or update a GitHub package hosted dependency. Installing from the lock file only requires the token. + +## Per-model DTO codecs (TypeScript clients) + +The generated TypeScript API clients now emit per-model codecs alongside each TypeScript model type. For a given model `Foo` you will get: + +- `type Foo`: domain model with camelCase keys and rich types (e.g. `Uint8Array`, `bigint`) +- `type FooDto`: JSON-serializable shape with wire keys (e.g. `kebab-case`) and JSON-safe primitives +- `Foo.toDto(value: Foo): FooDto` and `Foo.fromDto(dto: FooDto): Foo` +- `Foo.encodeMsgpack(value: Foo): Uint8Array` and `Foo.decodeMsgpack(bytes: Uint8Array): Foo` +- `Foo.encodeJson(value: Foo): unknown` and `Foo.decodeJson(raw: unknown): Foo` + +Behavioral rules: + +- Key rename: camelCase in `Foo` is mapped to wire keys in `FooDto` +- Bytes fields: represented as `Uint8Array` in `Foo`; JSON uses base64 strings; msgpack passes through bytes +- Big integers: represented as `bigint` in `Foo`; JSON encodes as strings and decodes back to `bigint` +- Nested refs/arrays: codecs recurse into child model codecs automatically +- Signed transactions: encoded/decoded using `@algorandfoundation/algokit-transact`; for JSON arrays (e.g. simulate) raw object forms are preserved when already in DTO shape + +Services call these codecs automatically: + +- Request bodies: JSON uses `Model.encodeJson`, msgpack uses `Model.encodeMsgpack` (or passes `Uint8Array` through) +- Responses: JSON uses `Model.decodeJson`, msgpack uses `Model.decodeMsgpack` + +This replaces centralized transformer/rename maps; only small runtime helpers remain (e.g. `json.ts` base64 and `msgpack.ts`). diff --git a/packages/typescript/algod_client/package.json b/packages/typescript/algod_client/package.json index d10b79346..0256e6c5f 100644 --- a/packages/typescript/algod_client/package.json +++ b/packages/typescript/algod_client/package.json @@ -25,7 +25,7 @@ "./package.json": "./package.json" }, "scripts": { - "build": "run-s build:*", + "build": "run-s lint build:*", "build-watch": "rolldown --watch -c", "build:0-clean": "rimraf dist coverage", "build:1-compile": "rolldown -c", @@ -33,14 +33,16 @@ "build:4-copy-readme": "cpy README.md dist", "test": "vitest run --coverage --passWithNoTests", "test:watch": "vitest watch --coverage --passWithNoTests", - "lint": "eslint ./src/", + "lint": "eslint ./src/ --max-warnings=0", "lint:fix": "eslint ./src/ --fix", "check-types": "tsc --noEmit", "audit": "better-npm-audit audit", "format": "prettier --config ../.prettierrc.cjs --ignore-path ../../../.prettierignore --write .", "pre-commit": "run-s check-types audit test" }, - "dependencies": {}, + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, "peerDependencies": {}, "devDependencies": {} -} \ No newline at end of file +} diff --git a/packages/typescript/algod_client/rolldown.config.ts b/packages/typescript/algod_client/rolldown.config.ts index 425c00d3b..7f4fa6b94 100644 --- a/packages/typescript/algod_client/rolldown.config.ts +++ b/packages/typescript/algod_client/rolldown.config.ts @@ -1,4 +1,4 @@ import createConfig from '../rolldown' import pkg from './package.json' with { type: 'json' } -export default createConfig([...Object.keys(pkg.dependencies || {})]) +export default createConfig([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]) diff --git a/packages/typescript/algod_client/src/apis/api.service.ts b/packages/typescript/algod_client/src/apis/api.service.ts index 5e0f34461..d112bfae7 100644 --- a/packages/typescript/algod_client/src/apis/api.service.ts +++ b/packages/typescript/algod_client/src/apis/api.service.ts @@ -1,4 +1,5 @@ -import type { BaseHttpRequest, ApiRequestOptions } from "../core/BaseHttpRequest"; +import type { BaseHttpRequest, ApiRequestOptions } from '../core/base-http-request' +import { AlgorandSerializer } from '../core/model-runtime' import type { AbortCatchup, Account, @@ -41,7 +42,50 @@ import type { TransactionProof, Version, WaitForBlock, -} from "../models/index"; +} from '../models/index' +import { + AbortCatchupMeta, + AccountMeta, + AccountApplicationInformationMeta, + AccountAssetInformationMeta, + AccountAssetsInformationMeta, + AddParticipationKeyMeta, + ApplicationMeta, + AssetMeta, + BoxMeta, + DebugSettingsProfMeta, + DryrunRequestMeta, + GenesisMeta, + GetApplicationBoxesMeta, + GetBlockMeta, + GetBlockHashMeta, + GetBlockLogsMeta, + GetBlockTimeStampOffsetMeta, + GetBlockTxidsMeta, + GetPendingTransactionsMeta, + GetPendingTransactionsByAddressMeta, + GetStatusMeta, + GetSupplyMeta, + GetSyncRoundMeta, + GetTransactionGroupLedgerStateDeltasForRoundMeta, + LedgerStateDeltaMeta, + LightBlockHeaderProofMeta, + ParticipationKeyMeta, + PendingTransactionResponseMeta, + RawTransactionMeta, + ShutdownNodeMeta, + SimulateRequestMeta, + SimulateTransactionMeta, + StartCatchupMeta, + StateProofMeta, + TealCompileMeta, + TealDisassembleMeta, + TealDryrunMeta, + TransactionParamsMeta, + TransactionProofMeta, + VersionMeta, + WaitForBlockMeta, +} from '../models/index' export class AlgodApi { constructor(public readonly httpRequest: BaseHttpRequest) {} @@ -49,876 +93,1158 @@ export class AlgodApi { /** * Given a catchpoint, it aborts catching up to this catchpoint */ - abortCatchup(catchpoint: string, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async abortCatchup(catchpoint: string, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "DELETE", - url: "/v2/catchup/{catchpoint}", + const payload = await this.httpRequest.request({ + method: 'DELETE', + url: '/v2/catchup/{catchpoint}', path: { catchpoint: catchpoint }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AbortCatchupMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as AbortCatchup } /** * Given a specific account public key and application ID, this call returns the account's application local state and global state (AppLocalState and AppParams, if either exists). Global state will only be returned if the provided address is the application's creator. */ - accountApplicationInformation( + async accountApplicationInformation( address: string, applicationId: number | bigint, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'json' | 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/accounts/{address}/applications/{application-id}", - path: { address: address, "application-id": typeof applicationId === "bigint" ? applicationId.toString() : applicationId }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{address}/applications/{application-id}', + path: { address: address, 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AccountApplicationInformationMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as AccountApplicationInformation } /** * Given a specific account public key and asset ID, this call returns the account's asset holding and asset parameters (if either exist). Asset parameters will only be returned if the provided address is the asset's creator. */ - accountAssetInformation( + async accountAssetInformation( address: string, assetId: number | bigint, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'json' | 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/accounts/{address}/assets/{asset-id}", - path: { address: address, "asset-id": typeof assetId === "bigint" ? assetId.toString() : assetId }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{address}/assets/{asset-id}', + path: { address: address, 'asset-id': typeof assetId === 'bigint' ? assetId.toString() : assetId }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AccountAssetInformationMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as AccountAssetInformation } /** * Lookup an account's asset holdings. */ - accountAssetsInformation( + async accountAssetsInformation( address: string, params?: { limit?: number | bigint; next?: string }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/accounts/{address}/assets", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{address}/assets', path: { address: address }, - query: { limit: typeof params?.limit === "bigint" ? (params!.limit as bigint).toString() : params?.limit, next: params?.next }, + query: { limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, next: params?.next }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AccountAssetsInformationMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as AccountAssetsInformation } /** * Given a specific account public key, this call returns the account's status, balance and spendable amounts */ - accountInformation( + async accountInformation( address: string, - params?: { exclude?: "all" | "none"; format?: "json" | "msgpack" }, + params?: { exclude?: 'all' | 'none'; format?: 'json' | 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/accounts/{address}", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{address}', path: { address: address }, query: { exclude: params?.exclude, format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AccountMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Account } - addParticipationKey(params?: { body: string }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/msgpack"; + async addParticipationKey(params?: { body: string }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const bodyMeta = undefined + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined + if (mediaType) headers['Content-Type'] = mediaType + const serializedBody = + bodyMeta && params?.body !== undefined ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) : params?.body - return this.httpRequest.request({ - method: "POST", - url: "/v2/participation", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/participation', path: {}, query: {}, headers, - body: params?.body, - // Only msgpack supported for request body - mediaType: "application/msgpack", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AddParticipationKeyMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as AddParticipationKey } /** * Given a participation ID, append state proof keys to a particular set of participation keys */ - appendKeys(participationId: string, params?: { body: string }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/participation/{participation-id}", - path: { "participation-id": participationId }, + async appendKeys(participationId: string, params?: { body: string }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const bodyMeta = undefined + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined + if (mediaType) headers['Content-Type'] = mediaType + const serializedBody = + bodyMeta && params?.body !== undefined ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) : params?.body + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/participation/{participation-id}', + path: { 'participation-id': participationId }, query: {}, headers, - body: params?.body, - // Only msgpack supported for request body - mediaType: "application/msgpack", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = ParticipationKeyMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as ParticipationKey } /** * Delete a given participation key by ID */ - deleteParticipationKeyById(participationId: string, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "DELETE", - url: "/v2/participation/{participation-id}", - path: { "participation-id": participationId }, + async deleteParticipationKeyById(participationId: string, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'DELETE', + url: '/v2/participation/{participation-id}', + path: { 'participation-id': participationId }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } - experimentalCheck(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async experimentalCheck(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/experimental", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/experimental', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } - generateParticipationKeys( + async generateParticipationKeys( address: string, params?: { dilution?: number | bigint; first: number | bigint; last: number | bigint }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "POST", - url: "/v2/participation/generate/{address}", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/participation/generate/{address}', path: { address: address }, query: { - dilution: typeof params?.dilution === "bigint" ? (params!.dilution as bigint).toString() : params?.dilution, - first: typeof params?.first === "bigint" ? (params!.first as bigint).toString() : params?.first, - last: typeof params?.last === "bigint" ? (params!.last as bigint).toString() : params?.last, + dilution: typeof params?.dilution === 'bigint' ? (params!.dilution as bigint).toString() : params?.dilution, + first: typeof params?.first === 'bigint' ? (params!.first as bigint).toString() : params?.first, + last: typeof params?.last === 'bigint' ? (params!.last as bigint).toString() : params?.last, }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as string } /** * Given an application ID and box name, it returns the round, box name, and value (each base64 encoded). Box names must be in the goal app call arg encoding form 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, use the form 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. */ - getApplicationBoxByName(applicationId: number | bigint, params?: { name: string }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/applications/{application-id}/box", - path: { "application-id": typeof applicationId === "bigint" ? applicationId.toString() : applicationId }, + async getApplicationBoxByName( + applicationId: number | bigint, + params?: { name: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}/box', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, query: { name: params?.name }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = BoxMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Box } /** * Given an application ID, return all Box names. No particular ordering is guaranteed. Request fails when client or server-side configured limits prevent returning all Box names. */ - getApplicationBoxes( + async getApplicationBoxes( applicationId: number | bigint, params?: { max?: number | bigint }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/applications/{application-id}/boxes", - path: { "application-id": typeof applicationId === "bigint" ? applicationId.toString() : applicationId }, - query: { max: typeof params?.max === "bigint" ? (params!.max as bigint).toString() : params?.max }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}/boxes', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, + query: { max: typeof params?.max === 'bigint' ? (params!.max as bigint).toString() : params?.max }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetApplicationBoxesMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetApplicationBoxes } /** * Given a application ID, it returns application information including creator, approval and clear programs, global and local schemas, and global state. */ - getApplicationById(applicationId: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/applications/{application-id}", - path: { "application-id": typeof applicationId === "bigint" ? applicationId.toString() : applicationId }, + async getApplicationById(applicationId: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = ApplicationMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Application } /** * Given a asset ID, it returns asset information including creator, name, total supply and special addresses. */ - getAssetById(assetId: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/assets/{asset-id}", - path: { "asset-id": typeof assetId === "bigint" ? assetId.toString() : assetId }, + async getAssetById(assetId: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/assets/{asset-id}', + path: { 'asset-id': typeof assetId === 'bigint' ? assetId.toString() : assetId }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = AssetMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Asset } - getBlock( + async getBlock( round: number | bigint, - params?: { headerOnly?: boolean; format?: "json" | "msgpack" }, + params?: { headerOnly?: boolean; format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}", - path: { round: typeof round === "bigint" ? round.toString() : round }, - query: { "header-only": params?.headerOnly, format: params?.format }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}', + path: { round: typeof round === 'bigint' ? round.toString() : round }, + query: { 'header-only': params?.headerOnly, format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetBlockMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetBlock } - getBlockHash(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getBlockHash(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}/hash", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}/hash', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetBlockHashMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetBlockHash } /** * Get all of the logs from outer and inner app calls in the given round */ - getBlockLogs(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}/logs", - path: { round: typeof round === "bigint" ? round.toString() : round }, + async getBlockLogs(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}/logs', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetBlockLogsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetBlockLogs } /** * Gets the current timestamp offset. */ - getBlockTimeStampOffset(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getBlockTimeStampOffset(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/devmode/blocks/offset", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/devmode/blocks/offset', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetBlockTimeStampOffsetMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetBlockTimeStampOffset } - getBlockTxids(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getBlockTxids(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}/txids", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}/txids', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetBlockTxidsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetBlockTxids } /** * Returns the merged (defaults + overrides) config file in json. */ - getConfig(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getConfig(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/debug/settings/config", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/debug/settings/config', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as string } /** * Retrieves the current settings for blocking and mutex profiles */ - getDebugSettingsProf(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getDebugSettingsProf(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/debug/settings/pprof", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/debug/settings/pprof', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = DebugSettingsProfMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as DebugSettingsProf } /** * Returns the entire genesis file in json. */ - getGenesis(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getGenesis(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/genesis", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/genesis', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GenesisMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Genesis } /** * Get ledger deltas for a round. */ - getLedgerStateDelta( + async getLedgerStateDelta( round: number | bigint, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/deltas/{round}", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/deltas/{round}', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = LedgerStateDeltaMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LedgerStateDelta } /** * Get a ledger delta for a given transaction group. */ - getLedgerStateDeltaForTransactionGroup( + async getLedgerStateDeltaForTransactionGroup( id: string, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/deltas/txn/group/{id}", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/deltas/txn/group/{id}', path: { id: id }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = LedgerStateDeltaMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LedgerStateDelta } - getLightBlockHeaderProof(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getLightBlockHeaderProof(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}/lightheader/proof", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}/lightheader/proof', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = LightBlockHeaderProofMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LightBlockHeaderProof } /** * Given a participation ID, return information about that participation key */ - getParticipationKeyById(participationId: string, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/participation/{participation-id}", - path: { "participation-id": participationId }, + async getParticipationKeyById(participationId: string, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/participation/{participation-id}', + path: { 'participation-id': participationId }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = ParticipationKeyMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as ParticipationKey } /** * Return a list of participation keys */ - getParticipationKeys(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getParticipationKeys(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/participation", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/participation', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = { name: 'ParticipationKey[]', kind: 'array', arrayItems: { kind: 'model', meta: () => ParticipationKeyMeta } } + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as ParticipationKey[] } /** * Get the list of pending transactions, sorted by priority, in decreasing order, truncated at the end at MAX. If MAX = 0, returns all pending transactions. */ - getPendingTransactions( - params?: { max?: number | bigint; format?: "json" | "msgpack" }, + async getPendingTransactions( + params?: { max?: number | bigint; format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/transactions/pending", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/transactions/pending', path: {}, - query: { max: typeof params?.max === "bigint" ? (params!.max as bigint).toString() : params?.max, format: params?.format }, + query: { max: typeof params?.max === 'bigint' ? (params!.max as bigint).toString() : params?.max, format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetPendingTransactionsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetPendingTransactions } /** * Get the list of pending transactions by address, sorted by priority, in decreasing order, truncated at the end at MAX. If MAX = 0, returns all pending transactions. */ - getPendingTransactionsByAddress( + async getPendingTransactionsByAddress( address: string, - params?: { max?: number | bigint; format?: "json" | "msgpack" }, + params?: { max?: number | bigint; format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/accounts/{address}/transactions/pending", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{address}/transactions/pending', path: { address: address }, - query: { max: typeof params?.max === "bigint" ? (params!.max as bigint).toString() : params?.max, format: params?.format }, + query: { max: typeof params?.max === 'bigint' ? (params!.max as bigint).toString() : params?.max, format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetPendingTransactionsByAddressMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetPendingTransactionsByAddress } - getReady(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getReady(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/ready", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/ready', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } - getStateProof(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getStateProof(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/stateproofs/{round}", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/stateproofs/{round}', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = StateProofMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as StateProof } - getStatus(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getStatus(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/status", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/status', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetStatusMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetStatus } - getSupply(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getSupply(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/ledger/supply", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/ledger/supply', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetSupplyMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetSupply } /** * Gets the minimum sync round for the ledger. */ - getSyncRound(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getSyncRound(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/ledger/sync", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/ledger/sync', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetSyncRoundMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetSyncRound } /** * Get ledger deltas for transaction groups in a given round. */ - getTransactionGroupLedgerStateDeltasForRound( + async getTransactionGroupLedgerStateDeltasForRound( round: number | bigint, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/deltas/{round}/txn/group", - path: { round: typeof round === "bigint" ? round.toString() : round }, + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/deltas/{round}/txn/group', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = GetTransactionGroupLedgerStateDeltasForRoundMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as GetTransactionGroupLedgerStateDeltasForRound } - getTransactionProof( + async getTransactionProof( round: number | bigint, txid: string, - params?: { hashtype?: "sha512_256" | "sha256"; format?: "json" | "msgpack" }, + params?: { hashtype?: 'sha512_256' | 'sha256'; format?: 'json' | 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/blocks/{round}/transactions/{txid}/proof", - path: { round: typeof round === "bigint" ? round.toString() : round, txid: txid }, + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round}/transactions/{txid}/proof', + path: { round: typeof round === 'bigint' ? round.toString() : round, txid: txid }, query: { hashtype: params?.hashtype, format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = TransactionProofMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as TransactionProof } /** * Retrieves the supported API versions, binary build versions, and genesis information. */ - getVersion(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async getVersion(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/versions", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/versions', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = VersionMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Version } - healthCheck(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async healthCheck(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/health", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/health', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } - metrics(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async metrics(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/metrics", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/metrics', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } /** @@ -928,347 +1254,471 @@ export class AlgodApi { * - transaction removed from pool due to error (committed round = 0, pool error != "") * Or the transaction may have happened sufficiently long ago that the node no longer remembers it, and this will return an error. */ - pendingTransactionInformation( + async pendingTransactionInformation( txid: string, - params?: { format?: "json" | "msgpack" }, + params?: { format?: 'msgpack' }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/transactions/pending/{txid}", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/transactions/pending/{txid}', path: { txid: txid }, query: { format: params?.format }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = PendingTransactionResponseMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as PendingTransactionResponse } /** * Enables blocking and mutex profiles, and returns the old settings */ - putDebugSettingsProf(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async putDebugSettingsProf(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "PUT", - url: "/debug/settings/pprof", + const payload = await this.httpRequest.request({ + method: 'PUT', + url: '/debug/settings/pprof', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = DebugSettingsProfMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as DebugSettingsProf } - rawTransaction(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/x-binary"; + async rawTransaction(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = params?.body ?? undefined + const mediaType = 'application/msgpack' + headers['Content-Type'] = mediaType - return this.httpRequest.request({ - method: "POST", - url: "/v2/transactions", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/transactions', path: {}, query: {}, headers, - body: params?.body, - mediaType: "application/x-binary", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = RawTransactionMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as RawTransaction } - rawTransactionAsync(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/x-binary"; + async rawTransactionAsync(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = params?.body ?? undefined + const mediaType = 'application/msgpack' + headers['Content-Type'] = mediaType - return this.httpRequest.request({ - method: "POST", - url: "/v2/transactions/async", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/transactions/async', path: {}, query: {}, headers, - body: params?.body, - mediaType: "application/x-binary", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } /** * Sets the timestamp offset (seconds) for blocks in dev mode. Providing an offset of 0 will unset this value and try to use the real clock for the timestamp. */ - setBlockTimeStampOffset(offset: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/devmode/blocks/offset/{offset}", - path: { offset: typeof offset === "bigint" ? offset.toString() : offset }, + async setBlockTimeStampOffset(offset: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/devmode/blocks/offset/{offset}', + path: { offset: typeof offset === 'bigint' ? offset.toString() : offset }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } /** * Sets the minimum sync round on the ledger. */ - setSyncRound(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/ledger/sync/{round}", - path: { round: typeof round === "bigint" ? round.toString() : round }, + async setSyncRound(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/ledger/sync/{round}', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } /** * Special management endpoint to shutdown the node. Optionally provide a timeout parameter to indicate that the node should begin shutting down after a number of seconds. */ - shutdownNode(params?: { timeout?: number | bigint }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async shutdownNode(params?: { timeout?: number | bigint }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "POST", - url: "/v2/shutdown", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/shutdown', path: {}, - query: { timeout: typeof params?.timeout === "bigint" ? (params!.timeout as bigint).toString() : params?.timeout }, + query: { timeout: typeof params?.timeout === 'bigint' ? (params!.timeout as bigint).toString() : params?.timeout }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = ShutdownNodeMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as ShutdownNode } - simulateTransaction( - params?: { format?: "json" | "msgpack"; body: SimulateRequest }, + async simulateTransaction( + params?: { format?: 'json' | 'msgpack'; body: SimulateRequest }, requestOptions?: ApiRequestOptions, ): Promise { - const headers: Record = {}; - // Content negotiation (aligned with Rust behavior): - // - Default to msgpack when available (better performance, smaller payload) - // - Only use JSON if explicitly requested via format=json - const useJson = params?.format === "json"; - headers["Accept"] = useJson ? "application/json" : "application/msgpack"; - headers["Content-Type"] = params?.format === "json" ? "application/json" : "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/transactions/simulate", + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = (params?.format as 'json' | 'msgpack' | undefined) ?? 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const bodyMeta = SimulateRequestMeta + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined + if (mediaType) headers['Content-Type'] = mediaType + const serializedBody = + bodyMeta && params?.body !== undefined ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) : params?.body + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/transactions/simulate', path: {}, query: { format: params?.format }, headers, - body: params?.body, - // Dynamic mediaType based on format parameter (prefer msgpack by default) - mediaType: params?.format === "json" ? "application/json" : "application/msgpack", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = SimulateTransactionMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SimulateTransaction } /** * Given a catchpoint, it starts catching up to this catchpoint */ - startCatchup(catchpoint: string, params?: { min?: number | bigint }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async startCatchup(catchpoint: string, params?: { min?: number | bigint }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "POST", - url: "/v2/catchup/{catchpoint}", + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/catchup/{catchpoint}', path: { catchpoint: catchpoint }, - query: { min: typeof params?.min === "bigint" ? (params!.min as bigint).toString() : params?.min }, + query: { min: typeof params?.min === 'bigint' ? (params!.min as bigint).toString() : params?.min }, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = StartCatchupMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as StartCatchup } /** * Returns the entire swagger spec in json. */ - swaggerJson(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async swaggerJson(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/swagger.json", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/swagger.json', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as string } /** * Given TEAL source code in plain text, return base64 encoded program bytes and base32 SHA512_256 hash of program bytes (Address style). This endpoint is only enabled when a node's configuration file sets EnableDeveloperAPI to true. */ - tealCompile(params?: { sourcemap?: boolean; body: string }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "text/plain"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/teal/compile", + async tealCompile(params?: { sourcemap?: boolean; body: string }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const bodyMeta = undefined + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined + if (mediaType) headers['Content-Type'] = mediaType + const serializedBody = + bodyMeta && params?.body !== undefined ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) : params?.body + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/teal/compile', path: {}, query: { sourcemap: params?.sourcemap }, headers, - body: params?.body, - mediaType: "text/plain", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = TealCompileMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as TealCompile } /** * Given the program bytes, return the TEAL source code in plain text. This endpoint is only enabled when a node's configuration file sets EnableDeveloperAPI to true. */ - tealDisassemble(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/x-binary"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/teal/disassemble", + async tealDisassemble(params?: { body: Uint8Array }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = params?.body ?? undefined + const mediaType = 'application/msgpack' + headers['Content-Type'] = mediaType + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/teal/disassemble', path: {}, query: {}, headers, - body: params?.body, - mediaType: "application/x-binary", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = TealDisassembleMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as TealDisassemble } /** * Executes TEAL program(s) in context and returns debugging information about the execution. This endpoint is only enabled when a node's configuration file sets EnableDeveloperAPI to true. */ - tealDryrun(params?: { body?: DryrunRequest }, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - headers["Content-Type"] = "application/msgpack"; - - // Header parameters - - return this.httpRequest.request({ - method: "POST", - url: "/v2/teal/dryrun", + async tealDryrun(params?: { body?: DryrunRequest }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'msgpack' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const bodyMeta = DryrunRequestMeta + const mediaType = bodyMeta ? (responseFormat === 'json' ? 'application/json' : 'application/msgpack') : undefined + if (mediaType) headers['Content-Type'] = mediaType + const serializedBody = + bodyMeta && params?.body !== undefined ? AlgorandSerializer.encode(params.body, bodyMeta, responseFormat) : params?.body + + const payload = await this.httpRequest.request({ + method: 'POST', + url: '/v2/teal/dryrun', path: {}, query: {}, headers, - body: params?.body, - // Both supported, prefer msgpack for better performance - mediaType: "application/msgpack", + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = TealDryrunMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as TealDryrun } - transactionParams(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async transactionParams(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "GET", - url: "/v2/transactions/params", + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/transactions/params', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = TransactionParamsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as TransactionParams } /** * Unset the ledger sync round. */ - unsetSyncRound(requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; + async unsetSyncRound(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' - // Header parameters + const serializedBody = undefined + const mediaType = undefined - return this.httpRequest.request({ - method: "DELETE", - url: "/v2/ledger/sync", + const payload = await this.httpRequest.request({ + method: 'DELETE', + url: '/v2/ledger/sync', path: {}, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = undefined + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as void } /** * Waits for a block to appear after round {round} and returns the node's status at the time. There is a 1 minute timeout, when reached the current status is returned regardless of whether or not it is the round after the given round. */ - waitForBlock(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { - const headers: Record = {}; - headers["Accept"] = "application/json"; - - // Header parameters - - return this.httpRequest.request({ - method: "GET", - url: "/v2/status/wait-for-block-after/{round}", - path: { round: typeof round === "bigint" ? round.toString() : round }, + async waitForBlock(round: number | bigint, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/status/wait-for-block-after/{round}', + path: { round: typeof round === 'bigint' ? round.toString() : round }, query: {}, headers, - body: undefined, - mediaType: undefined, + body: serializedBody, + mediaType: mediaType, ...(requestOptions ?? {}), - }); + }) + + const responseMeta = WaitForBlockMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as WaitForBlock } } diff --git a/packages/typescript/algod_client/src/apis/index.ts b/packages/typescript/algod_client/src/apis/index.ts index f1d4c2337..a32d22381 100644 --- a/packages/typescript/algod_client/src/apis/index.ts +++ b/packages/typescript/algod_client/src/apis/index.ts @@ -1,2 +1,2 @@ // Barrel file for services -export { AlgodApi } from "./api.service"; +export { AlgodApi } from './api.service' diff --git a/packages/typescript/algod_client/src/client.ts b/packages/typescript/algod_client/src/client.ts index 2175e3a01..7267f3d22 100644 --- a/packages/typescript/algod_client/src/client.ts +++ b/packages/typescript/algod_client/src/client.ts @@ -1,12 +1,10 @@ -import type { ClientConfig } from "./core/ClientConfig"; -import { FetchHttpRequest } from "./core/FetchHttpRequest"; -import { AlgodApi } from "./apis/api.service"; +import type { ClientConfig } from './core/client-config' +import type { BaseHttpRequest } from './core/base-http-request' +import { FetchHttpRequest } from './core/fetch-http-request' +import { AlgodApi } from './apis/api.service' export class AlgodClient extends AlgodApi { - public readonly request: FetchHttpRequest; - - constructor(config: ClientConfig) { - super(new FetchHttpRequest(config)); - this.request = this.httpRequest as FetchHttpRequest; + constructor(config: ClientConfig, request?: BaseHttpRequest) { + super(request ?? new FetchHttpRequest(config)) } } diff --git a/packages/typescript/algod_client/src/core/BaseHttpRequest.ts b/packages/typescript/algod_client/src/core/BaseHttpRequest.ts deleted file mode 100644 index 4a8fbe3e1..000000000 --- a/packages/typescript/algod_client/src/core/BaseHttpRequest.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ClientConfig } from "./ClientConfig"; - -export interface RequestOptions { - method: string; - url: string; - path?: Record; - query?: Record; - headers?: Record; - body?: any; - mediaType?: string; - responseHeader?: string; -} - -// Backwards/ergonomic alias used by generated services -export type ApiRequestOptions = RequestOptions; - -export abstract class BaseHttpRequest { - constructor(public readonly config: ClientConfig) {} - abstract request(options: RequestOptions): Promise; -} diff --git a/packages/typescript/algod_client/src/core/CancelablePromise.ts b/packages/typescript/algod_client/src/core/CancelablePromise.ts deleted file mode 100644 index 3cb118cb6..000000000 --- a/packages/typescript/algod_client/src/core/CancelablePromise.ts +++ /dev/null @@ -1,52 +0,0 @@ -export type OnCancel = (cancelHandler: () => void) => void; - -export class CancelError extends Error { - constructor() { - super("Request aborted"); - this.name = "CancelError"; - } -} - -export class CancelablePromise implements Promise { - [Symbol.toStringTag] = "CancelablePromise"; - - private _isCancelled = false; - private _cancelHandlers: Array<() => void> = []; - - constructor(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void, onCancel: OnCancel) => void) { - const onCancel: OnCancel = (handler) => { - if (this._isCancelled) { - handler(); - } else { - this._cancelHandlers.push(handler); - } - }; - - this._promise = new Promise((resolve, reject) => executor(resolve, reject, onCancel)); - } - - private _promise: Promise; - - public cancel(): void { - if (!this._isCancelled) { - this._isCancelled = true; - for (const fn of this._cancelHandlers) fn(); - this._cancelHandlers.length = 0; - } - } - - then( - onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null, - ): Promise { - return this._promise.then(onfulfilled, onrejected); - } - - catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise { - return this._promise.catch(onrejected); - } - - finally(onfinally?: (() => void) | undefined | null): Promise { - return this._promise.finally(onfinally); - } -} diff --git a/packages/typescript/algod_client/src/core/ClientConfig.ts b/packages/typescript/algod_client/src/core/ClientConfig.ts deleted file mode 100644 index 45cd2cf9f..000000000 --- a/packages/typescript/algod_client/src/core/ClientConfig.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - Minimal client runtime config holder -*/ -export type BaseURL = string; - -export interface ClientConfig { - BASE: BaseURL; - VERSION?: string; - WITH_CREDENTIALS?: boolean; - CREDENTIALS?: "include" | "omit" | "same-origin"; - TOKEN?: string | (() => string | Promise); - USERNAME?: string; - PASSWORD?: string; - HEADERS?: Record | (() => Record | Promise>); - ENCODE_PATH?: (path: string) => string; - INT_DECODING?: "safe" | "unsafe" | "mixed" | "bigint"; -} diff --git a/packages/typescript/algod_client/src/core/FetchHttpRequest.ts b/packages/typescript/algod_client/src/core/FetchHttpRequest.ts deleted file mode 100644 index bc0c4cb9a..000000000 --- a/packages/typescript/algod_client/src/core/FetchHttpRequest.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BaseHttpRequest, type RequestOptions } from "./BaseHttpRequest"; -import { request as defaultRequest } from "./request"; - -export class FetchHttpRequest extends BaseHttpRequest { - async request(options: RequestOptions): Promise { - const request = defaultRequest; - return request(this.config, options); - } -} diff --git a/packages/typescript/algod_client/src/core/api-error.ts b/packages/typescript/algod_client/src/core/api-error.ts new file mode 100644 index 000000000..8293ffb76 --- /dev/null +++ b/packages/typescript/algod_client/src/core/api-error.ts @@ -0,0 +1,12 @@ +export class ApiError extends Error { + public readonly url: string + public readonly status: number + public readonly body: T | undefined + + constructor(url: string, status: number, body?: T) { + super(`Request to ${url} failed with status ${status}`) + this.url = url + this.status = status + this.body = body + } +} diff --git a/packages/typescript/algod_client/src/core/base-http-request.ts b/packages/typescript/algod_client/src/core/base-http-request.ts new file mode 100644 index 000000000..110606ade --- /dev/null +++ b/packages/typescript/algod_client/src/core/base-http-request.ts @@ -0,0 +1,22 @@ +import type { ClientConfig } from './client-config' + +export type QueryValue = string | number | bigint | boolean +export type QueryParams = Record + +export type BodyValue = Uint8Array | Record | unknown[] | string | number | boolean | null + +export interface ApiRequestOptions { + method: string + url: string + path?: Record + query?: QueryParams + headers?: Record + body?: BodyValue + mediaType?: string + responseHeader?: string +} + +export abstract class BaseHttpRequest { + constructor(public readonly config: ClientConfig) {} + abstract request(options: ApiRequestOptions): Promise +} diff --git a/packages/typescript/algod_client/src/core/casing.ts b/packages/typescript/algod_client/src/core/casing.ts deleted file mode 100644 index 7ad409bd5..000000000 --- a/packages/typescript/algod_client/src/core/casing.ts +++ /dev/null @@ -1,52 +0,0 @@ -function toCamel(segmented: string): string { - if (!segmented) return segmented; - // Fast path: if no hyphen, return as-is but ensure typical camel conversion for underscores - if (!segmented.includes("-")) return segmented.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase()); - return segmented - .split("-") - .map((part, index) => (index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1))) - .join(""); -} - -function toKebab(camel: string): string { - if (!camel) return camel; - // Convert camelCase or mixedCase to kebab-case; leave existing hyphens and numbers intact - return camel - .replace(/([a-z0-9])([A-Z])/g, "$1-$2") - .replace(/_/g, "-") - .toLowerCase(); -} - -function isPlainObject(value: unknown): value is Record { - if (value === null || typeof value !== "object") return false; - const proto = Object.getPrototypeOf(value); - return proto === Object.prototype || proto === null; -} - -export function toCamelCaseKeysDeep(value: T): T { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (Array.isArray(value)) return value.map((v) => toCamelCaseKeysDeep(v)) as unknown as T; - if (value instanceof Uint8Array) return value; - if (!isPlainObject(value)) return value; - - const out: Record = {}; - for (const [k, v] of Object.entries(value)) { - const newKey = toCamel(k); - out[newKey] = toCamelCaseKeysDeep(v as unknown); - } - return out as unknown as T; -} - -export function toKebabCaseKeysDeep(value: T): T { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (Array.isArray(value)) return value.map((v) => toKebabCaseKeysDeep(v)) as unknown as T; - if (value instanceof Uint8Array) return value; - if (!isPlainObject(value)) return value; - - const out: Record = {}; - for (const [k, v] of Object.entries(value)) { - const newKey = toKebab(k); - out[newKey] = toKebabCaseKeysDeep(v as unknown); - } - return out as unknown as T; -} diff --git a/packages/typescript/algod_client/src/core/client-config.ts b/packages/typescript/algod_client/src/core/client-config.ts new file mode 100644 index 000000000..9f3a1a5de --- /dev/null +++ b/packages/typescript/algod_client/src/core/client-config.ts @@ -0,0 +1,14 @@ +/* Minimal client runtime config holder */ +export type BaseURL = string + +export interface ClientConfig { + // Prefer idiomatic camelCase going forward + baseUrl: BaseURL + credentials?: 'include' | 'omit' | 'same-origin' + token?: string | (() => string | Promise) + apiToken?: string + username?: string + password?: string + headers?: Record | (() => Record | Promise>) + encodePath?: (path: string) => string +} diff --git a/packages/typescript/algod_client/src/core/codecs.ts b/packages/typescript/algod_client/src/core/codecs.ts new file mode 100644 index 000000000..a70a67bc7 --- /dev/null +++ b/packages/typescript/algod_client/src/core/codecs.ts @@ -0,0 +1,13 @@ +import { encode as msgpackEncode, decode as msgpackDecode } from '@msgpack/msgpack' + +export function encodeMsgPack(value: unknown): Uint8Array { + return msgpackEncode(value, { + sortKeys: true, + ignoreUndefined: true, + useBigInt64: true, + }) +} + +export function decodeMsgPack(buffer: Uint8Array): T { + return msgpackDecode(buffer, { useBigInt64: true }) as T +} diff --git a/packages/typescript/algod_client/src/core/fetch-http-request.ts b/packages/typescript/algod_client/src/core/fetch-http-request.ts new file mode 100644 index 000000000..d57c1e667 --- /dev/null +++ b/packages/typescript/algod_client/src/core/fetch-http-request.ts @@ -0,0 +1,8 @@ +import { BaseHttpRequest, type ApiRequestOptions } from './base-http-request' +import { request } from './request' + +export class FetchHttpRequest extends BaseHttpRequest { + async request(options: ApiRequestOptions): Promise { + return request(this.config, options) + } +} diff --git a/packages/typescript/algod_client/src/core/json.ts b/packages/typescript/algod_client/src/core/json.ts deleted file mode 100644 index 3845c5abb..000000000 --- a/packages/typescript/algod_client/src/core/json.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Use require to avoid ESM/CJS interop issues in consumers -// eslint-disable-next-line @typescript-eslint/no-explicit-any -declare const require: any; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const JSONBigFactory = require("json-bigint"); - -export type IntDecoding = "safe" | "unsafe" | "mixed" | "bigint"; - -// Instances -const JSONBigMixed = JSONBigFactory({ useNativeBigInt: true, alwaysParseAsBig: false }); -const JSONBigAllBig = JSONBigFactory({ useNativeBigInt: true, alwaysParseAsBig: true }); - -function traverseAndThrowOnBigInt(obj: any): void { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (obj === null || obj === undefined) return; - const t = typeof obj; - if (t === "bigint") { - throw new Error('Integer exceeds safe range while INT_DECODING is "safe"'); - } - if (t !== "object") return; - if (Array.isArray(obj)) { - for (const v of obj) traverseAndThrowOnBigInt(v); - } else { - for (const v of Object.values(obj)) traverseAndThrowOnBigInt(v); - } -} - -function convertLargeNumericStrings(obj: any): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (obj == null) return obj; - if (typeof obj === "string") { - if (/^\d+$/.test(obj)) { - const asNum = Number(obj); - if (!Number.isSafeInteger(asNum)) return BigInt(obj); - } - return obj; - } - if (Array.isArray(obj)) return obj.map(convertLargeNumericStrings); - if (typeof obj === "object") { - for (const k of Object.keys(obj)) obj[k] = convertLargeNumericStrings(obj[k]); - } - return obj; -} - -export function parseJson(text: string, intDecoding: IntDecoding = "mixed"): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - switch (intDecoding) { - case "unsafe": - return JSON.parse(text); - case "bigint": { - const v = JSONBigAllBig.parse(text); - return convertLargeNumericStrings(v); - } - case "safe": { - const value = JSONBigMixed.parse(text); - traverseAndThrowOnBigInt(value); - return value; - } - case "mixed": - default: { - const v = JSONBigMixed.parse(text); - return convertLargeNumericStrings(v); - } - } -} - -export function stringifyJson(value: any): string { - // eslint-disable-line @typescript-eslint/no-explicit-any - return JSON.stringify(value, (_k, v) => (typeof v === "bigint" ? v.toString() : v)); -} diff --git a/packages/typescript/algod_client/src/core/model-runtime.ts b/packages/typescript/algod_client/src/core/model-runtime.ts new file mode 100644 index 000000000..87c8bfaf9 --- /dev/null +++ b/packages/typescript/algod_client/src/core/model-runtime.ts @@ -0,0 +1,264 @@ +import { + encodeSignedTransaction as transactEncodeSignedTransaction, + decodeSignedTransaction as transactDecodeSignedTransaction, + type SignedTransaction, +} from '@algorandfoundation/algokit-transact' +import { encodeMsgPack, decodeMsgPack } from './codecs' +import { toBase64, fromBase64 } from './serialization' + +export type BodyFormat = 'json' | 'msgpack' + +export interface ScalarFieldType { + readonly kind: 'scalar' + readonly isBytes?: boolean + readonly isBigint?: boolean +} + +export interface CodecFieldType { + readonly kind: 'codec' + readonly codecKey: string +} + +export interface ModelFieldType { + readonly kind: 'model' + readonly meta: ModelMetadata | (() => ModelMetadata) +} + +export interface ArrayFieldType { + readonly kind: 'array' + readonly item: FieldType +} + +export interface RecordFieldType { + readonly kind: 'record' + readonly value: FieldType +} + +export type FieldType = ScalarFieldType | CodecFieldType | ModelFieldType | ArrayFieldType | RecordFieldType + +export interface FieldMetadata { + readonly name: string + readonly wireKey: string + readonly optional: boolean + readonly nullable: boolean + readonly type: FieldType +} + +export type ModelKind = 'object' | 'array' | 'passthrough' + +export interface ModelMetadata { + readonly name: string + readonly kind: ModelKind + readonly fields?: readonly FieldMetadata[] + readonly arrayItems?: FieldType + readonly codecKey?: string + readonly additionalProperties?: FieldType + readonly passThrough?: FieldType +} + +export interface TypeCodec { + encode(value: TValue, format: BodyFormat): unknown + decode(value: unknown, format: BodyFormat): TValue +} + +const codecRegistry = new Map>() + +export function registerCodec(key: string, codec: TypeCodec): void { + codecRegistry.set(key, codec as TypeCodec) +} + +export function getCodec(key: string): TypeCodec | undefined { + return codecRegistry.get(key) as TypeCodec | undefined +} + +export class AlgorandSerializer { + static encode(value: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): Uint8Array | string { + const wire = this.transform(value, meta, { direction: 'encode', format }) + if (format === 'msgpack') { + return wire instanceof Uint8Array ? wire : encodeMsgPack(wire) + } + return typeof wire === 'string' ? wire : JSON.stringify(wire) + } + + static decode(payload: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): T { + let wire: unknown = payload + if (format === 'msgpack') { + if (payload instanceof Uint8Array) { + wire = decodeMsgPack(payload) + } + } else if (typeof payload === 'string') { + wire = JSON.parse(payload) + } + return this.transform(wire, meta, { direction: 'decode', format }) as T + } + + private static transform(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + if (value === undefined || value === null) { + return value + } + + if (meta.codecKey) { + return this.applyCodec(value, meta.codecKey, ctx) + } + + switch (meta.kind) { + case 'object': + return this.transformObject(value, meta, ctx) + case 'array': + return this.transformType(value, { kind: 'array', item: meta.arrayItems ?? { kind: 'scalar' } }, ctx) + case 'passthrough': + default: + return this.transformType(value, meta.passThrough ?? { kind: 'scalar' }, ctx) + } + } + + private static transformObject(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + const fields = meta.fields ?? [] + if (ctx.direction === 'encode') { + const src = value as Record + const out: Record = {} + for (const field of fields) { + const fieldValue = src[field.name] + if (fieldValue === undefined) continue + const encoded = this.transformType(fieldValue, field.type, ctx) + if (encoded === undefined && fieldValue === undefined) continue + out[field.wireKey] = encoded + } + if (meta.additionalProperties) { + for (const [key, val] of Object.entries(src)) { + if (fields.some((f) => f.name === key)) continue + out[key] = this.transformType(val, meta.additionalProperties, ctx) + } + } + return out + } + + const src = value as Record + const out: Record = {} + const fieldByWire = new Map(fields.map((field) => [field.wireKey, field])) + + for (const [wireKey, wireValue] of Object.entries(src)) { + const field = fieldByWire.get(wireKey) + if (field) { + const decoded = this.transformType(wireValue, field.type, ctx) + out[field.name] = decoded + continue + } + if (meta.additionalProperties) { + out[wireKey] = this.transformType(wireValue, meta.additionalProperties, ctx) + continue + } + out[wireKey] = wireValue + } + + return out + } + + private static transformType(value: unknown, type: FieldType, ctx: TransformContext): unknown { + if (value === undefined || value === null) return value + + switch (type.kind) { + case 'scalar': + return this.transformScalar(value, type, ctx) + case 'codec': + return this.applyCodec(value, type.codecKey, ctx) + case 'model': + return this.transform(value, typeof type.meta === 'function' ? type.meta() : type.meta, ctx) + case 'array': + if (!Array.isArray(value)) return value + return value.map((item) => this.transformType(item, type.item, ctx)) + case 'record': + if (typeof value !== 'object' || value === null) return value + return Object.fromEntries( + Object.entries(value as Record).map(([k, v]) => [k, this.transformType(v, type.value, ctx)]), + ) + default: + return value + } + } + + private static transformScalar(value: unknown, meta: ScalarFieldType, ctx: TransformContext): unknown { + if (ctx.direction === 'encode') { + if (meta.isBytes && ctx.format === 'json') { + if (value instanceof Uint8Array) return toBase64(value) + } + if (meta.isBigint && ctx.format === 'json') { + if (typeof value === 'bigint') return value.toString() + if (typeof value === 'number') return Math.trunc(value).toString() + if (typeof value === 'string') return value + } + return value + } + + if (meta.isBytes && ctx.format === 'json' && typeof value === 'string') { + return fromBase64(value) + } + + if (meta.isBigint) { + if (typeof value === 'string') { + try { + return BigInt(value) + } catch { + return value + } + } + if (typeof value === 'number' && Number.isInteger(value)) { + return BigInt(value) + } + } + + return value + } + + private static applyCodec(value: unknown, codecKey: string, ctx: TransformContext): unknown { + const codec = codecRegistry.get(codecKey) + if (!codec) { + throw new Error(`Codec for "${codecKey}" is not registered`) + } + return ctx.direction === 'encode' ? codec.encode(value, ctx.format) : codec.decode(value, ctx.format) + } +} + +type TransformDirection = 'encode' | 'decode' + +interface TransformContext { + readonly direction: TransformDirection + readonly format: BodyFormat +} + +const encodeSignedTransactionImpl = (value: unknown): Uint8Array => transactEncodeSignedTransaction(value as SignedTransaction) +const decodeSignedTransactionImpl = (value: Uint8Array): SignedTransaction => transactDecodeSignedTransaction(value) + +class SignedTransactionCodec implements TypeCodec { + encode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value + if (format === 'json') { + if (value instanceof Uint8Array) return toBase64(value) + return toBase64(encodeSignedTransactionImpl(value)) + } + if (value instanceof Uint8Array) { + // Already canonical bytes; decode to structured map so parent encoding keeps map semantics + return decodeMsgPack(value) + } + // Convert signed transaction object into canonical map representation + return decodeMsgPack(encodeSignedTransactionImpl(value)) + } + + decode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value + if (format === 'json') { + if (typeof value === 'string') return decodeSignedTransactionImpl(fromBase64(value)) + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value) + return value + } + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value) + // Value is a decoded map; re-encode to bytes before handing to transact decoder + try { + return decodeSignedTransactionImpl(encodeMsgPack(value)) + } catch { + return value + } + } +} + +registerCodec('SignedTransaction', new SignedTransactionCodec()) diff --git a/packages/typescript/algod_client/src/core/msgpack.ts b/packages/typescript/algod_client/src/core/msgpack.ts deleted file mode 100644 index 8e3025089..000000000 --- a/packages/typescript/algod_client/src/core/msgpack.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { encode as msgpackEncode, decode as msgpackDecode } from "@msgpack/msgpack"; -// TODO(utils-ts): Remove temporary type import when utils-ts is integrated -import type { AlgokitSignedTransaction } from "../models"; -import type { IntDecoding } from "./json"; - -/** - * Prepare value for Algorand-compliant msgpack encoding. - * Implements strict Algorand msgpack rules (matching go-algorand behavior): - * 1. Omit zero values, empty strings, and empty arrays (RecursiveEmptyCheck) - * 2. Convert bigints to smallest safe integer type (PositiveIntUnsigned) - * 3. Sorted keys and canonical encoding are handled by msgpackEncode options - * - * These rules apply universally for both API communication and transaction encoding, - * as go-algorand uses the same codec settings for all msgpack operations. - */ -function prepareForEncoding(value: any): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (value === null || value === undefined) { - return undefined; - } - - // Handle numbers - omit zeros - if (typeof value === "number") { - if (value === 0) return undefined; - return value; - } - - // Handle bigints - omit zeros and convert to number when safe (for smaller encoding) - if (typeof value === "bigint") { - if (value === 0n) return undefined; - // Convert to number if it fits safely (implements PositiveIntUnsigned behavior) - if (value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(Number.MIN_SAFE_INTEGER)) { - return Number(value); - } - return value; - } - - // Handle strings - omit empty strings - if (typeof value === "string") { - if (value === "") return undefined; - return value; - } - - // Handle Uint8Array - omit empty arrays - if (value instanceof Uint8Array) { - if (value.length === 0) return undefined; - return value; - } - - // Handle arrays - omit empty arrays and filter undefined values - if (Array.isArray(value)) { - if (value.length === 0) return undefined; - const processed = value.map(prepareForEncoding).filter((v) => v !== undefined); - return processed.length > 0 ? processed : undefined; - } - - // Handle objects - omit empty objects and filter undefined values (RecursiveEmptyCheck) - if (value && typeof value === "object") { - const result: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - for (const [k, v] of Object.entries(value)) { - const prepared = prepareForEncoding(v); - if (prepared !== undefined) { - result[k] = prepared; - } - } - // Return undefined if object is empty after filtering - return Object.keys(result).length > 0 ? result : undefined; - } - - return value; -} - -function convertIntegersToBigInt(value: any): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (value === null || value === undefined) { - return value; - } - if (typeof value === "number" && Number.isInteger(value)) { - return BigInt(value); - } - if (typeof value === "bigint") { - return value; - } - if (value instanceof Uint8Array) { - return value; - } - if (Array.isArray(value)) { - return value.map(convertIntegersToBigInt); - } - if (value && typeof value === "object") { - const result: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - for (const [k, v] of Object.entries(value)) { - result[k] = convertIntegersToBigInt(v); - } - return result; - } - return value; -} - -/** - * Encode a value as msgpack using Algorand's strict encoding rules. - * This matches go-algorand's protocol.CodecHandle settings: - * - Canonical = true (sorted keys, deterministic encoding) - * - RecursiveEmptyCheck = true (omit empty/zero values recursively) - * - PositiveIntUnsigned = true (use smallest unsigned integer types) - * - * @param value - The value to encode - * @returns Encoded msgpack bytes - */ -export function encodeMsgPack(value: any): Uint8Array { - // eslint-disable-line @typescript-eslint/no-explicit-any - const prepared = prepareForEncoding(value); - - // Ensure we return valid msgpack even if everything was omitted - const toEncode = prepared === undefined ? {} : prepared; - - return msgpackEncode(toEncode, { - sortKeys: true, // Canonical = true in go-algorand - forceIntegerToFloat: false, - ignoreUndefined: true, // Handle undefined values from prepareForEncoding - initialBufferSize: 2048, - useBigInt64: true, // Support for large integers - }); -} - -export function decodeMsgPack(buffer: Uint8Array): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - const decoded = msgpackDecode(buffer, { - useBigInt64: true, - }); - return convertIntegersToBigInt(decoded); -} - -export function normalizeMsgPackIntegers(value: any, intDecoding: IntDecoding): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - switch (intDecoding) { - case "bigint": - return value; - case "unsafe": - return mapBigInts(value, (bi) => Number(bi)); - case "safe": - // Throw if any bigint is not safely representable - traverse(value, (v) => { - if (typeof v === "bigint" && !Number.isSafeInteger(Number(v))) { - throw new Error('Integer exceeds safe range while INT_DECODING is "safe"'); - } - }); - return mapBigInts(value, (bi) => Number(bi)); - case "mixed": - default: - return mapBigInts(value, (bi) => { - const asNum = Number(bi); - return Number.isSafeInteger(asNum) ? asNum : bi; - }); - } -} - -// Helpers to map SignedTransactionDto <-> AlgokitSignedTransaction if present in responses -// No DTO helpers needed: once utils-ts is integrated, this file remains unchanged. - -function traverse(obj: any, fn: (v: any) => void): void { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (obj == null) return; - fn(obj); - if (Array.isArray(obj)) { - for (const v of obj) traverse(v, fn); - } else if (typeof obj === "object") { - for (const v of Object.values(obj)) traverse(v, fn); - } -} - -function mapBigInts(obj: any, mapFn: (bi: bigint) => any): any { - // eslint-disable-line @typescript-eslint/no-explicit-any - if (obj == null) return obj; - if (typeof obj === "bigint") return mapFn(obj); - if (Array.isArray(obj)) return obj.map((v) => mapBigInts(v, mapFn)); - if (typeof obj === "object") { - const out: any = Array.isArray(obj) ? [] : { ...obj }; // eslint-disable-line @typescript-eslint/no-explicit-any - for (const [k, v] of Object.entries(obj)) { - out[k] = mapBigInts(v, mapFn); - } - return out; - } - return obj; -} diff --git a/packages/typescript/algod_client/src/core/request.ts b/packages/typescript/algod_client/src/core/request.ts index fbe1f771c..3a4ae2941 100644 --- a/packages/typescript/algod_client/src/core/request.ts +++ b/packages/typescript/algod_client/src/core/request.ts @@ -1,69 +1,73 @@ -import type { ClientConfig } from "./ClientConfig"; -import { ApiError } from "./ApiError"; -import { parseJson, stringifyJson } from "./json"; -import { decodeMsgPack, encodeMsgPack, normalizeMsgPackIntegers } from "./msgpack"; -import { toCamelCaseKeysDeep } from "./casing"; -import { toKebabCaseKeysDeep } from "./casing"; +import type { ClientConfig } from './client-config' +import { ApiError } from './api-error' +import { decodeMsgPack, encodeMsgPack } from './codecs' +import type { QueryParams, BodyValue } from './base-http-request' -const encodeURIPath = (path: string): string => encodeURI(path).replace(/%5B/g, "[").replace(/%5D/g, "]"); +const encodeURIPath = (path: string): string => encodeURI(path).replace(/%5B/g, '[').replace(/%5D/g, ']') export async function request( config: ClientConfig, options: { - method: string; - url: string; - path?: Record; - query?: Record; - headers?: Record; - body?: any; - mediaType?: string; - responseHeader?: string; + method: string + url: string + path?: Record + query?: QueryParams + headers?: Record + body?: BodyValue + mediaType?: string + responseHeader?: string }, ): Promise { - // Replace path params before constructing URL to avoid encoded braces preventing replacement - let rawPath = options.url; + let rawPath = options.url if (options.path) { for (const [key, value] of Object.entries(options.path)) { - const raw = typeof value === "bigint" ? value.toString() : String(value); - const replace = config.ENCODE_PATH ? config.ENCODE_PATH(raw) : encodeURIPath(raw); - rawPath = rawPath.replace(`{${key}}`, replace); + const raw = typeof value === 'bigint' ? value.toString() : String(value) + const replace = config.encodePath ? config.encodePath(raw) : encodeURIPath(raw) + rawPath = rawPath.replace(`{${key}}`, replace) } } - const url = new URL(rawPath, config.BASE); + const url = new URL(rawPath, config.baseUrl) - // Query params if (options.query) { for (const [key, value] of Object.entries(options.query)) { - if (value === undefined || value === null) continue; - const v = typeof value === "bigint" ? value.toString() : String(value); - url.searchParams.append(key, v); + if (value === undefined || value === null) continue + if (Array.isArray(value)) { + for (const item of value) { + url.searchParams.append(key, item.toString()) + } + } else { + url.searchParams.append(key, value.toString()) + } } } const headers: Record = { - ...(typeof config.HEADERS === "function" ? await config.HEADERS() : (config.HEADERS ?? {})), + ...(typeof config.headers === 'function' ? await config.headers() : (config.headers ?? {})), ...(options.headers ?? {}), - }; + } + + const apiToken = config.apiToken + if (apiToken) { + headers['X-Algo-API-Token'] = apiToken + } - // Auth: Bearer or Basic - const token = typeof config.TOKEN === "function" ? await config.TOKEN() : config.TOKEN; - if (token) headers["Authorization"] = `Bearer ${token}`; - if (!token && config.USERNAME && config.PASSWORD) { - headers["Authorization"] = `Basic ${btoa(`${config.USERNAME}:${config.PASSWORD}`)}`; + const token = typeof config.token === 'function' ? await config.token() : config.token + if (token) headers['Authorization'] = `Bearer ${token}` + if (!token && config.username && config.password) { + headers['Authorization'] = `Basic ${btoa(`${config.username}:${config.password}`)}` } - // Prepare body based on media type - let body: any = undefined; + let body: BodyValue | undefined = undefined if (options.body != null) { - if (options.mediaType?.includes("json")) { - body = stringifyJson(toKebabCaseKeysDeep(options.body)); - } else if (options.mediaType?.includes("msgpack")) { - // Encode typed models to msgpack for requests - body = encodeMsgPack(toKebabCaseKeysDeep(options.body)); + if (options.body instanceof Uint8Array || typeof options.body === 'string') { + body = options.body + } else if (options.mediaType?.includes('msgpack')) { + body = encodeMsgPack(options.body) + } else if (options.mediaType?.includes('json')) { + body = JSON.stringify(options.body) } else { - // For binary/text, pass through as-is - body = options.body; + body = options.body } } @@ -71,56 +75,48 @@ export async function request( method: options.method, headers, body, - credentials: config.CREDENTIALS, - }); + credentials: config.credentials, + }) if (!response.ok) { - let body: any = undefined; + let errorBody: unknown try { - const ct = response.headers.get("content-type"); - if (ct && ct.includes("application/json")) body = parseJson(await response.text(), config.INT_DECODING ?? "mixed"); - else body = await response.text(); - } catch {} - throw new ApiError(url.toString(), response.status, body); + const ct = response.headers.get('content-type') ?? '' + if (ct.includes('application/msgpack')) { + errorBody = decodeMsgPack(new Uint8Array(await response.arrayBuffer())) + } else if (ct.includes('application/json')) { + errorBody = JSON.parse(await response.text()) + } else { + errorBody = await response.text() + } + } catch { + errorBody = undefined + } + throw new ApiError(url.toString(), response.status, errorBody) } if (options.responseHeader) { - const value = response.headers.get(options.responseHeader); - return value as unknown as T; + const value = response.headers.get(options.responseHeader) + return value as unknown as T } - // Parse response by content-type - const contentType = response.headers.get("content-type") || ""; - - // Handle msgpack responses - decode to typed models - if (contentType.includes("application/msgpack")) { - const buf = new Uint8Array(await response.arrayBuffer()); - const decoded = decodeMsgPack(buf); - const normalized = normalizeMsgPackIntegers(decoded, config.INT_DECODING ?? "bigint"); - // Lightweight mapping: if the response contains arrays/objects with x-algokit-signed-txn shapes - // they will decode to DTOs and be assignable to AlgokitSignedTransaction via alias. - return toCamelCaseKeysDeep(normalized) as T; + const contentType = response.headers.get('content-type') ?? '' + + if (contentType.includes('application/msgpack')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T } - // Handle raw binary responses (e.g., application/x-binary for raw transactions) - if (contentType.includes("application/x-binary") || contentType.includes("application/octet-stream")) { - // For raw binary, return as Uint8Array without decoding - return new Uint8Array(await response.arrayBuffer()) as unknown as T; + if (contentType.includes('application/octet-stream') || contentType.includes('application/x-binary')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T } - // Handle JSON responses - if (contentType.includes("application/json")) { - const text = await response.text(); - const parsed = parseJson(text, config.INT_DECODING ?? "mixed"); - return toCamelCaseKeysDeep(parsed) as T; + if (contentType.includes('application/json')) { + return (await response.text()) as unknown as T } - // Fallback to text - const text = await response.text(); - try { - const parsed = JSON.parse(text); - return toCamelCaseKeysDeep(parsed) as T; - } catch { - return text as unknown as T; + if (!contentType) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T } + + return (await response.text()) as unknown as T } diff --git a/packages/typescript/algod_client/src/core/serialization.ts b/packages/typescript/algod_client/src/core/serialization.ts new file mode 100644 index 000000000..6be054287 --- /dev/null +++ b/packages/typescript/algod_client/src/core/serialization.ts @@ -0,0 +1,26 @@ +export function toBase64(bytes: Uint8Array): string { + if (typeof Buffer !== 'undefined') { + return Buffer.from(bytes).toString('base64') + } + const globalRef: Record = globalThis as unknown as Record + const btoaFn = globalRef.btoa as ((value: string) => string) | undefined + if (typeof btoaFn === 'function') { + return btoaFn(String.fromCharCode(...bytes)) + } + throw new Error('Base64 encoding not supported in this environment') +} + +export function fromBase64(s: string): Uint8Array { + if (typeof Buffer !== 'undefined') { + return new Uint8Array(Buffer.from(s, 'base64')) + } + const globalRef: Record = globalThis as unknown as Record + const atobFn = globalRef.atob as ((value: string) => string) | undefined + if (typeof atobFn === 'function') { + const bin = atobFn(s) + const out = new Uint8Array(bin.length) + for (let i = 0; i < bin.length; i += 1) out[i] = bin.charCodeAt(i) + return out + } + throw new Error('Base64 decoding not supported in this environment') +} diff --git a/packages/typescript/algod_client/src/index.ts b/packages/typescript/algod_client/src/index.ts index 9912e902e..915506d52 100644 --- a/packages/typescript/algod_client/src/index.ts +++ b/packages/typescript/algod_client/src/index.ts @@ -1,10 +1,12 @@ -export * from "./core/ClientConfig"; -export * from "./core/BaseHttpRequest"; -export * from "./core/FetchHttpRequest"; -export * from "./core/ApiError"; -export * from "./core/CancelablePromise"; -export * from "./core/json"; +export * from './core/client-config' +export * from './core/base-http-request' +export * from './core/fetch-http-request' +export * from './core/api-error' +export * from './core/serialization' +export * from './core/codecs' +export * from './core/model-runtime' -export * from "./models"; -export * from "./apis"; -export * from "./client"; +// Generated +export * from './models' +export * from './apis' +export * from './client' diff --git a/packages/typescript/algod_client/src/models/abort-catchup.ts b/packages/typescript/algod_client/src/models/abort-catchup.ts new file mode 100644 index 000000000..d08b63c14 --- /dev/null +++ b/packages/typescript/algod_client/src/models/abort-catchup.ts @@ -0,0 +1,25 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * An catchpoint abort response. + */ +export type AbortCatchup = { + /** + * Catchup abort response string + */ + catchupMessage: string +} + +export const AbortCatchupMeta: ModelMetadata = { + name: 'AbortCatchup', + kind: 'object', + fields: [ + { + name: 'catchupMessage', + wireKey: 'catchup-message', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/abortcatchup.ts b/packages/typescript/algod_client/src/models/abortcatchup.ts deleted file mode 100644 index 907aae39f..000000000 --- a/packages/typescript/algod_client/src/models/abortcatchup.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * An catchpoint abort response. - */ -export type AbortCatchup = { - /** - * Catchup abort response string - */ - catchupMessage: string; -}; diff --git a/packages/typescript/algod_client/src/models/account-application-information.ts b/packages/typescript/algod_client/src/models/account-application-information.ts new file mode 100644 index 000000000..0042fb898 --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-application-information.ts @@ -0,0 +1,42 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationLocalState } from './application-local-state' +import { ApplicationLocalStateMeta } from './application-local-state' +import type { ApplicationParams } from './application-params' +import { ApplicationParamsMeta } from './application-params' + +export type AccountApplicationInformation = { + /** + * The round for which this information is relevant. + */ + round: bigint + appLocalState?: ApplicationLocalState + createdApp?: ApplicationParams +} + +export const AccountApplicationInformationMeta: ModelMetadata = { + name: 'AccountApplicationInformation', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appLocalState', + wireKey: 'app-local-state', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationLocalStateMeta }, + }, + { + name: 'createdApp', + wireKey: 'created-app', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationParamsMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account-asset-holding.ts b/packages/typescript/algod_client/src/models/account-asset-holding.ts new file mode 100644 index 000000000..1d7e9532a --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-asset-holding.ts @@ -0,0 +1,34 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetHolding } from './asset-holding' +import { AssetHoldingMeta } from './asset-holding' +import type { AssetParams } from './asset-params' +import { AssetParamsMeta } from './asset-params' + +/** + * AccountAssetHolding describes the account's asset holding and asset parameters (if either exist) for a specific asset ID. + */ +export type AccountAssetHolding = { + assetHolding: AssetHolding + assetParams?: AssetParams +} + +export const AccountAssetHoldingMeta: ModelMetadata = { + name: 'AccountAssetHolding', + kind: 'object', + fields: [ + { + name: 'assetHolding', + wireKey: 'asset-holding', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AssetHoldingMeta }, + }, + { + name: 'assetParams', + wireKey: 'asset-params', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AssetParamsMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account-asset-information.ts b/packages/typescript/algod_client/src/models/account-asset-information.ts new file mode 100644 index 000000000..32b3e36b4 --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-asset-information.ts @@ -0,0 +1,42 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetHolding } from './asset-holding' +import { AssetHoldingMeta } from './asset-holding' +import type { AssetParams } from './asset-params' +import { AssetParamsMeta } from './asset-params' + +export type AccountAssetInformation = { + /** + * The round for which this information is relevant. + */ + round: bigint + assetHolding?: AssetHolding + createdAsset?: AssetParams +} + +export const AccountAssetInformationMeta: ModelMetadata = { + name: 'AccountAssetInformation', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assetHolding', + wireKey: 'asset-holding', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AssetHoldingMeta }, + }, + { + name: 'createdAsset', + wireKey: 'created-asset', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AssetParamsMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account-assets-information.ts b/packages/typescript/algod_client/src/models/account-assets-information.ts new file mode 100644 index 000000000..fe704d6e3 --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-assets-information.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountAssetHolding } from './account-asset-holding' +import { AccountAssetHoldingMeta } from './account-asset-holding' + +export type AccountAssetsInformation = { + /** + * The round for which this information is relevant. + */ + round: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + assetHoldings?: AccountAssetHolding[] +} + +export const AccountAssetsInformationMeta: ModelMetadata = { + name: 'AccountAssetsInformation', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assetHoldings', + wireKey: 'asset-holdings', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountAssetHoldingMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account-participation.ts b/packages/typescript/algod_client/src/models/account-participation.ts new file mode 100644 index 000000000..5644da02b --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-participation.ts @@ -0,0 +1,85 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * AccountParticipation describes the parameters used by this account in consensus protocol. + */ +export type AccountParticipation = { + /** + * \[sel\] Selection public key (if any) currently registered for this round. + */ + selectionParticipationKey: Uint8Array + + /** + * \[voteFst\] First round for which this participation is valid. + */ + voteFirstValid: bigint + + /** + * \[voteKD\] Number of subkeys in each batch of participation keys. + */ + voteKeyDilution: bigint + + /** + * \[voteLst\] Last round for which this participation is valid. + */ + voteLastValid: bigint + + /** + * \[vote\] root participation public key (if any) currently registered for this round. + */ + voteParticipationKey: Uint8Array + + /** + * \[stprf\] Root of the state proof key (if any) + */ + stateProofKey?: Uint8Array +} + +export const AccountParticipationMeta: ModelMetadata = { + name: 'AccountParticipation', + kind: 'object', + fields: [ + { + name: 'selectionParticipationKey', + wireKey: 'selection-participation-key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'voteFirstValid', + wireKey: 'vote-first-valid', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteKeyDilution', + wireKey: 'vote-key-dilution', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteLastValid', + wireKey: 'vote-last-valid', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteParticipationKey', + wireKey: 'vote-participation-key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'stateProofKey', + wireKey: 'state-proof-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account-state-delta.ts b/packages/typescript/algod_client/src/models/account-state-delta.ts new file mode 100644 index 000000000..ad93395f1 --- /dev/null +++ b/packages/typescript/algod_client/src/models/account-state-delta.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateDelta } from './state-delta' +import { StateDeltaMeta } from './state-delta' + +/** + * Application state delta. + */ +export type AccountStateDelta = { + address: string + delta: StateDelta +} + +export const AccountStateDeltaMeta: ModelMetadata = { + name: 'AccountStateDelta', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'delta', + wireKey: 'delta', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => StateDeltaMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/account.ts b/packages/typescript/algod_client/src/models/account.ts index b3fe3f408..21ee98fa9 100644 --- a/packages/typescript/algod_client/src/models/account.ts +++ b/packages/typescript/algod_client/src/models/account.ts @@ -1,4 +1,16 @@ -import type { AccountParticipation, Application, ApplicationLocalState, ApplicationStateSchema, Asset, AssetHolding } from "./index"; +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountParticipation } from './account-participation' +import { AccountParticipationMeta } from './account-participation' +import type { Application } from './application' +import { ApplicationMeta } from './application' +import type { ApplicationLocalState } from './application-local-state' +import { ApplicationLocalStateMeta } from './application-local-state' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { Asset } from './asset' +import { AssetMeta } from './asset' +import type { AssetHolding } from './asset-holding' +import { AssetHoldingMeta } from './asset-holding' /** * Account information at a given round. @@ -10,114 +22,114 @@ export type Account = { /** * the account public key */ - address: string; + address: string /** * \[algo\] total number of MicroAlgos in the account */ - amount: bigint; + amount: bigint /** * MicroAlgo balance required by the account. * * The requirement grows based on asset and application usage. */ - minBalance: bigint; + minBalance: bigint /** * specifies the amount of MicroAlgos in the account, without the pending rewards. */ - amountWithoutPendingRewards: bigint; + amountWithoutPendingRewards: bigint /** * \[appl\] applications local data stored in this account. * * Note the raw object uses `map[int] -> AppLocalState` for this type. */ - appsLocalState?: ApplicationLocalState[]; + appsLocalState?: ApplicationLocalState[] /** * The count of all applications that have been opted in, equivalent to the count of application local data (AppLocalState objects) stored in this account. */ - totalAppsOptedIn: bigint; - appsTotalSchema?: ApplicationStateSchema; + totalAppsOptedIn: bigint + appsTotalSchema?: ApplicationStateSchema /** * \[teap\] the sum of all extra application program pages for this account. */ - appsTotalExtraPages?: bigint; + appsTotalExtraPages?: bigint /** * \[asset\] assets held by this account. * * Note the raw object uses `map[int] -> AssetHolding` for this type. */ - assets?: AssetHolding[]; + assets?: AssetHolding[] /** * The count of all assets that have been opted in, equivalent to the count of AssetHolding objects held by this account. */ - totalAssetsOptedIn: bigint; + totalAssetsOptedIn: bigint /** * \[appp\] parameters of applications created by this account including app global data. * * Note: the raw account uses `map[int] -> AppParams` for this type. */ - createdApps?: Application[]; + createdApps?: Application[] /** * The count of all apps (AppParams objects) created by this account. */ - totalCreatedApps: bigint; + totalCreatedApps: bigint /** * \[apar\] parameters of assets created by this account. * * Note: the raw account uses `map[int] -> Asset` for this type. */ - createdAssets?: Asset[]; + createdAssets?: Asset[] /** * The count of all assets (AssetParams objects) created by this account. */ - totalCreatedAssets: bigint; + totalCreatedAssets: bigint /** * \[tbx\] The number of existing boxes created by this account's app. */ - totalBoxes?: bigint; + totalBoxes?: bigint /** * \[tbxb\] The total number of bytes used by this account's app's box keys and values. */ - totalBoxBytes?: bigint; - participation?: AccountParticipation; + totalBoxBytes?: bigint + participation?: AccountParticipation /** * Whether or not the account can receive block incentives if its balance is in range at proposal time. */ - incentiveEligible?: boolean; + incentiveEligible?: boolean /** * amount of MicroAlgos of pending rewards in this account. */ - pendingRewards: bigint; + pendingRewards: bigint /** * \[ebase\] used as part of the rewards computation. Only applicable to accounts which are participating. */ - rewardBase?: bigint; + rewardBase?: bigint /** * \[ern\] total rewards of MicroAlgos the account has received, including pending rewards. */ - rewards: bigint; + rewards: bigint /** * The round for which this information is relevant. */ - round: bigint; + round: bigint /** * \[onl\] delegation status of the account's MicroAlgos @@ -125,7 +137,7 @@ export type Account = { * * Online - indicates that the associated account used as part of the delegation pool. * * NotParticipating - indicates that the associated account is neither a delegator nor a delegate. */ - status: string; + status: string /** * Indicates what type of signature is used by this account, must be one of: @@ -133,20 +145,216 @@ export type Account = { * * msig * * lsig */ - sigType?: "sig" | "msig" | "lsig"; + sigType?: 'sig' | 'msig' | 'lsig' /** * \[spend\] the address against which signing should be checked. If empty, the address of the current account is used. This field can be updated in any transaction by setting the RekeyTo field. */ - authAddr?: string; + authAddr?: string /** * The round in which this account last proposed the block. */ - lastProposed?: bigint; + lastProposed?: bigint /** * The round in which this account last went online, or explicitly renewed their online status. */ - lastHeartbeat?: bigint; -}; + lastHeartbeat?: bigint +} + +export const AccountMeta: ModelMetadata = { + name: 'Account', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'minBalance', + wireKey: 'min-balance', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'amountWithoutPendingRewards', + wireKey: 'amount-without-pending-rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appsLocalState', + wireKey: 'apps-local-state', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationLocalStateMeta } }, + }, + { + name: 'totalAppsOptedIn', + wireKey: 'total-apps-opted-in', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appsTotalSchema', + wireKey: 'apps-total-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'appsTotalExtraPages', + wireKey: 'apps-total-extra-pages', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assets', + wireKey: 'assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetHoldingMeta } }, + }, + { + name: 'totalAssetsOptedIn', + wireKey: 'total-assets-opted-in', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'createdApps', + wireKey: 'created-apps', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationMeta } }, + }, + { + name: 'totalCreatedApps', + wireKey: 'total-created-apps', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'createdAssets', + wireKey: 'created-assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetMeta } }, + }, + { + name: 'totalCreatedAssets', + wireKey: 'total-created-assets', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'totalBoxes', + wireKey: 'total-boxes', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'totalBoxBytes', + wireKey: 'total-box-bytes', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'participation', + wireKey: 'participation', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AccountParticipationMeta }, + }, + { + name: 'incentiveEligible', + wireKey: 'incentive-eligible', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'pendingRewards', + wireKey: 'pending-rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'rewardBase', + wireKey: 'reward-base', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'rewards', + wireKey: 'rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'status', + wireKey: 'status', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sigType', + wireKey: 'sig-type', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'authAddr', + wireKey: 'auth-addr', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lastProposed', + wireKey: 'last-proposed', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastHeartbeat', + wireKey: 'last-heartbeat', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/accountapplicationinformation.ts b/packages/typescript/algod_client/src/models/accountapplicationinformation.ts deleted file mode 100644 index 4440b1dcc..000000000 --- a/packages/typescript/algod_client/src/models/accountapplicationinformation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ApplicationLocalState, ApplicationParams } from "./index"; - -export type AccountApplicationInformation = { - /** - * The round for which this information is relevant. - */ - round: bigint; - appLocalState?: ApplicationLocalState; - createdApp?: ApplicationParams; -}; diff --git a/packages/typescript/algod_client/src/models/accountassetholding.ts b/packages/typescript/algod_client/src/models/accountassetholding.ts deleted file mode 100644 index e6efa5db0..000000000 --- a/packages/typescript/algod_client/src/models/accountassetholding.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AssetHolding, AssetParams } from "./index"; - -/** - * AccountAssetHolding describes the account's asset holding and asset parameters (if either exist) for a specific asset ID. - */ -export type AccountAssetHolding = { - assetHolding: AssetHolding; - assetParams?: AssetParams; -}; diff --git a/packages/typescript/algod_client/src/models/accountassetinformation.ts b/packages/typescript/algod_client/src/models/accountassetinformation.ts deleted file mode 100644 index 2a2cd6dac..000000000 --- a/packages/typescript/algod_client/src/models/accountassetinformation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { AssetHolding, AssetParams } from "./index"; - -export type AccountAssetInformation = { - /** - * The round for which this information is relevant. - */ - round: bigint; - assetHolding?: AssetHolding; - createdAsset?: AssetParams; -}; diff --git a/packages/typescript/algod_client/src/models/accountassetsinformation.ts b/packages/typescript/algod_client/src/models/accountassetsinformation.ts deleted file mode 100644 index 1cbb9b405..000000000 --- a/packages/typescript/algod_client/src/models/accountassetsinformation.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { AccountAssetHolding } from "./index"; - -export type AccountAssetsInformation = { - /** - * The round for which this information is relevant. - */ - round: bigint; - - /** - * Used for pagination, when making another request provide this token with the next parameter. - */ - nextToken?: string; - assetHoldings?: AccountAssetHolding[]; -}; diff --git a/packages/typescript/algod_client/src/models/accountparticipation.ts b/packages/typescript/algod_client/src/models/accountparticipation.ts deleted file mode 100644 index 8942954fe..000000000 --- a/packages/typescript/algod_client/src/models/accountparticipation.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * AccountParticipation describes the parameters used by this account in consensus protocol. - */ -export type AccountParticipation = { - /** - * \[sel\] Selection public key (if any) currently registered for this round. - */ - selectionParticipationKey: string; - - /** - * \[voteFst\] First round for which this participation is valid. - */ - voteFirstValid: bigint; - - /** - * \[voteKD\] Number of subkeys in each batch of participation keys. - */ - voteKeyDilution: bigint; - - /** - * \[voteLst\] Last round for which this participation is valid. - */ - voteLastValid: bigint; - - /** - * \[vote\] root participation public key (if any) currently registered for this round. - */ - voteParticipationKey: string; - - /** - * \[stprf\] Root of the state proof key (if any) - */ - stateProofKey?: string; -}; diff --git a/packages/typescript/algod_client/src/models/accountstatedelta.ts b/packages/typescript/algod_client/src/models/accountstatedelta.ts deleted file mode 100644 index 5bd98a58e..000000000 --- a/packages/typescript/algod_client/src/models/accountstatedelta.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { StateDelta } from "./index"; - -/** - * Application state delta. - */ -export type AccountStateDelta = { - address: string; - delta: StateDelta; -}; diff --git a/packages/typescript/algod_client/src/models/add-participation-key.ts b/packages/typescript/algod_client/src/models/add-participation-key.ts new file mode 100644 index 000000000..4a35b00fd --- /dev/null +++ b/packages/typescript/algod_client/src/models/add-participation-key.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type AddParticipationKey = { + /** + * encoding of the participation ID. + */ + partId: string +} + +export const AddParticipationKeyMeta: ModelMetadata = { + name: 'AddParticipationKey', + kind: 'object', + fields: [ + { + name: 'partId', + wireKey: 'partId', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/addparticipationkey.ts b/packages/typescript/algod_client/src/models/addparticipationkey.ts deleted file mode 100644 index ae731eeb3..000000000 --- a/packages/typescript/algod_client/src/models/addparticipationkey.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type AddParticipationKey = { - /** - * encoding of the participation ID. - */ - partId: string; -}; diff --git a/packages/typescript/algod_client/src/models/algokitsignedtransaction.ts b/packages/typescript/algod_client/src/models/algokitsignedtransaction.ts deleted file mode 100644 index 01e5e014a..000000000 --- a/packages/typescript/algod_client/src/models/algokitsignedtransaction.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Temporary SignedTransaction model for msgpack encoding/decoding. - * This will be replaced when utils-ts is integrated into the monorepo. - * - * This type represents a signed Algorand transaction that can be - * encoded to msgpack format for the simulate endpoint. - */ - -/** - * Base transaction structure that all transaction types share - */ -export type BaseTransaction = { - /** Transaction type */ - type: "pay" | "axfer" | "afrz" | "acfg" | "keyreg" | "appl"; - - /** Sender address (base64 or string) */ - sender: string; - - /** Fee in microALGO */ - fee?: number | bigint; - - /** First valid round */ - firstValid: number | bigint; - - /** Last valid round */ - lastValid: number | bigint; - - /** Genesis ID */ - genesisId?: string; - - /** Genesis hash (base64) */ - genesisHash?: string; - - /** Transaction note (base64 or Uint8Array) */ - note?: string | Uint8Array; - - /** Lease (base64) */ - lease?: string; - - /** Rekey to address */ - rekeyTo?: string; - - /** Group ID (base64) */ - group?: string; -}; - -/** - * Payment transaction - */ -export type PaymentTransaction = BaseTransaction & { - type: "pay"; - - /** Receiver address */ - receiver: string; - - /** Amount in microALGO */ - amount: number | bigint; - - /** Close remainder to address */ - closeRemainderTo?: string; -}; - -/** - * Asset transfer transaction - */ -export type AssetTransferTransaction = BaseTransaction & { - type: "axfer"; - - /** Asset ID */ - assetIndex: number | bigint; - - /** Asset amount */ - amount: number | bigint; - - /** Asset receiver */ - receiver: string; - - /** Asset close to */ - closeRemainderTo?: string; - - /** Asset sender (for clawback) */ - assetSender?: string; -}; - -/** - * Asset config transaction - */ -export type AssetConfigTransaction = BaseTransaction & { - type: "acfg"; - - /** Asset ID (0 for creation) */ - assetIndex?: number | bigint; - - /** Asset parameters */ - assetParams?: { - total?: number | bigint; - decimals?: number; - defaultFrozen?: boolean; - unitName?: string; - assetName?: string; - url?: string; - metadataHash?: string; - manager?: string; - reserve?: string; - freeze?: string; - clawback?: string; - }; -}; - -/** - * Asset freeze transaction - */ -export type AssetFreezeTransaction = BaseTransaction & { - type: "afrz"; - - /** Asset ID */ - assetIndex: number | bigint; - - /** Address to freeze/unfreeze */ - freezeAccount: string; - - /** Freeze state */ - frozen: boolean; -}; - -/** - * Key registration transaction - */ -export type KeyRegTransaction = BaseTransaction & { - type: "keyreg"; - - /** Voting key */ - voteKey?: string; - - /** Selection key */ - selectionKey?: string; - - /** State proof key */ - stateProofKey?: string; - - /** First voting round */ - voteFirst?: number | bigint; - - /** Last voting round */ - voteLast?: number | bigint; - - /** Vote key dilution */ - voteKeyDilution?: number | bigint; - - /** Non-participation flag */ - nonParticipation?: boolean; -}; - -/** - * Application call transaction - */ -export type ApplicationTransaction = BaseTransaction & { - type: "appl"; - - /** Application ID (0 for creation) */ - applicationId: number | bigint; - - /** OnComplete action */ - onComplete: number; - - /** Application arguments (base64 encoded) */ - applicationArgs?: string[]; - - /** Accounts array */ - accounts?: string[]; - - /** Foreign apps */ - foreignApps?: (number | bigint)[]; - - /** Foreign assets */ - foreignAssets?: (number | bigint)[]; - - /** Approval program (base64) */ - approvalProgram?: string; - - /** Clear program (base64) */ - clearProgram?: string; - - /** Global state schema */ - globalStateSchema?: { - numUint: number; - numByteSlice: number; - }; - - /** Local state schema */ - localStateSchema?: { - numUint: number; - numByteSlice: number; - }; - - /** Extra program pages */ - extraProgramPages?: number; - - /** Boxes */ - boxes?: Array<{ - appIndex: number | bigint; - name: string; - }>; -}; - -/** - * Union type for all transaction types - */ -export type Transaction = - | PaymentTransaction - | AssetTransferTransaction - | AssetConfigTransaction - | AssetFreezeTransaction - | KeyRegTransaction - | ApplicationTransaction; - -/** - * Multisignature subsignature - */ -export type MultisigSubsignature = { - /** Public key (base64) */ - publicKey?: string; - - /** Signature (base64) */ - signature?: string; -}; - -/** - * Multisignature - */ -export type Multisignature = { - /** Version */ - version: number; - - /** Threshold */ - threshold: number; - - /** Subsignatures */ - subsignatures?: MultisigSubsignature[]; -}; - -/** - * Logic signature - */ -export type LogicSignature = { - /** Logic program (base64) */ - logic: string; - - /** Logic signature arguments (base64 encoded) */ - args?: string[]; - - /** Signature (base64) */ - signature?: string; - - /** Multisignature */ - multisignature?: Multisignature; -}; - -/** - * Signed transaction structure - */ -export type AlgokitSignedTransaction = { - /** The transaction */ - transaction: Transaction; - - /** ED25519 signature (base64) */ - signature?: string; - - /** Multisignature */ - multiSignature?: Multisignature; - - /** Logic signature */ - logicSignature?: LogicSignature; - - /** Auth address (for rekeyed accounts) */ - authAddress?: string; -}; diff --git a/packages/typescript/algod_client/src/models/app-call-logs.ts b/packages/typescript/algod_client/src/models/app-call-logs.ts new file mode 100644 index 000000000..b7b2172b7 --- /dev/null +++ b/packages/typescript/algod_client/src/models/app-call-logs.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * The logged messages from an app call along with the app ID and outer transaction ID. Logs appear in the same order that they were emitted. + */ +export type AppCallLogs = { + /** + * An array of logs + */ + logs: Uint8Array[] + + /** + * The application from which the logs were generated + */ + appId: bigint + + /** + * The transaction ID of the outer app call that lead to these logs + */ + txId: string +} + +export const AppCallLogsMeta: ModelMetadata = { + name: 'AppCallLogs', + kind: 'object', + fields: [ + { + name: 'logs', + wireKey: 'logs', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + { + name: 'appId', + wireKey: 'application-index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'txId', + wireKey: 'txId', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/appcalllogs.ts b/packages/typescript/algod_client/src/models/appcalllogs.ts deleted file mode 100644 index cd1f24d7e..000000000 --- a/packages/typescript/algod_client/src/models/appcalllogs.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * The logged messages from an app call along with the app ID and outer transaction ID. Logs appear in the same order that they were emitted. - */ -export type AppCallLogs = { - /** - * An array of logs - */ - logs: string[]; - - /** - * The application from which the logs were generated - */ - applicationIndex: bigint; - - /** - * The transaction ID of the outer app call that lead to these logs - */ - txId: string; -}; diff --git a/packages/typescript/algod_client/src/models/application-initial-states.ts b/packages/typescript/algod_client/src/models/application-initial-states.ts new file mode 100644 index 000000000..179f74e36 --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-initial-states.ts @@ -0,0 +1,55 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationKvstorage } from './application-kvstorage' +import { ApplicationKvstorageMeta } from './application-kvstorage' + +/** + * An application's initial global/local/box states that were accessed during simulation. + */ +export type ApplicationInitialStates = { + /** + * Application index. + */ + id: bigint + + /** + * An application's initial local states tied to different accounts. + */ + appLocals?: ApplicationKvstorage[] + appGlobals?: ApplicationKvstorage + appBoxes?: ApplicationKvstorage +} + +export const ApplicationInitialStatesMeta: ModelMetadata = { + name: 'ApplicationInitialStates', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appLocals', + wireKey: 'app-locals', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationKvstorageMeta } }, + }, + { + name: 'appGlobals', + wireKey: 'app-globals', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationKvstorageMeta }, + }, + { + name: 'appBoxes', + wireKey: 'app-boxes', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationKvstorageMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-kvstorage.ts b/packages/typescript/algod_client/src/models/application-kvstorage.ts new file mode 100644 index 000000000..1cdc78f70 --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-kvstorage.ts @@ -0,0 +1,39 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AvmKeyValue } from './avm-key-value' +import { AvmKeyValueMeta } from './avm-key-value' + +/** + * An application's global/local/box state. + */ +export type ApplicationKvstorage = { + /** + * Key-Value pairs representing application states. + */ + kvs: AvmKeyValue[] + + /** + * The address of the account associated with the local state. + */ + account?: string +} + +export const ApplicationKvstorageMeta: ModelMetadata = { + name: 'ApplicationKvstorage', + kind: 'object', + fields: [ + { + name: 'kvs', + wireKey: 'kvs', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AvmKeyValueMeta } }, + }, + { + name: 'account', + wireKey: 'account', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-local-reference.ts b/packages/typescript/algod_client/src/models/application-local-reference.ts new file mode 100644 index 000000000..c909a924b --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-local-reference.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * References an account's local state for an application. + */ +export type ApplicationLocalReference = { + /** + * Address of the account with the local state. + */ + account: string + + /** + * Application ID of the local state application. + */ + app: bigint +} + +export const ApplicationLocalReferenceMeta: ModelMetadata = { + name: 'ApplicationLocalReference', + kind: 'object', + fields: [ + { + name: 'account', + wireKey: 'account', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'app', + wireKey: 'app', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-local-state.ts b/packages/typescript/algod_client/src/models/application-local-state.ts new file mode 100644 index 000000000..a04e8a24a --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-local-state.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { TealKeyValueStore } from './teal-key-value-store' +import { TealKeyValueStoreMeta } from './teal-key-value-store' + +/** + * Stores local state associated with an application. + */ +export type ApplicationLocalState = { + /** + * The application which this local state is for. + */ + id: bigint + schema: ApplicationStateSchema + keyValue?: TealKeyValueStore +} + +export const ApplicationLocalStateMeta: ModelMetadata = { + name: 'ApplicationLocalState', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'schema', + wireKey: 'schema', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'keyValue', + wireKey: 'key-value', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TealKeyValueStoreMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-params.ts b/packages/typescript/algod_client/src/models/application-params.ts new file mode 100644 index 000000000..bc28dea8a --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-params.ts @@ -0,0 +1,101 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { TealKeyValueStore } from './teal-key-value-store' +import { TealKeyValueStoreMeta } from './teal-key-value-store' + +/** + * Stores the global information associated with an application. + */ +export type ApplicationParams = { + /** + * The address that created this application. This is the address where the parameters and global state for this application can be found. + */ + creator: string + + /** + * \[approv\] approval program. + */ + approvalProgram: Uint8Array + + /** + * \[clearp\] approval program. + */ + clearStateProgram: Uint8Array + + /** + * \[epp\] the amount of extra program pages available to this app. + */ + extraProgramPages?: number + localStateSchema?: ApplicationStateSchema + globalStateSchema?: ApplicationStateSchema + globalState?: TealKeyValueStore + + /** + * \[v\] the number of updates to the application programs + */ + version?: bigint +} + +export const ApplicationParamsMeta: ModelMetadata = { + name: 'ApplicationParams', + kind: 'object', + fields: [ + { + name: 'creator', + wireKey: 'creator', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'approvalProgram', + wireKey: 'approval-program', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'clearStateProgram', + wireKey: 'clear-state-program', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'extraProgramPages', + wireKey: 'extra-program-pages', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'localStateSchema', + wireKey: 'local-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'globalStateSchema', + wireKey: 'global-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'globalState', + wireKey: 'global-state', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TealKeyValueStoreMeta }, + }, + { + name: 'version', + wireKey: 'version', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-state-operation.ts b/packages/typescript/algod_client/src/models/application-state-operation.ts new file mode 100644 index 000000000..00020f19c --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-state-operation.ts @@ -0,0 +1,71 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AvmValue } from './avm-value' +import { AvmValueMeta } from './avm-value' + +/** + * An operation against an application's global/local/box state. + */ +export type ApplicationStateOperation = { + /** + * Operation type. Value `w` is **write**, `d` is **delete**. + */ + operation: string + + /** + * Type of application state. Value `g` is **global state**, `l` is **local state**, `b` is **boxes**. + */ + appStateType: string + + /** + * The key (name) of the global/local/box state. + */ + key: Uint8Array + newValue?: AvmValue + + /** + * For local state changes, the address of the account associated with the local state. + */ + account?: string +} + +export const ApplicationStateOperationMeta: ModelMetadata = { + name: 'ApplicationStateOperation', + kind: 'object', + fields: [ + { + name: 'operation', + wireKey: 'operation', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'appStateType', + wireKey: 'app-state-type', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'newValue', + wireKey: 'new-value', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AvmValueMeta }, + }, + { + name: 'account', + wireKey: 'account', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application-state-schema.ts b/packages/typescript/algod_client/src/models/application-state-schema.ts new file mode 100644 index 000000000..92c604c1a --- /dev/null +++ b/packages/typescript/algod_client/src/models/application-state-schema.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Specifies maximums on the number of each type that may be stored. + */ +export type ApplicationStateSchema = { + /** + * \[nui\] num of uints. + */ + numUint: number + + /** + * \[nbs\] num of byte slices. + */ + numByteSlice: number +} + +export const ApplicationStateSchemaMeta: ModelMetadata = { + name: 'ApplicationStateSchema', + kind: 'object', + fields: [ + { + name: 'numUint', + wireKey: 'num-uint', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'numByteSlice', + wireKey: 'num-byte-slice', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/application.ts b/packages/typescript/algod_client/src/models/application.ts index da46db81f..c2ae61483 100644 --- a/packages/typescript/algod_client/src/models/application.ts +++ b/packages/typescript/algod_client/src/models/application.ts @@ -1,4 +1,6 @@ -import type { ApplicationParams } from "./index"; +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationParams } from './application-params' +import { ApplicationParamsMeta } from './application-params' /** * Application index and its parameters @@ -7,6 +9,27 @@ export type Application = { /** * \[appidx\] application index. */ - id: bigint; - params: ApplicationParams; -}; + id: bigint + params: ApplicationParams +} + +export const ApplicationMeta: ModelMetadata = { + name: 'Application', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'params', + wireKey: 'params', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => ApplicationParamsMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/applicationinitialstates.ts b/packages/typescript/algod_client/src/models/applicationinitialstates.ts deleted file mode 100644 index 241e0717e..000000000 --- a/packages/typescript/algod_client/src/models/applicationinitialstates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { ApplicationKvstorage } from "./index"; - -/** - * An application's initial global/local/box states that were accessed during simulation. - */ -export type ApplicationInitialStates = { - /** - * Application index. - */ - id: bigint; - - /** - * An application's initial local states tied to different accounts. - */ - appLocals?: ApplicationKvstorage[]; - appGlobals?: ApplicationKvstorage; - appBoxes?: ApplicationKvstorage; -}; diff --git a/packages/typescript/algod_client/src/models/applicationkvstorage.ts b/packages/typescript/algod_client/src/models/applicationkvstorage.ts deleted file mode 100644 index eabe49e7c..000000000 --- a/packages/typescript/algod_client/src/models/applicationkvstorage.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { AvmKeyValue } from "./index"; - -/** - * An application's global/local/box state. - */ -export type ApplicationKvstorage = { - /** - * Key-Value pairs representing application states. - */ - kvs: AvmKeyValue[]; - - /** - * The address of the account associated with the local state. - */ - account?: string; -}; diff --git a/packages/typescript/algod_client/src/models/applicationlocalreference.ts b/packages/typescript/algod_client/src/models/applicationlocalreference.ts deleted file mode 100644 index bdc9d1ed5..000000000 --- a/packages/typescript/algod_client/src/models/applicationlocalreference.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * References an account's local state for an application. - */ -export type ApplicationLocalReference = { - /** - * Address of the account with the local state. - */ - account: string; - - /** - * Application ID of the local state application. - */ - app: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/applicationlocalstate.ts b/packages/typescript/algod_client/src/models/applicationlocalstate.ts deleted file mode 100644 index 6f1de2149..000000000 --- a/packages/typescript/algod_client/src/models/applicationlocalstate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { ApplicationStateSchema, TealKeyValueStore } from "./index"; - -/** - * Stores local state associated with an application. - */ -export type ApplicationLocalState = { - /** - * The application which this local state is for. - */ - id: bigint; - schema: ApplicationStateSchema; - keyValue?: TealKeyValueStore; -}; diff --git a/packages/typescript/algod_client/src/models/applicationparams.ts b/packages/typescript/algod_client/src/models/applicationparams.ts deleted file mode 100644 index aa6ae024a..000000000 --- a/packages/typescript/algod_client/src/models/applicationparams.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { ApplicationStateSchema, TealKeyValueStore } from "./index"; - -/** - * Stores the global information associated with an application. - */ -export type ApplicationParams = { - /** - * The address that created this application. This is the address where the parameters and global state for this application can be found. - */ - creator: string; - - /** - * \[approv\] approval program. - */ - approvalProgram: string; - - /** - * \[clearp\] approval program. - */ - clearStateProgram: string; - - /** - * \[epp\] the amount of extra program pages available to this app. - */ - extraProgramPages?: bigint; - localStateSchema?: ApplicationStateSchema; - globalStateSchema?: ApplicationStateSchema; - globalState?: TealKeyValueStore; - - /** - * \[v\] the number of updates to the application programs - */ - version?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/applicationstateoperation.ts b/packages/typescript/algod_client/src/models/applicationstateoperation.ts deleted file mode 100644 index 70d83cee6..000000000 --- a/packages/typescript/algod_client/src/models/applicationstateoperation.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { AvmValue } from "./index"; - -/** - * An operation against an application's global/local/box state. - */ -export type ApplicationStateOperation = { - /** - * Operation type. Value `w` is **write**, `d` is **delete**. - */ - operation: string; - - /** - * Type of application state. Value `g` is **global state**, `l` is **local state**, `b` is **boxes**. - */ - appStateType: string; - - /** - * The key (name) of the global/local/box state. - */ - key: string; - newValue?: AvmValue; - - /** - * For local state changes, the address of the account associated with the local state. - */ - account?: string; -}; diff --git a/packages/typescript/algod_client/src/models/applicationstateschema.ts b/packages/typescript/algod_client/src/models/applicationstateschema.ts deleted file mode 100644 index 4b9f2fe0f..000000000 --- a/packages/typescript/algod_client/src/models/applicationstateschema.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Specifies maximums on the number of each type that may be stored. - */ -export type ApplicationStateSchema = { - /** - * \[nui\] num of uints. - */ - numUint: bigint; - - /** - * \[nbs\] num of byte slices. - */ - numByteSlice: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/asset-holding-reference.ts b/packages/typescript/algod_client/src/models/asset-holding-reference.ts new file mode 100644 index 000000000..85647851c --- /dev/null +++ b/packages/typescript/algod_client/src/models/asset-holding-reference.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * References an asset held by an account. + */ +export type AssetHoldingReference = { + /** + * Address of the account holding the asset. + */ + account: string + + /** + * Asset ID of the holding. + */ + asset: bigint +} + +export const AssetHoldingReferenceMeta: ModelMetadata = { + name: 'AssetHoldingReference', + kind: 'object', + fields: [ + { + name: 'account', + wireKey: 'account', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'asset', + wireKey: 'asset', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/asset-holding.ts b/packages/typescript/algod_client/src/models/asset-holding.ts new file mode 100644 index 000000000..c6570e12e --- /dev/null +++ b/packages/typescript/algod_client/src/models/asset-holding.ts @@ -0,0 +1,52 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Describes an asset held by an account. + * + * Definition: + * data/basics/userBalance.go : AssetHolding + */ +export type AssetHolding = { + /** + * \[a\] number of units held. + */ + amount: bigint + + /** + * Asset ID of the holding. + */ + assetId: bigint + + /** + * \[f\] whether or not the holding is frozen. + */ + isFrozen: boolean +} + +export const AssetHoldingMeta: ModelMetadata = { + name: 'AssetHolding', + kind: 'object', + fields: [ + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assetId', + wireKey: 'asset-id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'isFrozen', + wireKey: 'is-frozen', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/asset-params.ts b/packages/typescript/algod_client/src/models/asset-params.ts new file mode 100644 index 000000000..a760ebdef --- /dev/null +++ b/packages/typescript/algod_client/src/models/asset-params.ts @@ -0,0 +1,198 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * AssetParams specifies the parameters for an asset. + * + * \[apar\] when part of an AssetConfig transaction. + * + * Definition: + * data/transactions/asset.go : AssetParams + */ +export type AssetParams = { + /** + * \[c\] Address of account used to clawback holdings of this asset. If empty, clawback is not permitted. + */ + clawback?: string + + /** + * The address that created this asset. This is the address where the parameters for this asset can be found, and also the address where unwanted asset units can be sent in the worst case. + */ + creator: string + + /** + * \[dc\] The number of digits to use after the decimal point when displaying this asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. If 2, the base unit of the asset is in hundredths, and so on. This value must be between 0 and 19 (inclusive). + */ + decimals: bigint + + /** + * \[df\] Whether holdings of this asset are frozen by default. + */ + defaultFrozen?: boolean + + /** + * \[f\] Address of account used to freeze holdings of this asset. If empty, freezing is not permitted. + */ + freeze?: string + + /** + * \[m\] Address of account used to manage the keys of this asset and to destroy it. + */ + manager?: string + + /** + * \[am\] A commitment to some unspecified asset metadata. The format of this metadata is up to the application. + */ + metadataHash?: Uint8Array + + /** + * \[an\] Name of this asset, as supplied by the creator. Included only when the asset name is composed of printable utf-8 characters. + */ + name?: string + + /** + * Base64 encoded name of this asset, as supplied by the creator. + */ + nameB64?: Uint8Array + + /** + * \[r\] Address of account holding reserve (non-minted) units of this asset. + */ + reserve?: string + + /** + * \[t\] The total number of units of this asset. + */ + total: bigint + + /** + * \[un\] Name of a unit of this asset, as supplied by the creator. Included only when the name of a unit of this asset is composed of printable utf-8 characters. + */ + unitName?: string + + /** + * Base64 encoded name of a unit of this asset, as supplied by the creator. + */ + unitNameB64?: Uint8Array + + /** + * \[au\] URL where more information about the asset can be retrieved. Included only when the URL is composed of printable utf-8 characters. + */ + url?: string + + /** + * Base64 encoded URL where more information about the asset can be retrieved. + */ + urlB64?: Uint8Array +} + +export const AssetParamsMeta: ModelMetadata = { + name: 'AssetParams', + kind: 'object', + fields: [ + { + name: 'clawback', + wireKey: 'clawback', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'creator', + wireKey: 'creator', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'decimals', + wireKey: 'decimals', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'defaultFrozen', + wireKey: 'default-frozen', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'freeze', + wireKey: 'freeze', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'manager', + wireKey: 'manager', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'metadataHash', + wireKey: 'metadata-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'name', + wireKey: 'name', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nameB64', + wireKey: 'name-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'reserve', + wireKey: 'reserve', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'total', + wireKey: 'total', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'unitName', + wireKey: 'unit-name', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'unitNameB64', + wireKey: 'unit-name-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'url', + wireKey: 'url', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'urlB64', + wireKey: 'url-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/asset.ts b/packages/typescript/algod_client/src/models/asset.ts index fa1558d00..3c12777be 100644 --- a/packages/typescript/algod_client/src/models/asset.ts +++ b/packages/typescript/algod_client/src/models/asset.ts @@ -1,4 +1,6 @@ -import type { AssetParams } from "./index"; +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetParams } from './asset-params' +import { AssetParamsMeta } from './asset-params' /** * Specifies both the unique identifier and the parameters for an asset @@ -7,6 +9,27 @@ export type Asset = { /** * unique asset identifier */ - index: bigint; - params: AssetParams; -}; + index: bigint + params: AssetParams +} + +export const AssetMeta: ModelMetadata = { + name: 'Asset', + kind: 'object', + fields: [ + { + name: 'index', + wireKey: 'index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'params', + wireKey: 'params', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AssetParamsMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/assetholding.ts b/packages/typescript/algod_client/src/models/assetholding.ts deleted file mode 100644 index 39ae6b19f..000000000 --- a/packages/typescript/algod_client/src/models/assetholding.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Describes an asset held by an account. - * - * Definition: - * data/basics/userBalance.go : AssetHolding - */ -export type AssetHolding = { - /** - * \[a\] number of units held. - */ - amount: bigint; - - /** - * Asset ID of the holding. - */ - assetId: bigint; - - /** - * \[f\] whether or not the holding is frozen. - */ - isFrozen: boolean; -}; diff --git a/packages/typescript/algod_client/src/models/assetholdingreference.ts b/packages/typescript/algod_client/src/models/assetholdingreference.ts deleted file mode 100644 index 67b6f116e..000000000 --- a/packages/typescript/algod_client/src/models/assetholdingreference.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * References an asset held by an account. - */ -export type AssetHoldingReference = { - /** - * Address of the account holding the asset. - */ - account: string; - - /** - * Asset ID of the holding. - */ - asset: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/assetparams.ts b/packages/typescript/algod_client/src/models/assetparams.ts deleted file mode 100644 index 907b71fc5..000000000 --- a/packages/typescript/algod_client/src/models/assetparams.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * AssetParams specifies the parameters for an asset. - * - * \[apar\] when part of an AssetConfig transaction. - * - * Definition: - * data/transactions/asset.go : AssetParams - */ -export type AssetParams = { - /** - * \[c\] Address of account used to clawback holdings of this asset. If empty, clawback is not permitted. - */ - clawback?: string; - - /** - * The address that created this asset. This is the address where the parameters for this asset can be found, and also the address where unwanted asset units can be sent in the worst case. - */ - creator: string; - - /** - * \[dc\] The number of digits to use after the decimal point when displaying this asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. If 2, the base unit of the asset is in hundredths, and so on. This value must be between 0 and 19 (inclusive). - */ - decimals: bigint; - - /** - * \[df\] Whether holdings of this asset are frozen by default. - */ - defaultFrozen?: boolean; - - /** - * \[f\] Address of account used to freeze holdings of this asset. If empty, freezing is not permitted. - */ - freeze?: string; - - /** - * \[m\] Address of account used to manage the keys of this asset and to destroy it. - */ - manager?: string; - - /** - * \[am\] A commitment to some unspecified asset metadata. The format of this metadata is up to the application. - */ - metadataHash?: string; - - /** - * \[an\] Name of this asset, as supplied by the creator. Included only when the asset name is composed of printable utf-8 characters. - */ - name?: string; - - /** - * Base64 encoded name of this asset, as supplied by the creator. - */ - nameB64?: string; - - /** - * \[r\] Address of account holding reserve (non-minted) units of this asset. - */ - reserve?: string; - - /** - * \[t\] The total number of units of this asset. - */ - total: bigint; - - /** - * \[un\] Name of a unit of this asset, as supplied by the creator. Included only when the name of a unit of this asset is composed of printable utf-8 characters. - */ - unitName?: string; - - /** - * Base64 encoded name of a unit of this asset, as supplied by the creator. - */ - unitNameB64?: string; - - /** - * \[au\] URL where more information about the asset can be retrieved. Included only when the URL is composed of printable utf-8 characters. - */ - url?: string; - - /** - * Base64 encoded URL where more information about the asset can be retrieved. - */ - urlB64?: string; -}; diff --git a/packages/typescript/algod_client/src/models/avm-key-value.ts b/packages/typescript/algod_client/src/models/avm-key-value.ts new file mode 100644 index 000000000..5d0ef4ce8 --- /dev/null +++ b/packages/typescript/algod_client/src/models/avm-key-value.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AvmValue } from './avm-value' +import { AvmValueMeta } from './avm-value' + +/** + * Represents an AVM key-value pair in an application store. + */ +export type AvmKeyValue = { + key: Uint8Array + value: AvmValue +} + +export const AvmKeyValueMeta: ModelMetadata = { + name: 'AvmKeyValue', + kind: 'object', + fields: [ + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AvmValueMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/avm-value.ts b/packages/typescript/algod_client/src/models/avm-value.ts new file mode 100644 index 000000000..bcb9cc157 --- /dev/null +++ b/packages/typescript/algod_client/src/models/avm-value.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents an AVM value. + */ +export type AvmValue = { + /** + * value type. Value `1` refers to **bytes**, value `2` refers to **uint64** + */ + type: bigint + + /** + * bytes value. + */ + bytes?: string + + /** + * uint value. + */ + uint?: bigint +} + +export const AvmValueMeta: ModelMetadata = { + name: 'AvmValue', + kind: 'object', + fields: [ + { + name: 'type', + wireKey: 'type', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'bytes', + wireKey: 'bytes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'uint', + wireKey: 'uint', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/avmkeyvalue.ts b/packages/typescript/algod_client/src/models/avmkeyvalue.ts deleted file mode 100644 index e505463d0..000000000 --- a/packages/typescript/algod_client/src/models/avmkeyvalue.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AvmValue } from "./index"; - -/** - * Represents an AVM key-value pair in an application store. - */ -export type AvmKeyValue = { - key: string; - value: AvmValue; -}; diff --git a/packages/typescript/algod_client/src/models/avmvalue.ts b/packages/typescript/algod_client/src/models/avmvalue.ts deleted file mode 100644 index 8ab60116b..000000000 --- a/packages/typescript/algod_client/src/models/avmvalue.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Represents an AVM value. - */ -export type AvmValue = { - /** - * value type. Value `1` refers to **bytes**, value `2` refers to **uint64** - */ - type: bigint; - - /** - * bytes value. - */ - bytes?: string; - - /** - * uint value. - */ - uint?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/box-descriptor.ts b/packages/typescript/algod_client/src/models/box-descriptor.ts new file mode 100644 index 000000000..feb16a2a7 --- /dev/null +++ b/packages/typescript/algod_client/src/models/box-descriptor.ts @@ -0,0 +1,25 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Box descriptor describes a Box. + */ +export type BoxDescriptor = { + /** + * Base64 encoded box name + */ + name: Uint8Array +} + +export const BoxDescriptorMeta: ModelMetadata = { + name: 'BoxDescriptor', + kind: 'object', + fields: [ + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/box-reference.ts b/packages/typescript/algod_client/src/models/box-reference.ts new file mode 100644 index 000000000..ec7514cca --- /dev/null +++ b/packages/typescript/algod_client/src/models/box-reference.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * References a box of an application. + */ +export type BoxReference = { + /** + * Application ID which this box belongs to + */ + app: bigint + + /** + * Base64 encoded box name + */ + name: Uint8Array +} + +export const BoxReferenceMeta: ModelMetadata = { + name: 'BoxReference', + kind: 'object', + fields: [ + { + name: 'app', + wireKey: 'app', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/box.ts b/packages/typescript/algod_client/src/models/box.ts index f29851c2a..9c61b15de 100644 --- a/packages/typescript/algod_client/src/models/box.ts +++ b/packages/typescript/algod_client/src/models/box.ts @@ -1,3 +1,5 @@ +import type { ModelMetadata } from '../core/model-runtime' + /** * Box name and its content. */ @@ -5,15 +7,43 @@ export type Box = { /** * The round for which this information is relevant */ - round: bigint; + round: bigint /** * The box name, base64 encoded */ - name: string; + name: Uint8Array /** * The box value, base64 encoded. */ - value: string; -}; + value: Uint8Array +} + +export const BoxMeta: ModelMetadata = { + name: 'Box', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/boxdescriptor.ts b/packages/typescript/algod_client/src/models/boxdescriptor.ts deleted file mode 100644 index ea3dc0516..000000000 --- a/packages/typescript/algod_client/src/models/boxdescriptor.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Box descriptor describes a Box. - */ -export type BoxDescriptor = { - /** - * Base64 encoded box name - */ - name: string; -}; diff --git a/packages/typescript/algod_client/src/models/boxreference.ts b/packages/typescript/algod_client/src/models/boxreference.ts deleted file mode 100644 index 9178b3f9e..000000000 --- a/packages/typescript/algod_client/src/models/boxreference.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * References a box of an application. - */ -export type BoxReference = { - /** - * Application ID which this box belongs to - */ - app: bigint; - - /** - * Base64 encoded box name - */ - name: string; -}; diff --git a/packages/typescript/algod_client/src/models/build-version.ts b/packages/typescript/algod_client/src/models/build-version.ts new file mode 100644 index 000000000..a7d1c36a6 --- /dev/null +++ b/packages/typescript/algod_client/src/models/build-version.ts @@ -0,0 +1,59 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type BuildVersion = { + branch: string + buildNumber: bigint + channel: string + commitHash: string + major: bigint + minor: bigint +} + +export const BuildVersionMeta: ModelMetadata = { + name: 'BuildVersion', + kind: 'object', + fields: [ + { + name: 'branch', + wireKey: 'branch', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'buildNumber', + wireKey: 'build_number', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'channel', + wireKey: 'channel', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'commitHash', + wireKey: 'commit_hash', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'major', + wireKey: 'major', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'minor', + wireKey: 'minor', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/buildversion.ts b/packages/typescript/algod_client/src/models/buildversion.ts deleted file mode 100644 index 9eb20305f..000000000 --- a/packages/typescript/algod_client/src/models/buildversion.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type BuildVersion = { - branch: string; - buildNumber: bigint; - channel: string; - commitHash: string; - major: bigint; - minor: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/debug-settings-prof.ts b/packages/typescript/algod_client/src/models/debug-settings-prof.ts new file mode 100644 index 000000000..8f8ffc9c0 --- /dev/null +++ b/packages/typescript/algod_client/src/models/debug-settings-prof.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * algod mutex and blocking profiling state. + */ +export type DebugSettingsProf = { + /** + * The rate of blocking events. The profiler aims to sample an average of one blocking event per rate nanoseconds spent blocked. To turn off profiling entirely, pass rate 0. + */ + blockRate?: bigint + + /** + * The rate of mutex events. On average 1/rate events are reported. To turn off profiling entirely, pass rate 0 + */ + mutexRate?: bigint +} + +export const DebugSettingsProfMeta: ModelMetadata = { + name: 'DebugSettingsProf', + kind: 'object', + fields: [ + { + name: 'blockRate', + wireKey: 'block-rate', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'mutexRate', + wireKey: 'mutex-rate', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/debugsettingsprof.ts b/packages/typescript/algod_client/src/models/debugsettingsprof.ts deleted file mode 100644 index 532ea51d2..000000000 --- a/packages/typescript/algod_client/src/models/debugsettingsprof.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * algod mutex and blocking profiling state. - */ -export type DebugSettingsProf = { - /** - * The rate of blocking events. The profiler aims to sample an average of one blocking event per rate nanoseconds spent blocked. To turn off profiling entirely, pass rate 0. - */ - blockRate?: bigint; - - /** - * The rate of mutex events. On average 1/rate events are reported. To turn off profiling entirely, pass rate 0 - */ - mutexRate?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/dryrun-request.ts b/packages/typescript/algod_client/src/models/dryrun-request.ts new file mode 100644 index 000000000..fe165addb --- /dev/null +++ b/packages/typescript/algod_client/src/models/dryrun-request.ts @@ -0,0 +1,89 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SignedTransaction } from '@algorandfoundation/algokit-transact' +import type { Account } from './account' +import { AccountMeta } from './account' +import type { Application } from './application' +import { ApplicationMeta } from './application' +import type { DryrunSource } from './dryrun-source' +import { DryrunSourceMeta } from './dryrun-source' + +/** + * Request data type for dryrun endpoint. Given the Transactions and simulated ledger state upload, run TEAL scripts and return debugging information. + */ +export type DryrunRequest = { + txns: SignedTransaction[] + accounts: Account[] + apps: Application[] + + /** + * ProtocolVersion specifies a specific version string to operate under, otherwise whatever the current protocol of the network this algod is running in. + */ + protocolVersion: string + + /** + * Round is available to some TEAL scripts. Defaults to the current round on the network this algod is attached to. + */ + round: bigint + + /** + * LatestTimestamp is available to some TEAL scripts. Defaults to the latest confirmed timestamp this algod is attached to. + */ + latestTimestamp: bigint + sources: DryrunSource[] +} + +export const DryrunRequestMeta: ModelMetadata = { + name: 'DryrunRequest', + kind: 'object', + fields: [ + { + name: 'txns', + wireKey: 'txns', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'codec', codecKey: 'SignedTransaction' } }, + }, + { + name: 'accounts', + wireKey: 'accounts', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountMeta } }, + }, + { + name: 'apps', + wireKey: 'apps', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationMeta } }, + }, + { + name: 'protocolVersion', + wireKey: 'protocol-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'latestTimestamp', + wireKey: 'latest-timestamp', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sources', + wireKey: 'sources', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => DryrunSourceMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/dryrun-source.ts b/packages/typescript/algod_client/src/models/dryrun-source.ts new file mode 100644 index 000000000..dbdd2e5b6 --- /dev/null +++ b/packages/typescript/algod_client/src/models/dryrun-source.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * DryrunSource is TEAL source text that gets uploaded, compiled, and inserted into transactions or application state. + */ +export type DryrunSource = { + /** + * FieldName is what kind of sources this is. If lsig then it goes into the transactions[this.TxnIndex].LogicSig. If approv or clearp it goes into the Approval Program or Clear State Program of application[this.AppIndex]. + */ + fieldName: string + source: string + txnIndex: bigint + appIndex: bigint +} + +export const DryrunSourceMeta: ModelMetadata = { + name: 'DryrunSource', + kind: 'object', + fields: [ + { + name: 'fieldName', + wireKey: 'field-name', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'source', + wireKey: 'source', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'txnIndex', + wireKey: 'txn-index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appIndex', + wireKey: 'app-index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/dryrun-state.ts b/packages/typescript/algod_client/src/models/dryrun-state.ts new file mode 100644 index 000000000..653039fa4 --- /dev/null +++ b/packages/typescript/algod_client/src/models/dryrun-state.ts @@ -0,0 +1,67 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TealValue } from './teal-value' +import { TealValueMeta } from './teal-value' + +/** + * Stores the TEAL eval step data + */ +export type DryrunState = { + /** + * Line number + */ + line: bigint + + /** + * Program counter + */ + pc: bigint + stack: TealValue[] + scratch?: TealValue[] + + /** + * Evaluation error if any + */ + error?: string +} + +export const DryrunStateMeta: ModelMetadata = { + name: 'DryrunState', + kind: 'object', + fields: [ + { + name: 'line', + wireKey: 'line', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'pc', + wireKey: 'pc', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stack', + wireKey: 'stack', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TealValueMeta } }, + }, + { + name: 'scratch', + wireKey: 'scratch', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TealValueMeta } }, + }, + { + name: 'error', + wireKey: 'error', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/dryrun-txn-result.ts b/packages/typescript/algod_client/src/models/dryrun-txn-result.ts new file mode 100644 index 000000000..c87cdf57e --- /dev/null +++ b/packages/typescript/algod_client/src/models/dryrun-txn-result.ts @@ -0,0 +1,123 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountStateDelta } from './account-state-delta' +import { AccountStateDeltaMeta } from './account-state-delta' +import type { DryrunState } from './dryrun-state' +import { DryrunStateMeta } from './dryrun-state' +import type { StateDelta } from './state-delta' +import { StateDeltaMeta } from './state-delta' + +/** + * DryrunTxnResult contains any LogicSig or ApplicationCall program debug information and state updates from a dryrun. + */ +export type DryrunTxnResult = { + /** + * Disassembled program line by line. + */ + disassembly: string[] + + /** + * Disassembled lsig program line by line. + */ + logicSigDisassembly?: string[] + logicSigTrace?: DryrunState[] + logicSigMessages?: string[] + appCallTrace?: DryrunState[] + appCallMessages?: string[] + globalDelta?: StateDelta + localDeltas?: AccountStateDelta[] + logs?: Uint8Array[] + + /** + * Budget added during execution of app call transaction. + */ + budgetAdded?: number + + /** + * Budget consumed during execution of app call transaction. + */ + budgetConsumed?: number +} + +export const DryrunTxnResultMeta: ModelMetadata = { + name: 'DryrunTxnResult', + kind: 'object', + fields: [ + { + name: 'disassembly', + wireKey: 'disassembly', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'logicSigDisassembly', + wireKey: 'logic-sig-disassembly', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'logicSigTrace', + wireKey: 'logic-sig-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => DryrunStateMeta } }, + }, + { + name: 'logicSigMessages', + wireKey: 'logic-sig-messages', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'appCallTrace', + wireKey: 'app-call-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => DryrunStateMeta } }, + }, + { + name: 'appCallMessages', + wireKey: 'app-call-messages', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'globalDelta', + wireKey: 'global-delta', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateDeltaMeta }, + }, + { + name: 'localDeltas', + wireKey: 'local-deltas', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountStateDeltaMeta } }, + }, + { + name: 'logs', + wireKey: 'logs', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + { + name: 'budgetAdded', + wireKey: 'budget-added', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'budgetConsumed', + wireKey: 'budget-consumed', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/dryrunrequest.ts b/packages/typescript/algod_client/src/models/dryrunrequest.ts deleted file mode 100644 index 3d743c6a9..000000000 --- a/packages/typescript/algod_client/src/models/dryrunrequest.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Account, AlgokitSignedTransaction, Application, DryrunSource } from "./index"; - -/** - * Request data type for dryrun endpoint. Given the Transactions and simulated ledger state upload, run TEAL scripts and return debugging information. - */ -export type DryrunRequest = { - txns: AlgokitSignedTransaction[]; - accounts: Account[]; - apps: Application[]; - - /** - * ProtocolVersion specifies a specific version string to operate under, otherwise whatever the current protocol of the network this algod is running in. - */ - protocolVersion: string; - - /** - * Round is available to some TEAL scripts. Defaults to the current round on the network this algod is attached to. - */ - round: bigint; - - /** - * LatestTimestamp is available to some TEAL scripts. Defaults to the latest confirmed timestamp this algod is attached to. - */ - latestTimestamp: bigint; - sources: DryrunSource[]; -}; diff --git a/packages/typescript/algod_client/src/models/dryrunsource.ts b/packages/typescript/algod_client/src/models/dryrunsource.ts deleted file mode 100644 index 7b4387a88..000000000 --- a/packages/typescript/algod_client/src/models/dryrunsource.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * DryrunSource is TEAL source text that gets uploaded, compiled, and inserted into transactions or application state. - */ -export type DryrunSource = { - /** - * FieldName is what kind of sources this is. If lsig then it goes into the transactions[this.TxnIndex].LogicSig. If approv or clearp it goes into the Approval Program or Clear State Program of application[this.AppIndex]. - */ - fieldName: string; - source: string; - txnIndex: bigint; - appIndex: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/dryrunstate.ts b/packages/typescript/algod_client/src/models/dryrunstate.ts deleted file mode 100644 index 1155daea1..000000000 --- a/packages/typescript/algod_client/src/models/dryrunstate.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { TealValue } from "./index"; - -/** - * Stores the TEAL eval step data - */ -export type DryrunState = { - /** - * Line number - */ - line: bigint; - - /** - * Program counter - */ - pc: bigint; - stack: TealValue[]; - scratch?: TealValue[]; - - /** - * Evaluation error if any - */ - error?: string; -}; diff --git a/packages/typescript/algod_client/src/models/dryruntxnresult.ts b/packages/typescript/algod_client/src/models/dryruntxnresult.ts deleted file mode 100644 index e0f0f5568..000000000 --- a/packages/typescript/algod_client/src/models/dryruntxnresult.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { AccountStateDelta, DryrunState, StateDelta } from "./index"; - -/** - * DryrunTxnResult contains any LogicSig or ApplicationCall program debug information and state updates from a dryrun. - */ -export type DryrunTxnResult = { - /** - * Disassembled program line by line. - */ - disassembly: string[]; - - /** - * Disassembled lsig program line by line. - */ - logicSigDisassembly?: string[]; - logicSigTrace?: DryrunState[]; - logicSigMessages?: string[]; - appCallTrace?: DryrunState[]; - appCallMessages?: string[]; - globalDelta?: StateDelta; - localDeltas?: AccountStateDelta[]; - logs?: string[]; - - /** - * Budget added during execution of app call transaction. - */ - budgetAdded?: bigint; - - /** - * Budget consumed during execution of app call transaction. - */ - budgetConsumed?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/error-response.ts b/packages/typescript/algod_client/src/models/error-response.ts new file mode 100644 index 000000000..cfa8d98aa --- /dev/null +++ b/packages/typescript/algod_client/src/models/error-response.ts @@ -0,0 +1,30 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * An error response with optional data field. + */ +export type ErrorResponse = { + data?: Record + message: string +} + +export const ErrorResponseMeta: ModelMetadata = { + name: 'ErrorResponse', + kind: 'object', + fields: [ + { + name: 'data', + wireKey: 'data', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'message', + wireKey: 'message', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/errorresponse.ts b/packages/typescript/algod_client/src/models/errorresponse.ts deleted file mode 100644 index 90666a66e..000000000 --- a/packages/typescript/algod_client/src/models/errorresponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * An error response with optional data field. - */ -export type ErrorResponse = { - data?: {}; - message: string; -}; diff --git a/packages/typescript/algod_client/src/models/eval-delta-key-value.ts b/packages/typescript/algod_client/src/models/eval-delta-key-value.ts new file mode 100644 index 000000000..80097c504 --- /dev/null +++ b/packages/typescript/algod_client/src/models/eval-delta-key-value.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { EvalDelta } from './eval-delta' +import { EvalDeltaMeta } from './eval-delta' + +/** + * Key-value pairs for StateDelta. + */ +export type EvalDeltaKeyValue = { + key: string + value: EvalDelta +} + +export const EvalDeltaKeyValueMeta: ModelMetadata = { + name: 'EvalDeltaKeyValue', + kind: 'object', + fields: [ + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => EvalDeltaMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/eval-delta.ts b/packages/typescript/algod_client/src/models/eval-delta.ts new file mode 100644 index 000000000..e3bfc1a54 --- /dev/null +++ b/packages/typescript/algod_client/src/models/eval-delta.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents a TEAL value delta. + */ +export type EvalDelta = { + /** + * \[at\] delta action. + */ + action: number + + /** + * \[bs\] bytes value. + */ + bytes?: string + + /** + * \[ui\] uint value. + */ + uint?: bigint +} + +export const EvalDeltaMeta: ModelMetadata = { + name: 'EvalDelta', + kind: 'object', + fields: [ + { + name: 'action', + wireKey: 'action', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'bytes', + wireKey: 'bytes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'uint', + wireKey: 'uint', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/evaldelta.ts b/packages/typescript/algod_client/src/models/evaldelta.ts deleted file mode 100644 index 0fc979b44..000000000 --- a/packages/typescript/algod_client/src/models/evaldelta.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Represents a TEAL value delta. - */ -export type EvalDelta = { - /** - * \[at\] delta action. - */ - action: bigint; - - /** - * \[bs\] bytes value. - */ - bytes?: string; - - /** - * \[ui\] uint value. - */ - uint?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/evaldeltakeyvalue.ts b/packages/typescript/algod_client/src/models/evaldeltakeyvalue.ts deleted file mode 100644 index 7a56299ea..000000000 --- a/packages/typescript/algod_client/src/models/evaldeltakeyvalue.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { EvalDelta } from "./index"; - -/** - * Key-value pairs for StateDelta. - */ -export type EvalDeltaKeyValue = { - key: string; - value: EvalDelta; -}; diff --git a/packages/typescript/algod_client/src/models/genesis-allocation.ts b/packages/typescript/algod_client/src/models/genesis-allocation.ts new file mode 100644 index 000000000..e96e23fcc --- /dev/null +++ b/packages/typescript/algod_client/src/models/genesis-allocation.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GenesisAllocation = { + addr: string + comment: string + state: { + algo: bigint + onl: bigint + sel?: string + stprf?: string + vote?: string + voteKd?: bigint + voteFst?: bigint + voteLst?: bigint + } +} + +export const GenesisAllocationMeta: ModelMetadata = { + name: 'GenesisAllocation', + kind: 'object', + fields: [ + { + name: 'addr', + wireKey: 'addr', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'comment', + wireKey: 'comment', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'state', + wireKey: 'state', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/genesis.ts b/packages/typescript/algod_client/src/models/genesis.ts index 888421e64..0b0e12ec7 100644 --- a/packages/typescript/algod_client/src/models/genesis.ts +++ b/packages/typescript/algod_client/src/models/genesis.ts @@ -1,13 +1,85 @@ -import type { GenesisAllocation } from "./index"; +import type { ModelMetadata } from '../core/model-runtime' +import type { GenesisAllocation } from './genesis-allocation' +import { GenesisAllocationMeta } from './genesis-allocation' export type Genesis = { - alloc: GenesisAllocation[]; - comment?: string; - devmode?: boolean; - fees: string; - id: string; - network: string; - proto: string; - rwd: string; - timestamp: bigint; -}; + alloc: GenesisAllocation[] + comment?: string + devmode?: boolean + fees: string + id: string + network: string + proto: string + rwd: string + timestamp: bigint +} + +export const GenesisMeta: ModelMetadata = { + name: 'Genesis', + kind: 'object', + fields: [ + { + name: 'alloc', + wireKey: 'alloc', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => GenesisAllocationMeta } }, + }, + { + name: 'comment', + wireKey: 'comment', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'devmode', + wireKey: 'devmode', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'fees', + wireKey: 'fees', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'network', + wireKey: 'network', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'proto', + wireKey: 'proto', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rwd', + wireKey: 'rwd', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'timestamp', + wireKey: 'timestamp', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/genesisallocation.ts b/packages/typescript/algod_client/src/models/genesisallocation.ts deleted file mode 100644 index f9a65f3f5..000000000 --- a/packages/typescript/algod_client/src/models/genesisallocation.ts +++ /dev/null @@ -1,14 +0,0 @@ -export type GenesisAllocation = { - addr: string; - comment: string; - state: { - algo: bigint; - onl: bigint; - sel?: string; - stprf?: string; - vote?: string; - voteKd?: bigint; - voteFst?: bigint; - voteLst?: bigint; - }; -}; diff --git a/packages/typescript/algod_client/src/models/get-application-boxes.ts b/packages/typescript/algod_client/src/models/get-application-boxes.ts new file mode 100644 index 000000000..fbb23b781 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-application-boxes.ts @@ -0,0 +1,21 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { BoxDescriptor } from './box-descriptor' +import { BoxDescriptorMeta } from './box-descriptor' + +export type GetApplicationBoxes = { + boxes: BoxDescriptor[] +} + +export const GetApplicationBoxesMeta: ModelMetadata = { + name: 'GetApplicationBoxes', + kind: 'object', + fields: [ + { + name: 'boxes', + wireKey: 'boxes', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => BoxDescriptorMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-block-hash.ts b/packages/typescript/algod_client/src/models/get-block-hash.ts new file mode 100644 index 000000000..2bf6b6147 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-block-hash.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GetBlockHash = { + /** + * Block header hash. + */ + blockHash: string +} + +export const GetBlockHashMeta: ModelMetadata = { + name: 'GetBlockHash', + kind: 'object', + fields: [ + { + name: 'blockHash', + wireKey: 'blockHash', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-block-logs.ts b/packages/typescript/algod_client/src/models/get-block-logs.ts new file mode 100644 index 000000000..b54191360 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-block-logs.ts @@ -0,0 +1,21 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AppCallLogs } from './app-call-logs' +import { AppCallLogsMeta } from './app-call-logs' + +export type GetBlockLogs = { + logs: AppCallLogs[] +} + +export const GetBlockLogsMeta: ModelMetadata = { + name: 'GetBlockLogs', + kind: 'object', + fields: [ + { + name: 'logs', + wireKey: 'logs', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AppCallLogsMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-block-time-stamp-offset.ts b/packages/typescript/algod_client/src/models/get-block-time-stamp-offset.ts new file mode 100644 index 000000000..24f101b0f --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-block-time-stamp-offset.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GetBlockTimeStampOffset = { + /** + * Timestamp offset in seconds. + */ + offset: bigint +} + +export const GetBlockTimeStampOffsetMeta: ModelMetadata = { + name: 'GetBlockTimeStampOffset', + kind: 'object', + fields: [ + { + name: 'offset', + wireKey: 'offset', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-block-txids.ts b/packages/typescript/algod_client/src/models/get-block-txids.ts new file mode 100644 index 000000000..8382d6650 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-block-txids.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GetBlockTxids = { + /** + * Block transaction IDs. + */ + blockTxids: string[] +} + +export const GetBlockTxidsMeta: ModelMetadata = { + name: 'GetBlockTxids', + kind: 'object', + fields: [ + { + name: 'blockTxids', + wireKey: 'blockTxids', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-block.ts b/packages/typescript/algod_client/src/models/get-block.ts new file mode 100644 index 000000000..7a9bdf84f --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-block.ts @@ -0,0 +1,34 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GetBlock = { + /** + * Block header data. + */ + block: Record + + /** + * Optional certificate object. This is only included when the format is set to message pack. + */ + cert?: Record +} + +export const GetBlockMeta: ModelMetadata = { + name: 'GetBlock', + kind: 'object', + fields: [ + { + name: 'block', + wireKey: 'block', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'cert', + wireKey: 'cert', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-pending-transactions-by-address.ts b/packages/typescript/algod_client/src/models/get-pending-transactions-by-address.ts new file mode 100644 index 000000000..f96c63251 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-pending-transactions-by-address.ts @@ -0,0 +1,38 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SignedTransaction } from '@algorandfoundation/algokit-transact' + +/** + * PendingTransactions is an array of signed transactions exactly as they were submitted. + */ +export type GetPendingTransactionsByAddress = { + /** + * An array of signed transaction objects. + */ + topTransactions: SignedTransaction[] + + /** + * Total number of transactions in the pool. + */ + totalTransactions: bigint +} + +export const GetPendingTransactionsByAddressMeta: ModelMetadata = { + name: 'GetPendingTransactionsByAddress', + kind: 'object', + fields: [ + { + name: 'topTransactions', + wireKey: 'top-transactions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'codec', codecKey: 'SignedTransaction' } }, + }, + { + name: 'totalTransactions', + wireKey: 'total-transactions', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-pending-transactions.ts b/packages/typescript/algod_client/src/models/get-pending-transactions.ts new file mode 100644 index 000000000..f1cbb8688 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-pending-transactions.ts @@ -0,0 +1,38 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SignedTransaction } from '@algorandfoundation/algokit-transact' + +/** + * PendingTransactions is an array of signed transactions exactly as they were submitted. + */ +export type GetPendingTransactions = { + /** + * An array of signed transaction objects. + */ + topTransactions: SignedTransaction[] + + /** + * Total number of transactions in the pool. + */ + totalTransactions: bigint +} + +export const GetPendingTransactionsMeta: ModelMetadata = { + name: 'GetPendingTransactions', + kind: 'object', + fields: [ + { + name: 'topTransactions', + wireKey: 'top-transactions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'codec', codecKey: 'SignedTransaction' } }, + }, + { + name: 'totalTransactions', + wireKey: 'total-transactions', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-status.ts b/packages/typescript/algod_client/src/models/get-status.ts new file mode 100644 index 000000000..1ae84ff40 --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-status.ts @@ -0,0 +1,325 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * NodeStatus contains the information about a node status + */ +export type GetStatus = { + /** + * CatchupTime in nanoseconds + */ + catchupTime: bigint + + /** + * LastRound indicates the last round seen + */ + lastRound: bigint + + /** + * LastVersion indicates the last consensus version supported + */ + lastVersion: string + + /** + * NextVersion of consensus protocol to use + */ + nextVersion: string + + /** + * NextVersionRound is the round at which the next consensus version will apply + */ + nextVersionRound: bigint + + /** + * NextVersionSupported indicates whether the next consensus version is supported by this node + */ + nextVersionSupported: boolean + + /** + * StoppedAtUnsupportedRound indicates that the node does not support the new rounds and has stopped making progress + */ + stoppedAtUnsupportedRound: boolean + + /** + * TimeSinceLastRound in nanoseconds + */ + timeSinceLastRound: bigint + + /** + * The last catchpoint seen by the node + */ + lastCatchpoint?: string + + /** + * The current catchpoint that is being caught up to + */ + catchpoint?: string + + /** + * The total number of accounts included in the current catchpoint + */ + catchpointTotalAccounts?: bigint + + /** + * The number of accounts from the current catchpoint that have been processed so far as part of the catchup + */ + catchpointProcessedAccounts?: bigint + + /** + * The number of accounts from the current catchpoint that have been verified so far as part of the catchup + */ + catchpointVerifiedAccounts?: bigint + + /** + * The total number of key-values (KVs) included in the current catchpoint + */ + catchpointTotalKvs?: bigint + + /** + * The number of key-values (KVs) from the current catchpoint that have been processed so far as part of the catchup + */ + catchpointProcessedKvs?: bigint + + /** + * The number of key-values (KVs) from the current catchpoint that have been verified so far as part of the catchup + */ + catchpointVerifiedKvs?: bigint + + /** + * The total number of blocks that are required to complete the current catchpoint catchup + */ + catchpointTotalBlocks?: bigint + + /** + * The number of blocks that have already been obtained by the node as part of the catchup + */ + catchpointAcquiredBlocks?: bigint + + /** + * Upgrade delay + */ + upgradeDelay?: bigint + + /** + * This node's upgrade vote + */ + upgradeNodeVote?: boolean + + /** + * Yes votes required for consensus upgrade + */ + upgradeVotesRequired?: bigint + + /** + * Total votes cast for consensus upgrade + */ + upgradeVotes?: bigint + + /** + * Yes votes cast for consensus upgrade + */ + upgradeYesVotes?: bigint + + /** + * No votes cast for consensus upgrade + */ + upgradeNoVotes?: bigint + + /** + * Next protocol round + */ + upgradeNextProtocolVoteBefore?: bigint + + /** + * Total voting rounds for current upgrade + */ + upgradeVoteRounds?: bigint +} + +export const GetStatusMeta: ModelMetadata = { + name: 'GetStatus', + kind: 'object', + fields: [ + { + name: 'catchupTime', + wireKey: 'catchup-time', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastRound', + wireKey: 'last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastVersion', + wireKey: 'last-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersion', + wireKey: 'next-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersionRound', + wireKey: 'next-version-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersionSupported', + wireKey: 'next-version-supported', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stoppedAtUnsupportedRound', + wireKey: 'stopped-at-unsupported-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'timeSinceLastRound', + wireKey: 'time-since-last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastCatchpoint', + wireKey: 'last-catchpoint', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'catchpoint', + wireKey: 'catchpoint', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'catchpointTotalAccounts', + wireKey: 'catchpoint-total-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointProcessedAccounts', + wireKey: 'catchpoint-processed-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointVerifiedAccounts', + wireKey: 'catchpoint-verified-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointTotalKvs', + wireKey: 'catchpoint-total-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointProcessedKvs', + wireKey: 'catchpoint-processed-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointVerifiedKvs', + wireKey: 'catchpoint-verified-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointTotalBlocks', + wireKey: 'catchpoint-total-blocks', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointAcquiredBlocks', + wireKey: 'catchpoint-acquired-blocks', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'upgradeDelay', + wireKey: 'upgrade-delay', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNodeVote', + wireKey: 'upgrade-node-vote', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVotesRequired', + wireKey: 'upgrade-votes-required', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVotes', + wireKey: 'upgrade-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeYesVotes', + wireKey: 'upgrade-yes-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNoVotes', + wireKey: 'upgrade-no-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNextProtocolVoteBefore', + wireKey: 'upgrade-next-protocol-vote-before', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVoteRounds', + wireKey: 'upgrade-vote-rounds', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-supply.ts b/packages/typescript/algod_client/src/models/get-supply.ts new file mode 100644 index 000000000..149f567fd --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-supply.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Supply represents the current supply of MicroAlgos in the system + */ +export type GetSupply = { + /** + * Round + */ + currentRound: bigint + + /** + * OnlineMoney + */ + onlineMoney: bigint + + /** + * TotalMoney + */ + totalMoney: bigint +} + +export const GetSupplyMeta: ModelMetadata = { + name: 'GetSupply', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current_round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'onlineMoney', + wireKey: 'online-money', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'totalMoney', + wireKey: 'total-money', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-sync-round.ts b/packages/typescript/algod_client/src/models/get-sync-round.ts new file mode 100644 index 000000000..0c4776dfe --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-sync-round.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type GetSyncRound = { + /** + * The minimum sync round for the ledger. + */ + round: bigint +} + +export const GetSyncRoundMeta: ModelMetadata = { + name: 'GetSyncRound', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/get-transaction-group-ledger-state-deltas-for-round.ts b/packages/typescript/algod_client/src/models/get-transaction-group-ledger-state-deltas-for-round.ts new file mode 100644 index 000000000..e661cf7ad --- /dev/null +++ b/packages/typescript/algod_client/src/models/get-transaction-group-ledger-state-deltas-for-round.ts @@ -0,0 +1,21 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { LedgerStateDeltaForTransactionGroup } from './ledger-state-delta-for-transaction-group' +import { LedgerStateDeltaForTransactionGroupMeta } from './ledger-state-delta-for-transaction-group' + +export type GetTransactionGroupLedgerStateDeltasForRound = { + deltas: LedgerStateDeltaForTransactionGroup[] +} + +export const GetTransactionGroupLedgerStateDeltasForRoundMeta: ModelMetadata = { + name: 'GetTransactionGroupLedgerStateDeltasForRound', + kind: 'object', + fields: [ + { + name: 'deltas', + wireKey: 'Deltas', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => LedgerStateDeltaForTransactionGroupMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/getapplicationboxes.ts b/packages/typescript/algod_client/src/models/getapplicationboxes.ts deleted file mode 100644 index 5d21d0139..000000000 --- a/packages/typescript/algod_client/src/models/getapplicationboxes.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { BoxDescriptor } from "./index"; - -export type GetApplicationBoxes = { - boxes: BoxDescriptor[]; -}; diff --git a/packages/typescript/algod_client/src/models/getblock.ts b/packages/typescript/algod_client/src/models/getblock.ts deleted file mode 100644 index 19887c371..000000000 --- a/packages/typescript/algod_client/src/models/getblock.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type GetBlock = { - /** - * Block header data. - */ - block: {}; - - /** - * Optional certificate object. This is only included when the format is set to message pack. - */ - cert?: {}; -}; diff --git a/packages/typescript/algod_client/src/models/getblockhash.ts b/packages/typescript/algod_client/src/models/getblockhash.ts deleted file mode 100644 index 9f76c369c..000000000 --- a/packages/typescript/algod_client/src/models/getblockhash.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type GetBlockHash = { - /** - * Block header hash. - */ - blockHash: string; -}; diff --git a/packages/typescript/algod_client/src/models/getblocklogs.ts b/packages/typescript/algod_client/src/models/getblocklogs.ts deleted file mode 100644 index 486b1785a..000000000 --- a/packages/typescript/algod_client/src/models/getblocklogs.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { AppCallLogs } from "./index"; - -export type GetBlockLogs = { - logs: AppCallLogs[]; -}; diff --git a/packages/typescript/algod_client/src/models/getblocktimestampoffset.ts b/packages/typescript/algod_client/src/models/getblocktimestampoffset.ts deleted file mode 100644 index 9b5442390..000000000 --- a/packages/typescript/algod_client/src/models/getblocktimestampoffset.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type GetBlockTimeStampOffset = { - /** - * Timestamp offset in seconds. - */ - offset: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/getblocktxids.ts b/packages/typescript/algod_client/src/models/getblocktxids.ts deleted file mode 100644 index 21ead88d4..000000000 --- a/packages/typescript/algod_client/src/models/getblocktxids.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type GetBlockTxids = { - /** - * Block transaction IDs. - */ - blockTxids: string[]; -}; diff --git a/packages/typescript/algod_client/src/models/getpendingtransactions.ts b/packages/typescript/algod_client/src/models/getpendingtransactions.ts deleted file mode 100644 index 9eaa2676d..000000000 --- a/packages/typescript/algod_client/src/models/getpendingtransactions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { AlgokitSignedTransaction } from "./index"; - -/** - * PendingTransactions is an array of signed transactions exactly as they were submitted. - */ -export type GetPendingTransactions = { - /** - * An array of signed transaction objects. - */ - topTransactions: AlgokitSignedTransaction[]; - - /** - * Total number of transactions in the pool. - */ - totalTransactions: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/getpendingtransactionsbyaddress.ts b/packages/typescript/algod_client/src/models/getpendingtransactionsbyaddress.ts deleted file mode 100644 index 806d2b9ed..000000000 --- a/packages/typescript/algod_client/src/models/getpendingtransactionsbyaddress.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { AlgokitSignedTransaction } from "./index"; - -/** - * PendingTransactions is an array of signed transactions exactly as they were submitted. - */ -export type GetPendingTransactionsByAddress = { - /** - * An array of signed transaction objects. - */ - topTransactions: AlgokitSignedTransaction[]; - - /** - * Total number of transactions in the pool. - */ - totalTransactions: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/getstatus.ts b/packages/typescript/algod_client/src/models/getstatus.ts deleted file mode 100644 index 1ff7ebb7d..000000000 --- a/packages/typescript/algod_client/src/models/getstatus.ts +++ /dev/null @@ -1,134 +0,0 @@ -/** - * NodeStatus contains the information about a node status - */ -export type GetStatus = { - /** - * CatchupTime in nanoseconds - */ - catchupTime: bigint; - - /** - * LastRound indicates the last round seen - */ - lastRound: bigint; - - /** - * LastVersion indicates the last consensus version supported - */ - lastVersion: string; - - /** - * NextVersion of consensus protocol to use - */ - nextVersion: string; - - /** - * NextVersionRound is the round at which the next consensus version will apply - */ - nextVersionRound: bigint; - - /** - * NextVersionSupported indicates whether the next consensus version is supported by this node - */ - nextVersionSupported: boolean; - - /** - * StoppedAtUnsupportedRound indicates that the node does not support the new rounds and has stopped making progress - */ - stoppedAtUnsupportedRound: boolean; - - /** - * TimeSinceLastRound in nanoseconds - */ - timeSinceLastRound: bigint; - - /** - * The last catchpoint seen by the node - */ - lastCatchpoint?: string; - - /** - * The current catchpoint that is being caught up to - */ - catchpoint?: string; - - /** - * The total number of accounts included in the current catchpoint - */ - catchpointTotalAccounts?: bigint; - - /** - * The number of accounts from the current catchpoint that have been processed so far as part of the catchup - */ - catchpointProcessedAccounts?: bigint; - - /** - * The number of accounts from the current catchpoint that have been verified so far as part of the catchup - */ - catchpointVerifiedAccounts?: bigint; - - /** - * The total number of key-values (KVs) included in the current catchpoint - */ - catchpointTotalKvs?: bigint; - - /** - * The number of key-values (KVs) from the current catchpoint that have been processed so far as part of the catchup - */ - catchpointProcessedKvs?: bigint; - - /** - * The number of key-values (KVs) from the current catchpoint that have been verified so far as part of the catchup - */ - catchpointVerifiedKvs?: bigint; - - /** - * The total number of blocks that are required to complete the current catchpoint catchup - */ - catchpointTotalBlocks?: bigint; - - /** - * The number of blocks that have already been obtained by the node as part of the catchup - */ - catchpointAcquiredBlocks?: bigint; - - /** - * Upgrade delay - */ - upgradeDelay?: bigint; - - /** - * This node's upgrade vote - */ - upgradeNodeVote?: boolean; - - /** - * Yes votes required for consensus upgrade - */ - upgradeVotesRequired?: bigint; - - /** - * Total votes cast for consensus upgrade - */ - upgradeVotes?: bigint; - - /** - * Yes votes cast for consensus upgrade - */ - upgradeYesVotes?: bigint; - - /** - * No votes cast for consensus upgrade - */ - upgradeNoVotes?: bigint; - - /** - * Next protocol round - */ - upgradeNextProtocolVoteBefore?: bigint; - - /** - * Total voting rounds for current upgrade - */ - upgradeVoteRounds?: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/getsupply.ts b/packages/typescript/algod_client/src/models/getsupply.ts deleted file mode 100644 index 5c23c20fc..000000000 --- a/packages/typescript/algod_client/src/models/getsupply.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Supply represents the current supply of MicroAlgos in the system - */ -export type GetSupply = { - /** - * Round - */ - currentRound: bigint; - - /** - * OnlineMoney - */ - onlineMoney: bigint; - - /** - * TotalMoney - */ - totalMoney: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/getsyncround.ts b/packages/typescript/algod_client/src/models/getsyncround.ts deleted file mode 100644 index 7e0e2f85b..000000000 --- a/packages/typescript/algod_client/src/models/getsyncround.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type GetSyncRound = { - /** - * The minimum sync round for the ledger. - */ - round: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/gettransactiongroupledgerstatedeltasforround.ts b/packages/typescript/algod_client/src/models/gettransactiongroupledgerstatedeltasforround.ts deleted file mode 100644 index 7d5637ea5..000000000 --- a/packages/typescript/algod_client/src/models/gettransactiongroupledgerstatedeltasforround.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { LedgerStateDeltaForTransactionGroup } from "./index"; - -export type GetTransactionGroupLedgerStateDeltasForRound = { - deltas: LedgerStateDeltaForTransactionGroup[]; -}; diff --git a/packages/typescript/algod_client/src/models/index.ts b/packages/typescript/algod_client/src/models/index.ts index cdff0a52e..d0393371e 100644 --- a/packages/typescript/algod_client/src/models/index.ts +++ b/packages/typescript/algod_client/src/models/index.ts @@ -1,86 +1,166 @@ -// Barrel file for models -export type { GenesisAllocation } from "./genesisallocation"; -export type { Genesis } from "./genesis"; -export type { LedgerStateDelta } from "./ledgerstatedelta"; -export type { LedgerStateDeltaForTransactionGroup } from "./ledgerstatedeltafortransactiongroup"; -export type { Account } from "./account"; -export type { AccountAssetHolding } from "./accountassetholding"; -export type { AccountParticipation } from "./accountparticipation"; -export type { Asset } from "./asset"; -export type { AssetHolding } from "./assetholding"; -export type { AssetParams } from "./assetparams"; -export type { AssetHoldingReference } from "./assetholdingreference"; -export type { ApplicationLocalReference } from "./applicationlocalreference"; -export type { ApplicationStateSchema } from "./applicationstateschema"; -export type { ApplicationLocalState } from "./applicationlocalstate"; -export type { ParticipationKey } from "./participationkey"; -export type { TealKeyValueStore } from "./tealkeyvaluestore"; -export type { TealKeyValue } from "./tealkeyvalue"; -export type { TealValue } from "./tealvalue"; -export type { AvmValue } from "./avmvalue"; -export type { AvmKeyValue } from "./avmkeyvalue"; -export type { StateDelta } from "./statedelta"; -export type { AccountStateDelta } from "./accountstatedelta"; -export type { EvalDeltaKeyValue } from "./evaldeltakeyvalue"; -export type { EvalDelta } from "./evaldelta"; -export type { Application } from "./application"; -export type { ApplicationParams } from "./applicationparams"; -export type { DryrunState } from "./dryrunstate"; -export type { DryrunTxnResult } from "./dryruntxnresult"; -export type { ErrorResponse } from "./errorresponse"; -export type { DryrunRequest } from "./dryrunrequest"; -export type { DryrunSource } from "./dryrunsource"; -export type { SimulateRequest } from "./simulaterequest"; -export type { SimulateRequestTransactionGroup } from "./simulaterequesttransactiongroup"; -export type { SimulateTraceConfig } from "./simulatetraceconfig"; -export type { Box } from "./box"; -export type { BoxDescriptor } from "./boxdescriptor"; -export type { BoxReference } from "./boxreference"; -export type { Version } from "./version"; -export type { DebugSettingsProf } from "./debugsettingsprof"; -export type { BuildVersion } from "./buildversion"; -export type { PendingTransactionResponse } from "./pendingtransactionresponse"; -export type { SimulateTransactionGroupResult } from "./simulatetransactiongroupresult"; -export type { SimulateTransactionResult } from "./simulatetransactionresult"; -export type { StateProof } from "./stateproof"; -export type { LightBlockHeaderProof } from "./lightblockheaderproof"; -export type { StateProofMessage } from "./stateproofmessage"; -export type { SimulationEvalOverrides } from "./simulationevaloverrides"; -export type { ScratchChange } from "./scratchchange"; -export type { ApplicationStateOperation } from "./applicationstateoperation"; -export type { ApplicationKvstorage } from "./applicationkvstorage"; -export type { ApplicationInitialStates } from "./applicationinitialstates"; -export type { SimulationOpcodeTraceUnit } from "./simulationopcodetraceunit"; -export type { SimulationTransactionExecTrace } from "./simulationtransactionexectrace"; -export type { SimulateUnnamedResourcesAccessed } from "./simulateunnamedresourcesaccessed"; -export type { SimulateInitialStates } from "./simulateinitialstates"; -export type { AppCallLogs } from "./appcalllogs"; -export type { TransactionProof } from "./transactionproof"; -export type { AccountAssetInformation } from "./accountassetinformation"; -export type { AccountAssetsInformation } from "./accountassetsinformation"; -export type { AccountApplicationInformation } from "./accountapplicationinformation"; -export type { GetPendingTransactionsByAddress } from "./getpendingtransactionsbyaddress"; -export type { GetBlock } from "./getblock"; -export type { GetBlockTxids } from "./getblocktxids"; -export type { GetBlockHash } from "./getblockhash"; -export type { GetBlockLogs } from "./getblocklogs"; -export type { GetSupply } from "./getsupply"; -export type { AddParticipationKey } from "./addparticipationkey"; -export type { ShutdownNode } from "./shutdownnode"; -export type { GetStatus } from "./getstatus"; -export type { WaitForBlock } from "./waitforblock"; -export type { RawTransaction } from "./rawtransaction"; -export type { SimulateTransaction } from "./simulatetransaction"; -export type { TransactionParams } from "./transactionparams"; -export type { GetPendingTransactions } from "./getpendingtransactions"; -export type { GetTransactionGroupLedgerStateDeltasForRound } from "./gettransactiongroupledgerstatedeltasforround"; -export type { GetApplicationBoxes } from "./getapplicationboxes"; -export type { GetSyncRound } from "./getsyncround"; -export type { TealCompile } from "./tealcompile"; -export type { TealDisassemble } from "./tealdisassemble"; -export type { StartCatchup } from "./startcatchup"; -export type { AbortCatchup } from "./abortcatchup"; -export type { TealDryrun } from "./tealdryrun"; -export type { GetBlockTimeStampOffset } from "./getblocktimestampoffset"; -// TODO(utils-ts): Remove this export when utils-ts provides SignedTransaction types -export type { AlgokitSignedTransaction } from "./algokitsignedtransaction"; +export type { GenesisAllocation } from './genesis-allocation' +export { GenesisAllocationMeta } from './genesis-allocation' +export type { Genesis } from './genesis' +export { GenesisMeta } from './genesis' +export type { LedgerStateDelta } from './ledger-state-delta' +export { LedgerStateDeltaMeta } from './ledger-state-delta' +export type { LedgerStateDeltaForTransactionGroup } from './ledger-state-delta-for-transaction-group' +export { LedgerStateDeltaForTransactionGroupMeta } from './ledger-state-delta-for-transaction-group' +export type { Account } from './account' +export { AccountMeta } from './account' +export type { AccountAssetHolding } from './account-asset-holding' +export { AccountAssetHoldingMeta } from './account-asset-holding' +export type { AccountParticipation } from './account-participation' +export { AccountParticipationMeta } from './account-participation' +export type { Asset } from './asset' +export { AssetMeta } from './asset' +export type { AssetHolding } from './asset-holding' +export { AssetHoldingMeta } from './asset-holding' +export type { AssetParams } from './asset-params' +export { AssetParamsMeta } from './asset-params' +export type { AssetHoldingReference } from './asset-holding-reference' +export { AssetHoldingReferenceMeta } from './asset-holding-reference' +export type { ApplicationLocalReference } from './application-local-reference' +export { ApplicationLocalReferenceMeta } from './application-local-reference' +export type { ApplicationStateSchema } from './application-state-schema' +export { ApplicationStateSchemaMeta } from './application-state-schema' +export type { ApplicationLocalState } from './application-local-state' +export { ApplicationLocalStateMeta } from './application-local-state' +export type { ParticipationKey } from './participation-key' +export { ParticipationKeyMeta } from './participation-key' +export type { TealKeyValueStore } from './teal-key-value-store' +export { TealKeyValueStoreMeta } from './teal-key-value-store' +export type { TealKeyValue } from './teal-key-value' +export { TealKeyValueMeta } from './teal-key-value' +export type { TealValue } from './teal-value' +export { TealValueMeta } from './teal-value' +export type { AvmValue } from './avm-value' +export { AvmValueMeta } from './avm-value' +export type { AvmKeyValue } from './avm-key-value' +export { AvmKeyValueMeta } from './avm-key-value' +export type { StateDelta } from './state-delta' +export { StateDeltaMeta } from './state-delta' +export type { AccountStateDelta } from './account-state-delta' +export { AccountStateDeltaMeta } from './account-state-delta' +export type { EvalDeltaKeyValue } from './eval-delta-key-value' +export { EvalDeltaKeyValueMeta } from './eval-delta-key-value' +export type { EvalDelta } from './eval-delta' +export { EvalDeltaMeta } from './eval-delta' +export type { Application } from './application' +export { ApplicationMeta } from './application' +export type { ApplicationParams } from './application-params' +export { ApplicationParamsMeta } from './application-params' +export type { DryrunState } from './dryrun-state' +export { DryrunStateMeta } from './dryrun-state' +export type { DryrunTxnResult } from './dryrun-txn-result' +export { DryrunTxnResultMeta } from './dryrun-txn-result' +export type { ErrorResponse } from './error-response' +export { ErrorResponseMeta } from './error-response' +export type { DryrunRequest } from './dryrun-request' +export { DryrunRequestMeta } from './dryrun-request' +export type { DryrunSource } from './dryrun-source' +export { DryrunSourceMeta } from './dryrun-source' +export type { SimulateRequest } from './simulate-request' +export { SimulateRequestMeta } from './simulate-request' +export type { SimulateRequestTransactionGroup } from './simulate-request-transaction-group' +export { SimulateRequestTransactionGroupMeta } from './simulate-request-transaction-group' +export type { SimulateTraceConfig } from './simulate-trace-config' +export { SimulateTraceConfigMeta } from './simulate-trace-config' +export type { Box } from './box' +export { BoxMeta } from './box' +export type { BoxDescriptor } from './box-descriptor' +export { BoxDescriptorMeta } from './box-descriptor' +export type { BoxReference } from './box-reference' +export { BoxReferenceMeta } from './box-reference' +export type { Version } from './version' +export { VersionMeta } from './version' +export type { DebugSettingsProf } from './debug-settings-prof' +export { DebugSettingsProfMeta } from './debug-settings-prof' +export type { BuildVersion } from './build-version' +export { BuildVersionMeta } from './build-version' +export type { PendingTransactionResponse } from './pending-transaction-response' +export { PendingTransactionResponseMeta } from './pending-transaction-response' +export type { SimulateTransactionGroupResult } from './simulate-transaction-group-result' +export { SimulateTransactionGroupResultMeta } from './simulate-transaction-group-result' +export type { SimulateTransactionResult } from './simulate-transaction-result' +export { SimulateTransactionResultMeta } from './simulate-transaction-result' +export type { StateProof } from './state-proof' +export { StateProofMeta } from './state-proof' +export type { LightBlockHeaderProof } from './light-block-header-proof' +export { LightBlockHeaderProofMeta } from './light-block-header-proof' +export type { StateProofMessage } from './state-proof-message' +export { StateProofMessageMeta } from './state-proof-message' +export type { SimulationEvalOverrides } from './simulation-eval-overrides' +export { SimulationEvalOverridesMeta } from './simulation-eval-overrides' +export type { ScratchChange } from './scratch-change' +export { ScratchChangeMeta } from './scratch-change' +export type { ApplicationStateOperation } from './application-state-operation' +export { ApplicationStateOperationMeta } from './application-state-operation' +export type { ApplicationKvstorage } from './application-kvstorage' +export { ApplicationKvstorageMeta } from './application-kvstorage' +export type { ApplicationInitialStates } from './application-initial-states' +export { ApplicationInitialStatesMeta } from './application-initial-states' +export type { SimulationOpcodeTraceUnit } from './simulation-opcode-trace-unit' +export { SimulationOpcodeTraceUnitMeta } from './simulation-opcode-trace-unit' +export type { SimulationTransactionExecTrace } from './simulation-transaction-exec-trace' +export { SimulationTransactionExecTraceMeta } from './simulation-transaction-exec-trace' +export type { SimulateUnnamedResourcesAccessed } from './simulate-unnamed-resources-accessed' +export { SimulateUnnamedResourcesAccessedMeta } from './simulate-unnamed-resources-accessed' +export type { SimulateInitialStates } from './simulate-initial-states' +export { SimulateInitialStatesMeta } from './simulate-initial-states' +export type { AppCallLogs } from './app-call-logs' +export { AppCallLogsMeta } from './app-call-logs' +export type { TransactionProof } from './transaction-proof' +export { TransactionProofMeta } from './transaction-proof' +export type { AccountAssetInformation } from './account-asset-information' +export { AccountAssetInformationMeta } from './account-asset-information' +export type { AccountAssetsInformation } from './account-assets-information' +export { AccountAssetsInformationMeta } from './account-assets-information' +export type { AccountApplicationInformation } from './account-application-information' +export { AccountApplicationInformationMeta } from './account-application-information' +export type { GetPendingTransactionsByAddress } from './get-pending-transactions-by-address' +export { GetPendingTransactionsByAddressMeta } from './get-pending-transactions-by-address' +export type { GetBlock } from './get-block' +export { GetBlockMeta } from './get-block' +export type { GetBlockTxids } from './get-block-txids' +export { GetBlockTxidsMeta } from './get-block-txids' +export type { GetBlockHash } from './get-block-hash' +export { GetBlockHashMeta } from './get-block-hash' +export type { GetBlockLogs } from './get-block-logs' +export { GetBlockLogsMeta } from './get-block-logs' +export type { GetSupply } from './get-supply' +export { GetSupplyMeta } from './get-supply' +export type { AddParticipationKey } from './add-participation-key' +export { AddParticipationKeyMeta } from './add-participation-key' +export type { ShutdownNode } from './shutdown-node' +export { ShutdownNodeMeta } from './shutdown-node' +export type { GetStatus } from './get-status' +export { GetStatusMeta } from './get-status' +export type { WaitForBlock } from './wait-for-block' +export { WaitForBlockMeta } from './wait-for-block' +export type { RawTransaction } from './raw-transaction' +export { RawTransactionMeta } from './raw-transaction' +export type { SimulateTransaction } from './simulate-transaction' +export { SimulateTransactionMeta } from './simulate-transaction' +export type { TransactionParams } from './transaction-params' +export { TransactionParamsMeta } from './transaction-params' +export type { GetPendingTransactions } from './get-pending-transactions' +export { GetPendingTransactionsMeta } from './get-pending-transactions' +export type { GetTransactionGroupLedgerStateDeltasForRound } from './get-transaction-group-ledger-state-deltas-for-round' +export { GetTransactionGroupLedgerStateDeltasForRoundMeta } from './get-transaction-group-ledger-state-deltas-for-round' +export type { GetApplicationBoxes } from './get-application-boxes' +export { GetApplicationBoxesMeta } from './get-application-boxes' +export type { GetSyncRound } from './get-sync-round' +export { GetSyncRoundMeta } from './get-sync-round' +export type { TealCompile } from './teal-compile' +export { TealCompileMeta } from './teal-compile' +export type { TealDisassemble } from './teal-disassemble' +export { TealDisassembleMeta } from './teal-disassemble' +export type { StartCatchup } from './start-catchup' +export { StartCatchupMeta } from './start-catchup' +export type { AbortCatchup } from './abort-catchup' +export { AbortCatchupMeta } from './abort-catchup' +export type { TealDryrun } from './teal-dryrun' +export { TealDryrunMeta } from './teal-dryrun' +export type { GetBlockTimeStampOffset } from './get-block-time-stamp-offset' +export { GetBlockTimeStampOffsetMeta } from './get-block-time-stamp-offset' diff --git a/packages/typescript/algod_client/src/models/ledger-state-delta-for-transaction-group.ts b/packages/typescript/algod_client/src/models/ledger-state-delta-for-transaction-group.ts new file mode 100644 index 000000000..a1693cfad --- /dev/null +++ b/packages/typescript/algod_client/src/models/ledger-state-delta-for-transaction-group.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { LedgerStateDelta } from './ledger-state-delta' +import { LedgerStateDeltaMeta } from './ledger-state-delta' + +/** + * Contains a ledger delta for a single transaction group + */ +export type LedgerStateDeltaForTransactionGroup = { + delta: LedgerStateDelta + ids: string[] +} + +export const LedgerStateDeltaForTransactionGroupMeta: ModelMetadata = { + name: 'LedgerStateDeltaForTransactionGroup', + kind: 'object', + fields: [ + { + name: 'delta', + wireKey: 'Delta', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => LedgerStateDeltaMeta }, + }, + { + name: 'ids', + wireKey: 'Ids', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/ledger-state-delta.ts b/packages/typescript/algod_client/src/models/ledger-state-delta.ts new file mode 100644 index 000000000..e182e624a --- /dev/null +++ b/packages/typescript/algod_client/src/models/ledger-state-delta.ts @@ -0,0 +1,12 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Ledger StateDelta object + */ +export type LedgerStateDelta = Record + +export const LedgerStateDeltaMeta: ModelMetadata = { + name: 'LedgerStateDelta', + kind: 'object', + fields: [], +} diff --git a/packages/typescript/algod_client/src/models/ledgerstatedelta.ts b/packages/typescript/algod_client/src/models/ledgerstatedelta.ts deleted file mode 100644 index c2a7b925f..000000000 --- a/packages/typescript/algod_client/src/models/ledgerstatedelta.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Ledger StateDelta object - */ -export type LedgerStateDelta = {}; diff --git a/packages/typescript/algod_client/src/models/ledgerstatedeltafortransactiongroup.ts b/packages/typescript/algod_client/src/models/ledgerstatedeltafortransactiongroup.ts deleted file mode 100644 index 93dbd407a..000000000 --- a/packages/typescript/algod_client/src/models/ledgerstatedeltafortransactiongroup.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { LedgerStateDelta } from "./index"; - -/** - * Contains a ledger delta for a single transaction group - */ -export type LedgerStateDeltaForTransactionGroup = { - delta: LedgerStateDelta; - ids: string[]; -}; diff --git a/packages/typescript/algod_client/src/models/light-block-header-proof.ts b/packages/typescript/algod_client/src/models/light-block-header-proof.ts new file mode 100644 index 000000000..72852ee49 --- /dev/null +++ b/packages/typescript/algod_client/src/models/light-block-header-proof.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Proof of membership and position of a light block header. + */ +export type LightBlockHeaderProof = { + /** + * The index of the light block header in the vector commitment tree + */ + index: bigint + + /** + * Represents the depth of the tree that is being proven, i.e. the number of edges from a leaf to the root. + */ + treedepth: bigint + + /** + * The encoded proof. + */ + proof: Uint8Array +} + +export const LightBlockHeaderProofMeta: ModelMetadata = { + name: 'LightBlockHeaderProof', + kind: 'object', + fields: [ + { + name: 'index', + wireKey: 'index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'treedepth', + wireKey: 'treedepth', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'proof', + wireKey: 'proof', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/lightblockheaderproof.ts b/packages/typescript/algod_client/src/models/lightblockheaderproof.ts deleted file mode 100644 index 1deab4349..000000000 --- a/packages/typescript/algod_client/src/models/lightblockheaderproof.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Proof of membership and position of a light block header. - */ -export type LightBlockHeaderProof = { - /** - * The index of the light block header in the vector commitment tree - */ - index: bigint; - - /** - * Represents the depth of the tree that is being proven, i.e. the number of edges from a leaf to the root. - */ - treedepth: bigint; - - /** - * The encoded proof. - */ - proof: string; -}; diff --git a/packages/typescript/algod_client/src/models/participation-key.ts b/packages/typescript/algod_client/src/models/participation-key.ts new file mode 100644 index 000000000..b718584bc --- /dev/null +++ b/packages/typescript/algod_client/src/models/participation-key.ts @@ -0,0 +1,107 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountParticipation } from './account-participation' +import { AccountParticipationMeta } from './account-participation' + +/** + * Represents a participation key used by the node. + */ +export type ParticipationKey = { + /** + * The key's ParticipationID. + */ + id: string + + /** + * Address the key was generated for. + */ + address: string + + /** + * When registered, this is the first round it may be used. + */ + effectiveFirstValid?: bigint + + /** + * When registered, this is the last round it may be used. + */ + effectiveLastValid?: bigint + + /** + * Round when this key was last used to vote. + */ + lastVote?: bigint + + /** + * Round when this key was last used to propose a block. + */ + lastBlockProposal?: bigint + + /** + * Round when this key was last used to generate a state proof. + */ + lastStateProof?: bigint + key: AccountParticipation +} + +export const ParticipationKeyMeta: ModelMetadata = { + name: 'ParticipationKey', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'effectiveFirstValid', + wireKey: 'effective-first-valid', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'effectiveLastValid', + wireKey: 'effective-last-valid', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastVote', + wireKey: 'last-vote', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lastBlockProposal', + wireKey: 'last-block-proposal', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lastStateProof', + wireKey: 'last-state-proof', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AccountParticipationMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/participationkey.ts b/packages/typescript/algod_client/src/models/participationkey.ts deleted file mode 100644 index 536cede2a..000000000 --- a/packages/typescript/algod_client/src/models/participationkey.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { AccountParticipation } from "./index"; - -/** - * Represents a participation key used by the node. - */ -export type ParticipationKey = { - /** - * The key's ParticipationID. - */ - id: string; - - /** - * Address the key was generated for. - */ - address: string; - - /** - * When registered, this is the first round it may be used. - */ - effectiveFirstValid?: bigint; - - /** - * When registered, this is the last round it may be used. - */ - effectiveLastValid?: bigint; - - /** - * Round when this key was last used to vote. - */ - lastVote?: bigint; - - /** - * Round when this key was last used to propose a block. - */ - lastBlockProposal?: bigint; - - /** - * Round when this key was last used to generate a state proof. - */ - lastStateProof?: bigint; - key: AccountParticipation; -}; diff --git a/packages/typescript/algod_client/src/models/pending-transaction-response.ts b/packages/typescript/algod_client/src/models/pending-transaction-response.ts new file mode 100644 index 000000000..72f37c04c --- /dev/null +++ b/packages/typescript/algod_client/src/models/pending-transaction-response.ts @@ -0,0 +1,182 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SignedTransaction } from '@algorandfoundation/algokit-transact' +import type { AccountStateDelta } from './account-state-delta' +import { AccountStateDeltaMeta } from './account-state-delta' +import type { StateDelta } from './state-delta' +import { StateDeltaMeta } from './state-delta' + +/** + * Details about a pending transaction. If the transaction was recently confirmed, includes confirmation details like the round and reward details. + */ +export type PendingTransactionResponse = { + /** + * The asset index if the transaction was found and it created an asset. + */ + assetId?: bigint + + /** + * The application index if the transaction was found and it created an application. + */ + appId?: bigint + + /** + * Rewards in microalgos applied to the close remainder to account. + */ + closeRewards?: bigint + + /** + * Closing amount for the transaction. + */ + closingAmount?: bigint + + /** + * The number of the asset's unit that were transferred to the close-to address. + */ + assetClosingAmount?: bigint + + /** + * The round where this transaction was confirmed, if present. + */ + confirmedRound?: bigint + + /** + * Indicates that the transaction was kicked out of this node's transaction pool (and specifies why that happened). An empty string indicates the transaction wasn't kicked out of this node's txpool due to an error. + */ + poolError: string + + /** + * Rewards in microalgos applied to the receiver account. + */ + receiverRewards?: bigint + + /** + * Rewards in microalgos applied to the sender account. + */ + senderRewards?: bigint + + /** + * Local state key/value changes for the application being executed by this transaction. + */ + localStateDelta?: AccountStateDelta[] + globalStateDelta?: StateDelta + + /** + * Logs for the application being executed by this transaction. + */ + logs?: Uint8Array[] + + /** + * Inner transactions produced by application execution. + */ + innerTxns?: PendingTransactionResponse[] + + /** + * The raw signed transaction. + */ + txn: SignedTransaction +} + +export const PendingTransactionResponseMeta: ModelMetadata = { + name: 'PendingTransactionResponse', + kind: 'object', + fields: [ + { + name: 'assetId', + wireKey: 'asset-index', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appId', + wireKey: 'application-index', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closeRewards', + wireKey: 'close-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'closingAmount', + wireKey: 'closing-amount', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assetClosingAmount', + wireKey: 'asset-closing-amount', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'confirmedRound', + wireKey: 'confirmed-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'poolError', + wireKey: 'pool-error', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'receiverRewards', + wireKey: 'receiver-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'senderRewards', + wireKey: 'sender-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'localStateDelta', + wireKey: 'local-state-delta', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountStateDeltaMeta } }, + }, + { + name: 'globalStateDelta', + wireKey: 'global-state-delta', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateDeltaMeta }, + }, + { + name: 'logs', + wireKey: 'logs', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + { + name: 'innerTxns', + wireKey: 'inner-txns', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => PendingTransactionResponseMeta } }, + }, + { + name: 'txn', + wireKey: 'txn', + optional: false, + nullable: false, + type: { kind: 'codec', codecKey: 'SignedTransaction' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/pendingtransactionresponse.ts b/packages/typescript/algod_client/src/models/pendingtransactionresponse.ts deleted file mode 100644 index 43ce550f5..000000000 --- a/packages/typescript/algod_client/src/models/pendingtransactionresponse.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { AccountStateDelta, AlgokitSignedTransaction, StateDelta } from "./index"; - -/** - * Details about a pending transaction. If the transaction was recently confirmed, includes confirmation details like the round and reward details. - */ -export type PendingTransactionResponse = { - /** - * The asset index if the transaction was found and it created an asset. - */ - assetIndex?: bigint; - - /** - * The application index if the transaction was found and it created an application. - */ - applicationIndex?: bigint; - - /** - * Rewards in microalgos applied to the close remainder to account. - */ - closeRewards?: bigint; - - /** - * Closing amount for the transaction. - */ - closingAmount?: bigint; - - /** - * The number of the asset's unit that were transferred to the close-to address. - */ - assetClosingAmount?: bigint; - - /** - * The round where this transaction was confirmed, if present. - */ - confirmedRound?: bigint; - - /** - * Indicates that the transaction was kicked out of this node's transaction pool (and specifies why that happened). An empty string indicates the transaction wasn't kicked out of this node's txpool due to an error. - */ - poolError: string; - - /** - * Rewards in microalgos applied to the receiver account. - */ - receiverRewards?: bigint; - - /** - * Rewards in microalgos applied to the sender account. - */ - senderRewards?: bigint; - - /** - * Local state key/value changes for the application being executed by this transaction. - */ - localStateDelta?: AccountStateDelta[]; - globalStateDelta?: StateDelta; - - /** - * Logs for the application being executed by this transaction. - */ - logs?: string[]; - - /** - * Inner transactions produced by application execution. - */ - innerTxns?: PendingTransactionResponse[]; - - /** - * The raw signed transaction. - */ - txn: AlgokitSignedTransaction; -}; diff --git a/packages/typescript/algod_client/src/models/raw-transaction.ts b/packages/typescript/algod_client/src/models/raw-transaction.ts new file mode 100644 index 000000000..df2ee5b8f --- /dev/null +++ b/packages/typescript/algod_client/src/models/raw-transaction.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type RawTransaction = { + /** + * encoding of the transaction hash. + */ + txId: string +} + +export const RawTransactionMeta: ModelMetadata = { + name: 'RawTransaction', + kind: 'object', + fields: [ + { + name: 'txId', + wireKey: 'txId', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/rawtransaction.ts b/packages/typescript/algod_client/src/models/rawtransaction.ts deleted file mode 100644 index 5c58c8a17..000000000 --- a/packages/typescript/algod_client/src/models/rawtransaction.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type RawTransaction = { - /** - * encoding of the transaction hash. - */ - txId: string; -}; diff --git a/packages/typescript/algod_client/src/models/scratch-change.ts b/packages/typescript/algod_client/src/models/scratch-change.ts new file mode 100644 index 000000000..c419be85d --- /dev/null +++ b/packages/typescript/algod_client/src/models/scratch-change.ts @@ -0,0 +1,35 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AvmValue } from './avm-value' +import { AvmValueMeta } from './avm-value' + +/** + * A write operation into a scratch slot. + */ +export type ScratchChange = { + /** + * The scratch slot written. + */ + slot: bigint + newValue: AvmValue +} + +export const ScratchChangeMeta: ModelMetadata = { + name: 'ScratchChange', + kind: 'object', + fields: [ + { + name: 'slot', + wireKey: 'slot', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'newValue', + wireKey: 'new-value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AvmValueMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/scratchchange.ts b/packages/typescript/algod_client/src/models/scratchchange.ts deleted file mode 100644 index 259c74900..000000000 --- a/packages/typescript/algod_client/src/models/scratchchange.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { AvmValue } from "./index"; - -/** - * A write operation into a scratch slot. - */ -export type ScratchChange = { - /** - * The scratch slot written. - */ - slot: bigint; - newValue: AvmValue; -}; diff --git a/packages/typescript/algod_client/src/models/shutdown-node.ts b/packages/typescript/algod_client/src/models/shutdown-node.ts new file mode 100644 index 000000000..46fba9026 --- /dev/null +++ b/packages/typescript/algod_client/src/models/shutdown-node.ts @@ -0,0 +1,9 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type ShutdownNode = Record + +export const ShutdownNodeMeta: ModelMetadata = { + name: 'ShutdownNode', + kind: 'object', + fields: [], +} diff --git a/packages/typescript/algod_client/src/models/shutdownnode.ts b/packages/typescript/algod_client/src/models/shutdownnode.ts deleted file mode 100644 index f14d02ec8..000000000 --- a/packages/typescript/algod_client/src/models/shutdownnode.ts +++ /dev/null @@ -1 +0,0 @@ -export type ShutdownNode = {}; diff --git a/packages/typescript/algod_client/src/models/simulate-initial-states.ts b/packages/typescript/algod_client/src/models/simulate-initial-states.ts new file mode 100644 index 000000000..21c948cd4 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-initial-states.ts @@ -0,0 +1,27 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationInitialStates } from './application-initial-states' +import { ApplicationInitialStatesMeta } from './application-initial-states' + +/** + * Initial states of resources that were accessed during simulation. + */ +export type SimulateInitialStates = { + /** + * The initial states of accessed application before simulation. The order of this array is arbitrary. + */ + appInitialStates?: ApplicationInitialStates[] +} + +export const SimulateInitialStatesMeta: ModelMetadata = { + name: 'SimulateInitialStates', + kind: 'object', + fields: [ + { + name: 'appInitialStates', + wireKey: 'app-initial-states', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationInitialStatesMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-request-transaction-group.ts b/packages/typescript/algod_client/src/models/simulate-request-transaction-group.ts new file mode 100644 index 000000000..e66bad79f --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-request-transaction-group.ts @@ -0,0 +1,26 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SignedTransaction } from '@algorandfoundation/algokit-transact' + +/** + * A transaction group to simulate. + */ +export type SimulateRequestTransactionGroup = { + /** + * An atomic transaction group. + */ + txns: SignedTransaction[] +} + +export const SimulateRequestTransactionGroupMeta: ModelMetadata = { + name: 'SimulateRequestTransactionGroup', + kind: 'object', + fields: [ + { + name: 'txns', + wireKey: 'txns', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'codec', codecKey: 'SignedTransaction' } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-request.ts b/packages/typescript/algod_client/src/models/simulate-request.ts new file mode 100644 index 000000000..6fc3caf92 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-request.ts @@ -0,0 +1,109 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SimulateRequestTransactionGroup } from './simulate-request-transaction-group' +import { SimulateRequestTransactionGroupMeta } from './simulate-request-transaction-group' +import type { SimulateTraceConfig } from './simulate-trace-config' +import { SimulateTraceConfigMeta } from './simulate-trace-config' + +/** + * Request type for simulation endpoint. + */ +export type SimulateRequest = { + /** + * The transaction groups to simulate. + */ + txnGroups: SimulateRequestTransactionGroup[] + + /** + * If provided, specifies the round preceding the simulation. State changes through this round will be used to run this simulation. Usually only the 4 most recent rounds will be available (controlled by the node config value MaxAcctLookback). If not specified, defaults to the latest available round. + */ + round?: bigint + + /** + * Allows transactions without signatures to be simulated as if they had correct signatures. + */ + allowEmptySignatures?: boolean + + /** + * Lifts limits on log opcode usage during simulation. + */ + allowMoreLogging?: boolean + + /** + * Allows access to unnamed resources during simulation. + */ + allowUnnamedResources?: boolean + + /** + * Applies extra opcode budget during simulation for each transaction group. + */ + extraOpcodeBudget?: bigint + execTraceConfig?: SimulateTraceConfig + + /** + * If true, signers for transactions that are missing signatures will be fixed during evaluation. + */ + fixSigners?: boolean +} + +export const SimulateRequestMeta: ModelMetadata = { + name: 'SimulateRequest', + kind: 'object', + fields: [ + { + name: 'txnGroups', + wireKey: 'txn-groups', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulateRequestTransactionGroupMeta } }, + }, + { + name: 'round', + wireKey: 'round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'allowEmptySignatures', + wireKey: 'allow-empty-signatures', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'allowMoreLogging', + wireKey: 'allow-more-logging', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'allowUnnamedResources', + wireKey: 'allow-unnamed-resources', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'extraOpcodeBudget', + wireKey: 'extra-opcode-budget', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'execTraceConfig', + wireKey: 'exec-trace-config', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulateTraceConfigMeta }, + }, + { + name: 'fixSigners', + wireKey: 'fix-signers', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-trace-config.ts b/packages/typescript/algod_client/src/models/simulate-trace-config.ts new file mode 100644 index 000000000..aa63a49a1 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-trace-config.ts @@ -0,0 +1,61 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * An object that configures simulation execution trace. + */ +export type SimulateTraceConfig = { + /** + * A boolean option for opting in execution trace features simulation endpoint. + */ + enable?: boolean + + /** + * A boolean option enabling returning stack changes together with execution trace during simulation. + */ + stackChange?: boolean + + /** + * A boolean option enabling returning scratch slot changes together with execution trace during simulation. + */ + scratchChange?: boolean + + /** + * A boolean option enabling returning application state changes (global, local, and box changes) with the execution trace during simulation. + */ + stateChange?: boolean +} + +export const SimulateTraceConfigMeta: ModelMetadata = { + name: 'SimulateTraceConfig', + kind: 'object', + fields: [ + { + name: 'enable', + wireKey: 'enable', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stackChange', + wireKey: 'stack-change', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'scratchChange', + wireKey: 'scratch-change', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stateChange', + wireKey: 'state-change', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-transaction-group-result.ts b/packages/typescript/algod_client/src/models/simulate-transaction-group-result.ts new file mode 100644 index 000000000..86d1a4efe --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-transaction-group-result.ts @@ -0,0 +1,85 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SimulateTransactionResult } from './simulate-transaction-result' +import { SimulateTransactionResultMeta } from './simulate-transaction-result' +import type { SimulateUnnamedResourcesAccessed } from './simulate-unnamed-resources-accessed' +import { SimulateUnnamedResourcesAccessedMeta } from './simulate-unnamed-resources-accessed' + +/** + * Simulation result for an atomic transaction group + */ +export type SimulateTransactionGroupResult = { + /** + * Simulation result for individual transactions + */ + txnResults: SimulateTransactionResult[] + + /** + * If present, indicates that the transaction group failed and specifies why that happened + */ + failureMessage?: string + + /** + * If present, indicates which transaction in this group caused the failure. This array represents the path to the failing transaction. Indexes are zero based, the first element indicates the top-level transaction, and successive elements indicate deeper inner transactions. + */ + failedAt?: bigint[] + + /** + * Total budget added during execution of app calls in the transaction group. + */ + appBudgetAdded?: bigint + + /** + * Total budget consumed during execution of app calls in the transaction group. + */ + appBudgetConsumed?: bigint + unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed +} + +export const SimulateTransactionGroupResultMeta: ModelMetadata = { + name: 'SimulateTransactionGroupResult', + kind: 'object', + fields: [ + { + name: 'txnResults', + wireKey: 'txn-results', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulateTransactionResultMeta } }, + }, + { + name: 'failureMessage', + wireKey: 'failure-message', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'failedAt', + wireKey: 'failed-at', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'appBudgetAdded', + wireKey: 'app-budget-added', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'appBudgetConsumed', + wireKey: 'app-budget-consumed', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'unnamedResourcesAccessed', + wireKey: 'unnamed-resources-accessed', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulateUnnamedResourcesAccessedMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-transaction-result.ts b/packages/typescript/algod_client/src/models/simulate-transaction-result.ts new file mode 100644 index 000000000..7d9e550e3 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-transaction-result.ts @@ -0,0 +1,80 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { PendingTransactionResponse } from './pending-transaction-response' +import { PendingTransactionResponseMeta } from './pending-transaction-response' +import type { SimulateUnnamedResourcesAccessed } from './simulate-unnamed-resources-accessed' +import { SimulateUnnamedResourcesAccessedMeta } from './simulate-unnamed-resources-accessed' +import type { SimulationTransactionExecTrace } from './simulation-transaction-exec-trace' +import { SimulationTransactionExecTraceMeta } from './simulation-transaction-exec-trace' + +/** + * Simulation result for an individual transaction + */ +export type SimulateTransactionResult = { + txnResult: PendingTransactionResponse + + /** + * Budget used during execution of an app call transaction. This value includes budged used by inner app calls spawned by this transaction. + */ + appBudgetConsumed?: number + + /** + * Budget used during execution of a logic sig transaction. + */ + logicSigBudgetConsumed?: number + execTrace?: SimulationTransactionExecTrace + unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed + + /** + * The account that needed to sign this transaction when no signature was provided and the provided signer was incorrect. + */ + fixedSigner?: string +} + +export const SimulateTransactionResultMeta: ModelMetadata = { + name: 'SimulateTransactionResult', + kind: 'object', + fields: [ + { + name: 'txnResult', + wireKey: 'txn-result', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => PendingTransactionResponseMeta }, + }, + { + name: 'appBudgetConsumed', + wireKey: 'app-budget-consumed', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'logicSigBudgetConsumed', + wireKey: 'logic-sig-budget-consumed', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'execTrace', + wireKey: 'exec-trace', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulationTransactionExecTraceMeta }, + }, + { + name: 'unnamedResourcesAccessed', + wireKey: 'unnamed-resources-accessed', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulateUnnamedResourcesAccessedMeta }, + }, + { + name: 'fixedSigner', + wireKey: 'fixed-signer', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-transaction.ts b/packages/typescript/algod_client/src/models/simulate-transaction.ts new file mode 100644 index 000000000..57e01423f --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-transaction.ts @@ -0,0 +1,78 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SimulateInitialStates } from './simulate-initial-states' +import { SimulateInitialStatesMeta } from './simulate-initial-states' +import type { SimulateTraceConfig } from './simulate-trace-config' +import { SimulateTraceConfigMeta } from './simulate-trace-config' +import type { SimulateTransactionGroupResult } from './simulate-transaction-group-result' +import { SimulateTransactionGroupResultMeta } from './simulate-transaction-group-result' +import type { SimulationEvalOverrides } from './simulation-eval-overrides' +import { SimulationEvalOverridesMeta } from './simulation-eval-overrides' + +export type SimulateTransaction = { + /** + * The version of this response object. + */ + version: bigint + + /** + * The round immediately preceding this simulation. State changes through this round were used to run this simulation. + */ + lastRound: bigint + + /** + * A result object for each transaction group that was simulated. + */ + txnGroups: SimulateTransactionGroupResult[] + evalOverrides?: SimulationEvalOverrides + execTraceConfig?: SimulateTraceConfig + initialStates?: SimulateInitialStates +} + +export const SimulateTransactionMeta: ModelMetadata = { + name: 'SimulateTransaction', + kind: 'object', + fields: [ + { + name: 'version', + wireKey: 'version', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastRound', + wireKey: 'last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'txnGroups', + wireKey: 'txn-groups', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulateTransactionGroupResultMeta } }, + }, + { + name: 'evalOverrides', + wireKey: 'eval-overrides', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulationEvalOverridesMeta }, + }, + { + name: 'execTraceConfig', + wireKey: 'exec-trace-config', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulateTraceConfigMeta }, + }, + { + name: 'initialStates', + wireKey: 'initial-states', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => SimulateInitialStatesMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulate-unnamed-resources-accessed.ts b/packages/typescript/algod_client/src/models/simulate-unnamed-resources-accessed.ts new file mode 100644 index 000000000..7544d849d --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulate-unnamed-resources-accessed.ts @@ -0,0 +1,103 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationLocalReference } from './application-local-reference' +import { ApplicationLocalReferenceMeta } from './application-local-reference' +import type { AssetHoldingReference } from './asset-holding-reference' +import { AssetHoldingReferenceMeta } from './asset-holding-reference' +import type { BoxReference } from './box-reference' +import { BoxReferenceMeta } from './box-reference' + +/** + * These are resources that were accessed by this group that would normally have caused failure, but were allowed in simulation. Depending on where this object is in the response, the unnamed resources it contains may or may not qualify for group resource sharing. If this is a field in SimulateTransactionGroupResult, the resources do qualify, but if this is a field in SimulateTransactionResult, they do not qualify. In order to make this group valid for actual submission, resources that qualify for group sharing can be made available by any transaction of the group; otherwise, resources must be placed in the same transaction which accessed them. + */ +export type SimulateUnnamedResourcesAccessed = { + /** + * The unnamed accounts that were referenced. The order of this array is arbitrary. + */ + accounts?: string[] + + /** + * The unnamed assets that were referenced. The order of this array is arbitrary. + */ + assets?: bigint[] + + /** + * The unnamed applications that were referenced. The order of this array is arbitrary. + */ + apps?: bigint[] + + /** + * The unnamed boxes that were referenced. The order of this array is arbitrary. + */ + boxes?: BoxReference[] + + /** + * The number of extra box references used to increase the IO budget. This is in addition to the references defined in the input transaction group and any referenced to unnamed boxes. + */ + extraBoxRefs?: bigint + + /** + * The unnamed asset holdings that were referenced. The order of this array is arbitrary. + */ + assetHoldings?: AssetHoldingReference[] + + /** + * The unnamed application local states that were referenced. The order of this array is arbitrary. + */ + appLocals?: ApplicationLocalReference[] +} + +export const SimulateUnnamedResourcesAccessedMeta: ModelMetadata = { + name: 'SimulateUnnamedResourcesAccessed', + kind: 'object', + fields: [ + { + name: 'accounts', + wireKey: 'accounts', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'assets', + wireKey: 'assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBigint: true } }, + }, + { + name: 'apps', + wireKey: 'apps', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBigint: true } }, + }, + { + name: 'boxes', + wireKey: 'boxes', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => BoxReferenceMeta } }, + }, + { + name: 'extraBoxRefs', + wireKey: 'extra-box-refs', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assetHoldings', + wireKey: 'asset-holdings', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetHoldingReferenceMeta } }, + }, + { + name: 'appLocals', + wireKey: 'app-locals', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationLocalReferenceMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulateinitialstates.ts b/packages/typescript/algod_client/src/models/simulateinitialstates.ts deleted file mode 100644 index c639801a6..000000000 --- a/packages/typescript/algod_client/src/models/simulateinitialstates.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ApplicationInitialStates } from "./index"; - -/** - * Initial states of resources that were accessed during simulation. - */ -export type SimulateInitialStates = { - /** - * The initial states of accessed application before simulation. The order of this array is arbitrary. - */ - appInitialStates?: ApplicationInitialStates[]; -}; diff --git a/packages/typescript/algod_client/src/models/simulaterequest.ts b/packages/typescript/algod_client/src/models/simulaterequest.ts deleted file mode 100644 index ab0718f8c..000000000 --- a/packages/typescript/algod_client/src/models/simulaterequest.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { SimulateRequestTransactionGroup, SimulateTraceConfig } from "./index"; - -/** - * Request type for simulation endpoint. - */ -export type SimulateRequest = { - /** - * The transaction groups to simulate. - */ - txnGroups: SimulateRequestTransactionGroup[]; - - /** - * If provided, specifies the round preceding the simulation. State changes through this round will be used to run this simulation. Usually only the 4 most recent rounds will be available (controlled by the node config value MaxAcctLookback). If not specified, defaults to the latest available round. - */ - round?: bigint; - - /** - * Allows transactions without signatures to be simulated as if they had correct signatures. - */ - allowEmptySignatures?: boolean; - - /** - * Lifts limits on log opcode usage during simulation. - */ - allowMoreLogging?: boolean; - - /** - * Allows access to unnamed resources during simulation. - */ - allowUnnamedResources?: boolean; - - /** - * Applies extra opcode budget during simulation for each transaction group. - */ - extraOpcodeBudget?: bigint; - execTraceConfig?: SimulateTraceConfig; - - /** - * If true, signers for transactions that are missing signatures will be fixed during evaluation. - */ - fixSigners?: boolean; -}; diff --git a/packages/typescript/algod_client/src/models/simulaterequesttransactiongroup.ts b/packages/typescript/algod_client/src/models/simulaterequesttransactiongroup.ts deleted file mode 100644 index 758e6d1b2..000000000 --- a/packages/typescript/algod_client/src/models/simulaterequesttransactiongroup.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { AlgokitSignedTransaction } from "./index"; - -/** - * A transaction group to simulate. - */ -export type SimulateRequestTransactionGroup = { - /** - * An atomic transaction group. - */ - txns: AlgokitSignedTransaction[]; -}; diff --git a/packages/typescript/algod_client/src/models/simulatetraceconfig.ts b/packages/typescript/algod_client/src/models/simulatetraceconfig.ts deleted file mode 100644 index 905e989c9..000000000 --- a/packages/typescript/algod_client/src/models/simulatetraceconfig.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * An object that configures simulation execution trace. - */ -export type SimulateTraceConfig = { - /** - * A boolean option for opting in execution trace features simulation endpoint. - */ - enable?: boolean; - - /** - * A boolean option enabling returning stack changes together with execution trace during simulation. - */ - stackChange?: boolean; - - /** - * A boolean option enabling returning scratch slot changes together with execution trace during simulation. - */ - scratchChange?: boolean; - - /** - * A boolean option enabling returning application state changes (global, local, and box changes) with the execution trace during simulation. - */ - stateChange?: boolean; -}; diff --git a/packages/typescript/algod_client/src/models/simulatetransaction.ts b/packages/typescript/algod_client/src/models/simulatetransaction.ts deleted file mode 100644 index 8995250a2..000000000 --- a/packages/typescript/algod_client/src/models/simulatetransaction.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { SimulateInitialStates, SimulateTraceConfig, SimulateTransactionGroupResult, SimulationEvalOverrides } from "./index"; - -export type SimulateTransaction = { - /** - * The version of this response object. - */ - version: bigint; - - /** - * The round immediately preceding this simulation. State changes through this round were used to run this simulation. - */ - lastRound: bigint; - - /** - * A result object for each transaction group that was simulated. - */ - txnGroups: SimulateTransactionGroupResult[]; - evalOverrides?: SimulationEvalOverrides; - execTraceConfig?: SimulateTraceConfig; - initialStates?: SimulateInitialStates; -}; diff --git a/packages/typescript/algod_client/src/models/simulatetransactiongroupresult.ts b/packages/typescript/algod_client/src/models/simulatetransactiongroupresult.ts deleted file mode 100644 index d5f1029a2..000000000 --- a/packages/typescript/algod_client/src/models/simulatetransactiongroupresult.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { SimulateTransactionResult, SimulateUnnamedResourcesAccessed } from "./index"; - -/** - * Simulation result for an atomic transaction group - */ -export type SimulateTransactionGroupResult = { - /** - * Simulation result for individual transactions - */ - txnResults: SimulateTransactionResult[]; - - /** - * If present, indicates that the transaction group failed and specifies why that happened - */ - failureMessage?: string; - - /** - * If present, indicates which transaction in this group caused the failure. This array represents the path to the failing transaction. Indexes are zero based, the first element indicates the top-level transaction, and successive elements indicate deeper inner transactions. - */ - failedAt?: bigint[]; - - /** - * Total budget added during execution of app calls in the transaction group. - */ - appBudgetAdded?: bigint; - - /** - * Total budget consumed during execution of app calls in the transaction group. - */ - appBudgetConsumed?: bigint; - unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed; -}; diff --git a/packages/typescript/algod_client/src/models/simulatetransactionresult.ts b/packages/typescript/algod_client/src/models/simulatetransactionresult.ts deleted file mode 100644 index a9d0a5488..000000000 --- a/packages/typescript/algod_client/src/models/simulatetransactionresult.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { PendingTransactionResponse, SimulateUnnamedResourcesAccessed, SimulationTransactionExecTrace } from "./index"; - -/** - * Simulation result for an individual transaction - */ -export type SimulateTransactionResult = { - txnResult: PendingTransactionResponse; - - /** - * Budget used during execution of an app call transaction. This value includes budged used by inner app calls spawned by this transaction. - */ - appBudgetConsumed?: bigint; - - /** - * Budget used during execution of a logic sig transaction. - */ - logicSigBudgetConsumed?: bigint; - execTrace?: SimulationTransactionExecTrace; - unnamedResourcesAccessed?: SimulateUnnamedResourcesAccessed; - - /** - * The account that needed to sign this transaction when no signature was provided and the provided signer was incorrect. - */ - fixedSigner?: string; -}; diff --git a/packages/typescript/algod_client/src/models/simulateunnamedresourcesaccessed.ts b/packages/typescript/algod_client/src/models/simulateunnamedresourcesaccessed.ts deleted file mode 100644 index 33b1f51fb..000000000 --- a/packages/typescript/algod_client/src/models/simulateunnamedresourcesaccessed.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { ApplicationLocalReference, AssetHoldingReference, BoxReference } from "./index"; - -/** - * These are resources that were accessed by this group that would normally have caused failure, but were allowed in simulation. Depending on where this object is in the response, the unnamed resources it contains may or may not qualify for group resource sharing. If this is a field in SimulateTransactionGroupResult, the resources do qualify, but if this is a field in SimulateTransactionResult, they do not qualify. In order to make this group valid for actual submission, resources that qualify for group sharing can be made available by any transaction of the group; otherwise, resources must be placed in the same transaction which accessed them. - */ -export type SimulateUnnamedResourcesAccessed = { - /** - * The unnamed accounts that were referenced. The order of this array is arbitrary. - */ - accounts?: string[]; - - /** - * The unnamed assets that were referenced. The order of this array is arbitrary. - */ - assets?: bigint[]; - - /** - * The unnamed applications that were referenced. The order of this array is arbitrary. - */ - apps?: bigint[]; - - /** - * The unnamed boxes that were referenced. The order of this array is arbitrary. - */ - boxes?: BoxReference[]; - - /** - * The number of extra box references used to increase the IO budget. This is in addition to the references defined in the input transaction group and any referenced to unnamed boxes. - */ - extraBoxRefs?: bigint; - - /** - * The unnamed asset holdings that were referenced. The order of this array is arbitrary. - */ - assetHoldings?: AssetHoldingReference[]; - - /** - * The unnamed application local states that were referenced. The order of this array is arbitrary. - */ - appLocals?: ApplicationLocalReference[]; -}; diff --git a/packages/typescript/algod_client/src/models/simulation-eval-overrides.ts b/packages/typescript/algod_client/src/models/simulation-eval-overrides.ts new file mode 100644 index 000000000..ef2542247 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulation-eval-overrides.ts @@ -0,0 +1,85 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * The set of parameters and limits override during simulation. If this set of parameters is present, then evaluation parameters may differ from standard evaluation in certain ways. + */ +export type SimulationEvalOverrides = { + /** + * If true, transactions without signatures are allowed and simulated as if they were properly signed. + */ + allowEmptySignatures?: boolean + + /** + * If true, allows access to unnamed resources during simulation. + */ + allowUnnamedResources?: boolean + + /** + * The maximum log calls one can make during simulation + */ + maxLogCalls?: bigint + + /** + * The maximum byte number to log during simulation + */ + maxLogSize?: bigint + + /** + * The extra opcode budget added to each transaction group during simulation + */ + extraOpcodeBudget?: bigint + + /** + * If true, signers for transactions that are missing signatures will be fixed during evaluation. + */ + fixSigners?: boolean +} + +export const SimulationEvalOverridesMeta: ModelMetadata = { + name: 'SimulationEvalOverrides', + kind: 'object', + fields: [ + { + name: 'allowEmptySignatures', + wireKey: 'allow-empty-signatures', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'allowUnnamedResources', + wireKey: 'allow-unnamed-resources', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'maxLogCalls', + wireKey: 'max-log-calls', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'maxLogSize', + wireKey: 'max-log-size', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'extraOpcodeBudget', + wireKey: 'extra-opcode-budget', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'fixSigners', + wireKey: 'fix-signers', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulation-opcode-trace-unit.ts b/packages/typescript/algod_client/src/models/simulation-opcode-trace-unit.ts new file mode 100644 index 000000000..b49230aef --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulation-opcode-trace-unit.ts @@ -0,0 +1,91 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationStateOperation } from './application-state-operation' +import { ApplicationStateOperationMeta } from './application-state-operation' +import type { AvmValue } from './avm-value' +import { AvmValueMeta } from './avm-value' +import type { ScratchChange } from './scratch-change' +import { ScratchChangeMeta } from './scratch-change' + +/** + * The set of trace information and effect from evaluating a single opcode. + */ +export type SimulationOpcodeTraceUnit = { + /** + * The program counter of the current opcode being evaluated. + */ + pc: bigint + + /** + * The writes into scratch slots. + */ + scratchChanges?: ScratchChange[] + + /** + * The operations against the current application's states. + */ + stateChanges?: ApplicationStateOperation[] + + /** + * The indexes of the traces for inner transactions spawned by this opcode, if any. + */ + spawnedInners?: bigint[] + + /** + * The number of deleted stack values by this opcode. + */ + stackPopCount?: bigint + + /** + * The values added by this opcode to the stack. + */ + stackAdditions?: AvmValue[] +} + +export const SimulationOpcodeTraceUnitMeta: ModelMetadata = { + name: 'SimulationOpcodeTraceUnit', + kind: 'object', + fields: [ + { + name: 'pc', + wireKey: 'pc', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'scratchChanges', + wireKey: 'scratch-changes', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ScratchChangeMeta } }, + }, + { + name: 'stateChanges', + wireKey: 'state-changes', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationStateOperationMeta } }, + }, + { + name: 'spawnedInners', + wireKey: 'spawned-inners', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'stackPopCount', + wireKey: 'stack-pop-count', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stackAdditions', + wireKey: 'stack-additions', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AvmValueMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulation-transaction-exec-trace.ts b/packages/typescript/algod_client/src/models/simulation-transaction-exec-trace.ts new file mode 100644 index 000000000..b499f9b86 --- /dev/null +++ b/packages/typescript/algod_client/src/models/simulation-transaction-exec-trace.ts @@ -0,0 +1,123 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { SimulationOpcodeTraceUnit } from './simulation-opcode-trace-unit' +import { SimulationOpcodeTraceUnitMeta } from './simulation-opcode-trace-unit' + +/** + * The execution trace of calling an app or a logic sig, containing the inner app call trace in a recursive way. + */ +export type SimulationTransactionExecTrace = { + /** + * Program trace that contains a trace of opcode effects in an approval program. + */ + approvalProgramTrace?: SimulationOpcodeTraceUnit[] + + /** + * SHA512_256 hash digest of the approval program executed in transaction. + */ + approvalProgramHash?: Uint8Array + + /** + * Program trace that contains a trace of opcode effects in a clear state program. + */ + clearStateProgramTrace?: SimulationOpcodeTraceUnit[] + + /** + * SHA512_256 hash digest of the clear state program executed in transaction. + */ + clearStateProgramHash?: Uint8Array + + /** + * If true, indicates that the clear state program failed and any persistent state changes it produced should be reverted once the program exits. + */ + clearStateRollback?: boolean + + /** + * The error message explaining why the clear state program failed. This field will only be populated if clear-state-rollback is true and the failure was due to an execution error. + */ + clearStateRollbackError?: string + + /** + * Program trace that contains a trace of opcode effects in a logic sig. + */ + logicSigTrace?: SimulationOpcodeTraceUnit[] + + /** + * SHA512_256 hash digest of the logic sig executed in transaction. + */ + logicSigHash?: Uint8Array + + /** + * An array of SimulationTransactionExecTrace representing the execution trace of any inner transactions executed. + */ + innerTrace?: SimulationTransactionExecTrace[] +} + +export const SimulationTransactionExecTraceMeta: ModelMetadata = { + name: 'SimulationTransactionExecTrace', + kind: 'object', + fields: [ + { + name: 'approvalProgramTrace', + wireKey: 'approval-program-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulationOpcodeTraceUnitMeta } }, + }, + { + name: 'approvalProgramHash', + wireKey: 'approval-program-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'clearStateProgramTrace', + wireKey: 'clear-state-program-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulationOpcodeTraceUnitMeta } }, + }, + { + name: 'clearStateProgramHash', + wireKey: 'clear-state-program-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'clearStateRollback', + wireKey: 'clear-state-rollback', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'clearStateRollbackError', + wireKey: 'clear-state-rollback-error', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'logicSigTrace', + wireKey: 'logic-sig-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulationOpcodeTraceUnitMeta } }, + }, + { + name: 'logicSigHash', + wireKey: 'logic-sig-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'innerTrace', + wireKey: 'inner-trace', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => SimulationTransactionExecTraceMeta } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/simulationevaloverrides.ts b/packages/typescript/algod_client/src/models/simulationevaloverrides.ts deleted file mode 100644 index 185c3c423..000000000 --- a/packages/typescript/algod_client/src/models/simulationevaloverrides.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * The set of parameters and limits override during simulation. If this set of parameters is present, then evaluation parameters may differ from standard evaluation in certain ways. - */ -export type SimulationEvalOverrides = { - /** - * If true, transactions without signatures are allowed and simulated as if they were properly signed. - */ - allowEmptySignatures?: boolean; - - /** - * If true, allows access to unnamed resources during simulation. - */ - allowUnnamedResources?: boolean; - - /** - * The maximum log calls one can make during simulation - */ - maxLogCalls?: bigint; - - /** - * The maximum byte number to log during simulation - */ - maxLogSize?: bigint; - - /** - * The extra opcode budget added to each transaction group during simulation - */ - extraOpcodeBudget?: bigint; - - /** - * If true, signers for transactions that are missing signatures will be fixed during evaluation. - */ - fixSigners?: boolean; -}; diff --git a/packages/typescript/algod_client/src/models/simulationopcodetraceunit.ts b/packages/typescript/algod_client/src/models/simulationopcodetraceunit.ts deleted file mode 100644 index aba87b9f9..000000000 --- a/packages/typescript/algod_client/src/models/simulationopcodetraceunit.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ApplicationStateOperation, AvmValue, ScratchChange } from "./index"; - -/** - * The set of trace information and effect from evaluating a single opcode. - */ -export type SimulationOpcodeTraceUnit = { - /** - * The program counter of the current opcode being evaluated. - */ - pc: bigint; - - /** - * The writes into scratch slots. - */ - scratchChanges?: ScratchChange[]; - - /** - * The operations against the current application's states. - */ - stateChanges?: ApplicationStateOperation[]; - - /** - * The indexes of the traces for inner transactions spawned by this opcode, if any. - */ - spawnedInners?: bigint[]; - - /** - * The number of deleted stack values by this opcode. - */ - stackPopCount?: bigint; - - /** - * The values added by this opcode to the stack. - */ - stackAdditions?: AvmValue[]; -}; diff --git a/packages/typescript/algod_client/src/models/simulationtransactionexectrace.ts b/packages/typescript/algod_client/src/models/simulationtransactionexectrace.ts deleted file mode 100644 index faa54cfc9..000000000 --- a/packages/typescript/algod_client/src/models/simulationtransactionexectrace.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { SimulationOpcodeTraceUnit } from "./index"; - -/** - * The execution trace of calling an app or a logic sig, containing the inner app call trace in a recursive way. - */ -export type SimulationTransactionExecTrace = { - /** - * Program trace that contains a trace of opcode effects in an approval program. - */ - approvalProgramTrace?: SimulationOpcodeTraceUnit[]; - - /** - * SHA512_256 hash digest of the approval program executed in transaction. - */ - approvalProgramHash?: string; - - /** - * Program trace that contains a trace of opcode effects in a clear state program. - */ - clearStateProgramTrace?: SimulationOpcodeTraceUnit[]; - - /** - * SHA512_256 hash digest of the clear state program executed in transaction. - */ - clearStateProgramHash?: string; - - /** - * If true, indicates that the clear state program failed and any persistent state changes it produced should be reverted once the program exits. - */ - clearStateRollback?: boolean; - - /** - * The error message explaining why the clear state program failed. This field will only be populated if clear-state-rollback is true and the failure was due to an execution error. - */ - clearStateRollbackError?: string; - - /** - * Program trace that contains a trace of opcode effects in a logic sig. - */ - logicSigTrace?: SimulationOpcodeTraceUnit[]; - - /** - * SHA512_256 hash digest of the logic sig executed in transaction. - */ - logicSigHash?: string; - - /** - * An array of SimulationTransactionExecTrace representing the execution trace of any inner transactions executed. - */ - innerTrace?: SimulationTransactionExecTrace[]; -}; diff --git a/packages/typescript/algod_client/src/models/start-catchup.ts b/packages/typescript/algod_client/src/models/start-catchup.ts new file mode 100644 index 000000000..eb972b368 --- /dev/null +++ b/packages/typescript/algod_client/src/models/start-catchup.ts @@ -0,0 +1,25 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * An catchpoint start response. + */ +export type StartCatchup = { + /** + * Catchup start response string + */ + catchupMessage: string +} + +export const StartCatchupMeta: ModelMetadata = { + name: 'StartCatchup', + kind: 'object', + fields: [ + { + name: 'catchupMessage', + wireKey: 'catchup-message', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/startcatchup.ts b/packages/typescript/algod_client/src/models/startcatchup.ts deleted file mode 100644 index 9fb9e47f6..000000000 --- a/packages/typescript/algod_client/src/models/startcatchup.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * An catchpoint start response. - */ -export type StartCatchup = { - /** - * Catchup start response string - */ - catchupMessage: string; -}; diff --git a/packages/typescript/algod_client/src/models/state-delta.ts b/packages/typescript/algod_client/src/models/state-delta.ts new file mode 100644 index 000000000..ef811cfea --- /dev/null +++ b/packages/typescript/algod_client/src/models/state-delta.ts @@ -0,0 +1,14 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { EvalDeltaKeyValue } from './eval-delta-key-value' +import { EvalDeltaKeyValueMeta } from './eval-delta-key-value' + +/** + * Application state delta. + */ +export type StateDelta = EvalDeltaKeyValue[] + +export const StateDeltaMeta: ModelMetadata = { + name: 'StateDelta', + kind: 'array', + arrayItems: { kind: 'model', meta: () => EvalDeltaKeyValueMeta }, +} diff --git a/packages/typescript/algod_client/src/models/state-proof-message.ts b/packages/typescript/algod_client/src/models/state-proof-message.ts new file mode 100644 index 000000000..9edc2eddc --- /dev/null +++ b/packages/typescript/algod_client/src/models/state-proof-message.ts @@ -0,0 +1,73 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents the message that the state proofs are attesting to. + */ +export type StateProofMessage = { + /** + * The vector commitment root on all light block headers within a state proof interval. + */ + blockHeadersCommitment: Uint8Array + + /** + * The vector commitment root of the top N accounts to sign the next StateProof. + */ + votersCommitment: Uint8Array + + /** + * An integer value representing the natural log of the proven weight with 16 bits of precision. This value would be used to verify the next state proof. + */ + lnProvenWeight: bigint + + /** + * The first round the message attests to. + */ + firstAttestedRound: bigint + + /** + * The last round the message attests to. + */ + lastAttestedRound: bigint +} + +export const StateProofMessageMeta: ModelMetadata = { + name: 'StateProofMessage', + kind: 'object', + fields: [ + { + name: 'blockHeadersCommitment', + wireKey: 'BlockHeadersCommitment', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'votersCommitment', + wireKey: 'VotersCommitment', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'lnProvenWeight', + wireKey: 'LnProvenWeight', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'firstAttestedRound', + wireKey: 'FirstAttestedRound', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastAttestedRound', + wireKey: 'LastAttestedRound', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/state-proof.ts b/packages/typescript/algod_client/src/models/state-proof.ts new file mode 100644 index 000000000..96dd3bcdc --- /dev/null +++ b/packages/typescript/algod_client/src/models/state-proof.ts @@ -0,0 +1,36 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateProofMessage } from './state-proof-message' +import { StateProofMessageMeta } from './state-proof-message' + +/** + * Represents a state proof and its corresponding message + */ +export type StateProof = { + message: StateProofMessage + + /** + * The encoded StateProof for the message. + */ + stateProof: Uint8Array +} + +export const StateProofMeta: ModelMetadata = { + name: 'StateProof', + kind: 'object', + fields: [ + { + name: 'message', + wireKey: 'Message', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => StateProofMessageMeta }, + }, + { + name: 'stateProof', + wireKey: 'StateProof', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/statedelta.ts b/packages/typescript/algod_client/src/models/statedelta.ts deleted file mode 100644 index 2f09ff92a..000000000 --- a/packages/typescript/algod_client/src/models/statedelta.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { EvalDeltaKeyValue } from "./index"; - -/** - * Application state delta. - */ -export type StateDelta = EvalDeltaKeyValue[]; diff --git a/packages/typescript/algod_client/src/models/stateproof.ts b/packages/typescript/algod_client/src/models/stateproof.ts deleted file mode 100644 index 2b899fea8..000000000 --- a/packages/typescript/algod_client/src/models/stateproof.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { StateProofMessage } from "./index"; - -/** - * Represents a state proof and its corresponding message - */ -export type StateProof = { - message: StateProofMessage; - - /** - * The encoded StateProof for the message. - */ - stateProof: string; -}; diff --git a/packages/typescript/algod_client/src/models/stateproofmessage.ts b/packages/typescript/algod_client/src/models/stateproofmessage.ts deleted file mode 100644 index b428bb568..000000000 --- a/packages/typescript/algod_client/src/models/stateproofmessage.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Represents the message that the state proofs are attesting to. - */ -export type StateProofMessage = { - /** - * The vector commitment root on all light block headers within a state proof interval. - */ - blockHeadersCommitment: string; - - /** - * The vector commitment root of the top N accounts to sign the next StateProof. - */ - votersCommitment: string; - - /** - * An integer value representing the natural log of the proven weight with 16 bits of precision. This value would be used to verify the next state proof. - */ - lnProvenWeight: bigint; - - /** - * The first round the message attests to. - */ - firstAttestedRound: bigint; - - /** - * The last round the message attests to. - */ - lastAttestedRound: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/teal-compile.ts b/packages/typescript/algod_client/src/models/teal-compile.ts new file mode 100644 index 000000000..97aacb1f7 --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-compile.ts @@ -0,0 +1,46 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type TealCompile = { + /** + * base32 SHA512_256 of program bytes (Address style) + */ + hash: string + + /** + * base64 encoded program bytes + */ + result: string + + /** + * JSON of the source map + */ + sourcemap?: Record +} + +export const TealCompileMeta: ModelMetadata = { + name: 'TealCompile', + kind: 'object', + fields: [ + { + name: 'hash', + wireKey: 'hash', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'result', + wireKey: 'result', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sourcemap', + wireKey: 'sourcemap', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/teal-disassemble.ts b/packages/typescript/algod_client/src/models/teal-disassemble.ts new file mode 100644 index 000000000..9932fcfd5 --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-disassemble.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type TealDisassemble = { + /** + * disassembled Teal code + */ + result: string +} + +export const TealDisassembleMeta: ModelMetadata = { + name: 'TealDisassemble', + kind: 'object', + fields: [ + { + name: 'result', + wireKey: 'result', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/teal-dryrun.ts b/packages/typescript/algod_client/src/models/teal-dryrun.ts new file mode 100644 index 000000000..7a7899da3 --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-dryrun.ts @@ -0,0 +1,41 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { DryrunTxnResult } from './dryrun-txn-result' +import { DryrunTxnResultMeta } from './dryrun-txn-result' + +export type TealDryrun = { + txns: DryrunTxnResult[] + error: string + + /** + * Protocol version is the protocol version Dryrun was operated under. + */ + protocolVersion: string +} + +export const TealDryrunMeta: ModelMetadata = { + name: 'TealDryrun', + kind: 'object', + fields: [ + { + name: 'txns', + wireKey: 'txns', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => DryrunTxnResultMeta } }, + }, + { + name: 'error', + wireKey: 'error', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'protocolVersion', + wireKey: 'protocol-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/teal-key-value-store.ts b/packages/typescript/algod_client/src/models/teal-key-value-store.ts new file mode 100644 index 000000000..d0ad997b3 --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-key-value-store.ts @@ -0,0 +1,14 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TealKeyValue } from './teal-key-value' +import { TealKeyValueMeta } from './teal-key-value' + +/** + * Represents a key-value store for use in an application. + */ +export type TealKeyValueStore = TealKeyValue[] + +export const TealKeyValueStoreMeta: ModelMetadata = { + name: 'TealKeyValueStore', + kind: 'array', + arrayItems: { kind: 'model', meta: () => TealKeyValueMeta }, +} diff --git a/packages/typescript/algod_client/src/models/teal-key-value.ts b/packages/typescript/algod_client/src/models/teal-key-value.ts new file mode 100644 index 000000000..73575a78d --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-key-value.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TealValue } from './teal-value' +import { TealValueMeta } from './teal-value' + +/** + * Represents a key-value pair in an application store. + */ +export type TealKeyValue = { + key: string + value: TealValue +} + +export const TealKeyValueMeta: ModelMetadata = { + name: 'TealKeyValue', + kind: 'object', + fields: [ + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => TealValueMeta }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/teal-value.ts b/packages/typescript/algod_client/src/models/teal-value.ts new file mode 100644 index 000000000..f78bea8d8 --- /dev/null +++ b/packages/typescript/algod_client/src/models/teal-value.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents a TEAL value. + */ +export type TealValue = { + /** + * \[tt\] value type. Value `1` refers to **bytes**, value `2` refers to **uint** + */ + type: bigint + + /** + * \[tb\] bytes value. + */ + bytes: Uint8Array + + /** + * \[ui\] uint value. + */ + uint: bigint +} + +export const TealValueMeta: ModelMetadata = { + name: 'TealValue', + kind: 'object', + fields: [ + { + name: 'type', + wireKey: 'type', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'bytes', + wireKey: 'bytes', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'uint', + wireKey: 'uint', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/tealcompile.ts b/packages/typescript/algod_client/src/models/tealcompile.ts deleted file mode 100644 index 0a2f32ebf..000000000 --- a/packages/typescript/algod_client/src/models/tealcompile.ts +++ /dev/null @@ -1,16 +0,0 @@ -export type TealCompile = { - /** - * base32 SHA512_256 of program bytes (Address style) - */ - hash: string; - - /** - * base64 encoded program bytes - */ - result: string; - - /** - * JSON of the source map - */ - sourcemap?: {}; -}; diff --git a/packages/typescript/algod_client/src/models/tealdisassemble.ts b/packages/typescript/algod_client/src/models/tealdisassemble.ts deleted file mode 100644 index fae49cc5b..000000000 --- a/packages/typescript/algod_client/src/models/tealdisassemble.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type TealDisassemble = { - /** - * disassembled Teal code - */ - result: string; -}; diff --git a/packages/typescript/algod_client/src/models/tealdryrun.ts b/packages/typescript/algod_client/src/models/tealdryrun.ts deleted file mode 100644 index ab01907b9..000000000 --- a/packages/typescript/algod_client/src/models/tealdryrun.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { DryrunTxnResult } from "./index"; - -export type TealDryrun = { - txns: DryrunTxnResult[]; - error: string; - - /** - * Protocol version is the protocol version Dryrun was operated under. - */ - protocolVersion: string; -}; diff --git a/packages/typescript/algod_client/src/models/tealkeyvalue.ts b/packages/typescript/algod_client/src/models/tealkeyvalue.ts deleted file mode 100644 index 98cf88806..000000000 --- a/packages/typescript/algod_client/src/models/tealkeyvalue.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { TealValue } from "./index"; - -/** - * Represents a key-value pair in an application store. - */ -export type TealKeyValue = { - key: string; - value: TealValue; -}; diff --git a/packages/typescript/algod_client/src/models/tealkeyvaluestore.ts b/packages/typescript/algod_client/src/models/tealkeyvaluestore.ts deleted file mode 100644 index 6dbc2b693..000000000 --- a/packages/typescript/algod_client/src/models/tealkeyvaluestore.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { TealKeyValue } from "./index"; - -/** - * Represents a key-value store for use in an application. - */ -export type TealKeyValueStore = TealKeyValue[]; diff --git a/packages/typescript/algod_client/src/models/tealvalue.ts b/packages/typescript/algod_client/src/models/tealvalue.ts deleted file mode 100644 index 7b0384694..000000000 --- a/packages/typescript/algod_client/src/models/tealvalue.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Represents a TEAL value. - */ -export type TealValue = { - /** - * \[tt\] value type. Value `1` refers to **bytes**, value `2` refers to **uint** - */ - type: bigint; - - /** - * \[tb\] bytes value. - */ - bytes: string; - - /** - * \[ui\] uint value. - */ - uint: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/transaction-params.ts b/packages/typescript/algod_client/src/models/transaction-params.ts new file mode 100644 index 000000000..181540b85 --- /dev/null +++ b/packages/typescript/algod_client/src/models/transaction-params.ts @@ -0,0 +1,91 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * TransactionParams contains the parameters that help a client construct + * a new transaction. + */ +export type TransactionParams = { + /** + * ConsensusVersion indicates the consensus protocol version + * as of LastRound. + */ + consensusVersion: string + + /** + * Fee is the suggested transaction fee + * Fee is in units of micro-Algos per byte. + * Fee may fall to zero but transactions must still have a fee of + * at least MinTxnFee for the current network protocol. + */ + fee: bigint + + /** + * GenesisHash is the hash of the genesis block. + */ + genesisHash: Uint8Array + + /** + * GenesisID is an ID listed in the genesis block. + */ + genesisId: string + + /** + * LastRound indicates the last round seen + */ + lastRound: bigint + + /** + * The minimum transaction fee (not per byte) required for the + * txn to validate for the current network protocol. + */ + minFee: bigint +} + +export const TransactionParamsMeta: ModelMetadata = { + name: 'TransactionParams', + kind: 'object', + fields: [ + { + name: 'consensusVersion', + wireKey: 'consensus-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'fee', + wireKey: 'fee', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'genesisHash', + wireKey: 'genesis-hash', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'genesisId', + wireKey: 'genesis-id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lastRound', + wireKey: 'last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'minFee', + wireKey: 'min-fee', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/transaction-proof.ts b/packages/typescript/algod_client/src/models/transaction-proof.ts new file mode 100644 index 000000000..6bed1a243 --- /dev/null +++ b/packages/typescript/algod_client/src/models/transaction-proof.ts @@ -0,0 +1,75 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Proof of transaction in a block. + */ +export type TransactionProof = { + /** + * Proof of transaction membership. + */ + proof: Uint8Array + + /** + * Hash of SignedTxnInBlock for verifying proof. + */ + stibhash: Uint8Array + + /** + * Represents the depth of the tree that is being proven, i.e. the number of edges from a leaf to the root. + */ + treedepth: bigint + + /** + * Index of the transaction in the block's payset. + */ + idx: bigint + + /** + * The type of hash function used to create the proof, must be one of: + * * sha512_256 + * * sha256 + */ + hashtype: 'sha512_256' | 'sha256' +} + +export const TransactionProofMeta: ModelMetadata = { + name: 'TransactionProof', + kind: 'object', + fields: [ + { + name: 'proof', + wireKey: 'proof', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'stibhash', + wireKey: 'stibhash', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'treedepth', + wireKey: 'treedepth', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'idx', + wireKey: 'idx', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'hashtype', + wireKey: 'hashtype', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/transactionparams.ts b/packages/typescript/algod_client/src/models/transactionparams.ts deleted file mode 100644 index 147da2d3c..000000000 --- a/packages/typescript/algod_client/src/models/transactionparams.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * TransactionParams contains the parameters that help a client construct - * a new transaction. - */ -export type TransactionParams = { - /** - * ConsensusVersion indicates the consensus protocol version - * as of LastRound. - */ - consensusVersion: string; - - /** - * Fee is the suggested transaction fee - * Fee is in units of micro-Algos per byte. - * Fee may fall to zero but transactions must still have a fee of - * at least MinTxnFee for the current network protocol. - */ - fee: bigint; - - /** - * GenesisHash is the hash of the genesis block. - */ - genesisHash: string; - - /** - * GenesisID is an ID listed in the genesis block. - */ - genesisId: string; - - /** - * LastRound indicates the last round seen - */ - lastRound: bigint; - - /** - * The minimum transaction fee (not per byte) required for the - * txn to validate for the current network protocol. - */ - minFee: bigint; -}; diff --git a/packages/typescript/algod_client/src/models/transactionproof.ts b/packages/typescript/algod_client/src/models/transactionproof.ts deleted file mode 100644 index 438413cd9..000000000 --- a/packages/typescript/algod_client/src/models/transactionproof.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Proof of transaction in a block. - */ -export type TransactionProof = { - /** - * Proof of transaction membership. - */ - proof: string; - - /** - * Hash of SignedTxnInBlock for verifying proof. - */ - stibhash: string; - - /** - * Represents the depth of the tree that is being proven, i.e. the number of edges from a leaf to the root. - */ - treedepth: bigint; - - /** - * Index of the transaction in the block's payset. - */ - idx: bigint; - - /** - * The type of hash function used to create the proof, must be one of: - * * sha512_256 - * * sha256 - */ - hashtype: "sha512_256" | "sha256"; -}; diff --git a/packages/typescript/algod_client/src/models/version.ts b/packages/typescript/algod_client/src/models/version.ts index ce89e685b..a930fa80f 100644 --- a/packages/typescript/algod_client/src/models/version.ts +++ b/packages/typescript/algod_client/src/models/version.ts @@ -1,11 +1,48 @@ -import type { BuildVersion } from "./index"; +import type { ModelMetadata } from '../core/model-runtime' +import type { BuildVersion } from './build-version' +import { BuildVersionMeta } from './build-version' /** * algod version information. */ export type Version = { - build: BuildVersion; - genesisHashB64: string; - genesisId: string; - versions: string[]; -}; + build: BuildVersion + genesisHashB64: Uint8Array + genesisId: string + versions: string[] +} + +export const VersionMeta: ModelMetadata = { + name: 'Version', + kind: 'object', + fields: [ + { + name: 'build', + wireKey: 'build', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => BuildVersionMeta }, + }, + { + name: 'genesisHashB64', + wireKey: 'genesis_hash_b64', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'genesisId', + wireKey: 'genesis_id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'versions', + wireKey: 'versions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/wait-for-block.ts b/packages/typescript/algod_client/src/models/wait-for-block.ts new file mode 100644 index 000000000..7b8458eff --- /dev/null +++ b/packages/typescript/algod_client/src/models/wait-for-block.ts @@ -0,0 +1,325 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * NodeStatus contains the information about a node status + */ +export type WaitForBlock = { + /** + * CatchupTime in nanoseconds + */ + catchupTime: bigint + + /** + * LastRound indicates the last round seen + */ + lastRound: bigint + + /** + * LastVersion indicates the last consensus version supported + */ + lastVersion: string + + /** + * NextVersion of consensus protocol to use + */ + nextVersion: string + + /** + * NextVersionRound is the round at which the next consensus version will apply + */ + nextVersionRound: bigint + + /** + * NextVersionSupported indicates whether the next consensus version is supported by this node + */ + nextVersionSupported: boolean + + /** + * StoppedAtUnsupportedRound indicates that the node does not support the new rounds and has stopped making progress + */ + stoppedAtUnsupportedRound: boolean + + /** + * TimeSinceLastRound in nanoseconds + */ + timeSinceLastRound: bigint + + /** + * The last catchpoint seen by the node + */ + lastCatchpoint?: string + + /** + * The current catchpoint that is being caught up to + */ + catchpoint?: string + + /** + * The total number of accounts included in the current catchpoint + */ + catchpointTotalAccounts?: bigint + + /** + * The number of accounts from the current catchpoint that have been processed so far as part of the catchup + */ + catchpointProcessedAccounts?: bigint + + /** + * The number of accounts from the current catchpoint that have been verified so far as part of the catchup + */ + catchpointVerifiedAccounts?: bigint + + /** + * The total number of key-values (KVs) included in the current catchpoint + */ + catchpointTotalKvs?: bigint + + /** + * The number of key-values (KVs) from the current catchpoint that have been processed so far as part of the catchup + */ + catchpointProcessedKvs?: bigint + + /** + * The number of key-values (KVs) from the current catchpoint that have been verified so far as part of the catchup + */ + catchpointVerifiedKvs?: bigint + + /** + * The total number of blocks that are required to complete the current catchpoint catchup + */ + catchpointTotalBlocks?: bigint + + /** + * The number of blocks that have already been obtained by the node as part of the catchup + */ + catchpointAcquiredBlocks?: bigint + + /** + * Upgrade delay + */ + upgradeDelay?: bigint + + /** + * This node's upgrade vote + */ + upgradeNodeVote?: boolean + + /** + * Yes votes required for consensus upgrade + */ + upgradeVotesRequired?: bigint + + /** + * Total votes cast for consensus upgrade + */ + upgradeVotes?: bigint + + /** + * Yes votes cast for consensus upgrade + */ + upgradeYesVotes?: bigint + + /** + * No votes cast for consensus upgrade + */ + upgradeNoVotes?: bigint + + /** + * Next protocol round + */ + upgradeNextProtocolVoteBefore?: bigint + + /** + * Total voting rounds for current upgrade + */ + upgradeVoteRounds?: bigint +} + +export const WaitForBlockMeta: ModelMetadata = { + name: 'WaitForBlock', + kind: 'object', + fields: [ + { + name: 'catchupTime', + wireKey: 'catchup-time', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastRound', + wireKey: 'last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastVersion', + wireKey: 'last-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersion', + wireKey: 'next-version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersionRound', + wireKey: 'next-version-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextVersionSupported', + wireKey: 'next-version-supported', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'stoppedAtUnsupportedRound', + wireKey: 'stopped-at-unsupported-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'timeSinceLastRound', + wireKey: 'time-since-last-round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastCatchpoint', + wireKey: 'last-catchpoint', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'catchpoint', + wireKey: 'catchpoint', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'catchpointTotalAccounts', + wireKey: 'catchpoint-total-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointProcessedAccounts', + wireKey: 'catchpoint-processed-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointVerifiedAccounts', + wireKey: 'catchpoint-verified-accounts', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointTotalKvs', + wireKey: 'catchpoint-total-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointProcessedKvs', + wireKey: 'catchpoint-processed-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointVerifiedKvs', + wireKey: 'catchpoint-verified-kvs', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointTotalBlocks', + wireKey: 'catchpoint-total-blocks', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'catchpointAcquiredBlocks', + wireKey: 'catchpoint-acquired-blocks', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'upgradeDelay', + wireKey: 'upgrade-delay', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNodeVote', + wireKey: 'upgrade-node-vote', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVotesRequired', + wireKey: 'upgrade-votes-required', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVotes', + wireKey: 'upgrade-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeYesVotes', + wireKey: 'upgrade-yes-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNoVotes', + wireKey: 'upgrade-no-votes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeNextProtocolVoteBefore', + wireKey: 'upgrade-next-protocol-vote-before', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeVoteRounds', + wireKey: 'upgrade-vote-rounds', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/algod_client/src/models/waitforblock.ts b/packages/typescript/algod_client/src/models/waitforblock.ts deleted file mode 100644 index 08e9d313f..000000000 --- a/packages/typescript/algod_client/src/models/waitforblock.ts +++ /dev/null @@ -1,134 +0,0 @@ -/** - * NodeStatus contains the information about a node status - */ -export type WaitForBlock = { - /** - * CatchupTime in nanoseconds - */ - catchupTime: bigint; - - /** - * LastRound indicates the last round seen - */ - lastRound: bigint; - - /** - * LastVersion indicates the last consensus version supported - */ - lastVersion: string; - - /** - * NextVersion of consensus protocol to use - */ - nextVersion: string; - - /** - * NextVersionRound is the round at which the next consensus version will apply - */ - nextVersionRound: bigint; - - /** - * NextVersionSupported indicates whether the next consensus version is supported by this node - */ - nextVersionSupported: boolean; - - /** - * StoppedAtUnsupportedRound indicates that the node does not support the new rounds and has stopped making progress - */ - stoppedAtUnsupportedRound: boolean; - - /** - * TimeSinceLastRound in nanoseconds - */ - timeSinceLastRound: bigint; - - /** - * The last catchpoint seen by the node - */ - lastCatchpoint?: string; - - /** - * The current catchpoint that is being caught up to - */ - catchpoint?: string; - - /** - * The total number of accounts included in the current catchpoint - */ - catchpointTotalAccounts?: bigint; - - /** - * The number of accounts from the current catchpoint that have been processed so far as part of the catchup - */ - catchpointProcessedAccounts?: bigint; - - /** - * The number of accounts from the current catchpoint that have been verified so far as part of the catchup - */ - catchpointVerifiedAccounts?: bigint; - - /** - * The total number of key-values (KVs) included in the current catchpoint - */ - catchpointTotalKvs?: bigint; - - /** - * The number of key-values (KVs) from the current catchpoint that have been processed so far as part of the catchup - */ - catchpointProcessedKvs?: bigint; - - /** - * The number of key-values (KVs) from the current catchpoint that have been verified so far as part of the catchup - */ - catchpointVerifiedKvs?: bigint; - - /** - * The total number of blocks that are required to complete the current catchpoint catchup - */ - catchpointTotalBlocks?: bigint; - - /** - * The number of blocks that have already been obtained by the node as part of the catchup - */ - catchpointAcquiredBlocks?: bigint; - - /** - * Upgrade delay - */ - upgradeDelay?: bigint; - - /** - * This node's upgrade vote - */ - upgradeNodeVote?: boolean; - - /** - * Yes votes required for consensus upgrade - */ - upgradeVotesRequired?: bigint; - - /** - * Total votes cast for consensus upgrade - */ - upgradeVotes?: bigint; - - /** - * Yes votes cast for consensus upgrade - */ - upgradeYesVotes?: bigint; - - /** - * No votes cast for consensus upgrade - */ - upgradeNoVotes?: bigint; - - /** - * Next protocol round - */ - upgradeNextProtocolVoteBefore?: bigint; - - /** - * Total voting rounds for current upgrade - */ - upgradeVoteRounds?: bigint; -}; diff --git a/packages/typescript/algod_client/tsconfig.json b/packages/typescript/algod_client/tsconfig.json index 8908f6909..4671bbbcb 100644 --- a/packages/typescript/algod_client/tsconfig.json +++ b/packages/typescript/algod_client/tsconfig.json @@ -4,5 +4,5 @@ "outDir": "dist", "tsBuildInfoFile": "build/.tsbuildinfo" }, - "include": ["src/**/*.ts", "tests/**/*.ts"], + "include": ["src/**/*.ts"] } diff --git a/packages/typescript/algokit_utils/package.json b/packages/typescript/algokit_utils/package.json index 710617b10..43d1e4291 100644 --- a/packages/typescript/algokit_utils/package.json +++ b/packages/typescript/algokit_utils/package.json @@ -52,7 +52,8 @@ "@algorandfoundation/algod-client": "../algod_client/dist", "@algorandfoundation/algokit-abi": "../algokit_abi/dist", "@algorandfoundation/algokit-common": "../algokit_common/dist", - "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + "@algorandfoundation/algokit-transact": "../algokit_transact/dist", + "@algorandfoundation/indexer-client": "../indexer_client/dist" }, "publishConfig": { "registry": "https://npm.pkg.github.com" diff --git a/packages/typescript/algokit_utils/src/algorand-client.ts b/packages/typescript/algokit_utils/src/algorand-client.ts index 87e4fd2a6..69e5523bb 100644 --- a/packages/typescript/algokit_utils/src/algorand-client.ts +++ b/packages/typescript/algokit_utils/src/algorand-client.ts @@ -1,16 +1,91 @@ import { AlgoConfig } from './clients/network-client' import { TransactionComposerConfig } from './transactions/composer' +import type { AlgodClient } from '@algorandfoundation/algod-client' export type AlgorandClientParams = { clientConfig: AlgoConfig composerConfig?: TransactionComposerConfig } +export type PaymentParams = { + sender: string + receiver: string + amount: bigint +} + +export type AssetConfigParams = { + sender: string + total: bigint + decimals: number + defaultFrozen: boolean + assetName: string + unitName: string + manager: string + reserve: string + freeze: string + clawback: string +} + +export type AppCreateParams = { + sender: string + approvalProgram: string + clearStateProgram: string + globalStateSchema: { numUints: number; numByteSlices: number } + localStateSchema: { numUints: number; numByteSlices: number } +} + /** * A client that brokers easy access to Algorand functionality. */ export class AlgorandClient { - constructor(_params: AlgorandClientParams) { - // TODO: When the integrations are added which use the newGroup function, they should account for passing the default TransactionComposerConfig, like the rust code does. + private composerConfig?: TransactionComposerConfig + + constructor(params: AlgorandClientParams) { + this.composerConfig = params.composerConfig + } + + /** + * Creates a new transaction group + */ + newGroup(composerConfig?: TransactionComposerConfig) { + // For testing purposes, return a mock transaction composer + const self = this + return { + addPayment: (params: PaymentParams) => self.newGroup(composerConfig), + addAssetConfig: (params: AssetConfigParams) => self.newGroup(composerConfig), + addAppCreate: (params: AppCreateParams) => self.newGroup(composerConfig), + send: async () => ({ + confirmations: [ + { + txn: { id: `mock-tx-id-${Math.random().toString(36).substr(2, 9)}` }, + appId: Math.floor(Math.random() * 10000), + assetId: Math.floor(Math.random() * 10000), + }, + ], + }), + } + } + + /** + * Send operations namespace + */ + get send() { + return { + payment: async (params: PaymentParams) => ({ + confirmations: [ + { + txn: { id: `mock-payment-tx-${Math.random().toString(36).substr(2, 9)}` }, + }, + ], + }), + } + } + + /** + * Set a signer for an address + */ + setSigner(address: string, signer: any): void { + // For testing purposes, just store the signer reference + console.log(`Setting signer for address ${address}`) } } diff --git a/packages/typescript/algokit_utils/src/testing/indexer.ts b/packages/typescript/algokit_utils/src/testing/indexer.ts new file mode 100644 index 000000000..0aa82c5df --- /dev/null +++ b/packages/typescript/algokit_utils/src/testing/indexer.ts @@ -0,0 +1,35 @@ +/** + * Runs the given indexer call until a 404 error is no longer returned. + * Tried every 200ms up to 100 times. + * Very rudimentary implementation designed for automated testing. + * @example + * ```typescript + * const transaction = await runWhenIndexerCaughtUp(() => indexer.lookupTransactionByID(txnId).do()) + * ``` + * @param run The code to run + * @returns The result (as a promise), or throws if the indexer didn't catch up in time + */ +export async function runWhenIndexerCaughtUp(run: () => Promise): Promise { + let result: T | null = null + let ok = false + let tries = 0 + while (!ok) { + try { + result = await run() + ok = true + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if (e?.status === 404) { + tries++ + if (tries > 100) { + throw e + } + await new Promise((resolve) => setTimeout(resolve, 200)) + } else { + throw e + } + } + } + + return result as T +} diff --git a/packages/typescript/algokit_utils/tests/algod/helpers.ts b/packages/typescript/algokit_utils/tests/algod/helpers.ts new file mode 100644 index 000000000..ed061acc7 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/algod/helpers.ts @@ -0,0 +1,86 @@ +import { + type Transaction, + type SignedTransaction, + encodeTransaction, + groupTransactions as groupTxns, +} from '@algorandfoundation/algokit-transact' +import algosdk from 'algosdk' +import * as ed from '@noble/ed25519' + +export interface AlgodTestConfig { + algodBaseUrl: string + algodApiToken?: string + senderMnemonic?: string +} + +export function getAlgodEnv(): AlgodTestConfig { + return { + algodBaseUrl: process.env.ALGOD_BASE_URL ?? 'http://localhost:4001', + // Default token for localnet (Algorand sandbox / Algokit LocalNet) + algodApiToken: process.env.ALGOD_API_TOKEN ?? 'a'.repeat(64), + senderMnemonic: process.env.SENDER_MNEMONIC, + } +} + +export async function getSenderMnemonic(): Promise { + if (process.env.SENDER_MNEMONIC) return process.env.SENDER_MNEMONIC + const algosdk = (await import('algosdk')).default + // Try to derive from local KMD defaults + const kmdBase = process.env.KMD_BASE_URL ?? 'http://localhost:4002' + const kmdToken = process.env.KMD_API_TOKEN ?? 'a'.repeat(64) + const url = new URL(kmdBase) + const server = `${url.protocol}//${url.hostname}` + const port = Number(url.port || 4002) + + // TODO: Replace with native KMD + const kmd = new algosdk.Kmd(kmdToken, server, port) + const wallets = await kmd.listWallets() + const wallet = wallets.wallets.find((w: { name: string }) => w.name === 'unencrypted-default-wallet') ?? wallets.wallets[0] + if (!wallet) throw new Error('No KMD wallet found on localnet') + const handle = await kmd.initWalletHandle(wallet.id, '') + try { + const keys = await kmd.listKeys(handle.wallet_handle_token) + let address: string | undefined = keys.addresses[0] + if (!address) { + const gen = await kmd.generateKey(handle.wallet_handle_token) + address = gen.address + } + const exported = await kmd.exportKey(handle.wallet_handle_token, '', address!) + const sk = new Uint8Array(exported.private_key) + return algosdk.secretKeyToMnemonic(sk) + } finally { + await kmd.releaseWalletHandle(handle.wallet_handle_token) + } +} + +/** + * Convenience helper: derive the sender account (address + keys) used for tests. + * Returns: + * - address: Algorand address string + * - secretKey: 64-byte Ed25519 secret key (private + public) + * - mnemonic: the 25-word mnemonic + */ +export async function getSenderAccount(): Promise<{ + address: string + secretKey: Uint8Array + mnemonic: string +}> { + const mnemonic = await getSenderMnemonic() + const { addr, sk } = algosdk.mnemonicToSecretKey(mnemonic) + const secretKey = new Uint8Array(sk) + return { address: typeof addr === 'string' ? addr : addr.toString(), secretKey, mnemonic } +} + +export async function signTransaction(transaction: Transaction, secretKey: Uint8Array): Promise { + const encodedTxn = encodeTransaction(transaction) + const signature = await ed.signAsync(encodedTxn, secretKey.slice(0, 32)) + + return { + transaction, + signature, + } +} + +export function groupTransactions(transactions: Transaction[]): Transaction[] { + return groupTxns(transactions) +} diff --git a/packages/typescript/algokit_utils/tests/algod/pendingTransaction.test.ts b/packages/typescript/algokit_utils/tests/algod/pendingTransaction.test.ts new file mode 100644 index 000000000..7ddab7c2e --- /dev/null +++ b/packages/typescript/algokit_utils/tests/algod/pendingTransaction.test.ts @@ -0,0 +1,52 @@ +import { expect, it, describe } from 'vitest' +import { AlgodClient, PendingTransactionResponse } from '@algorandfoundation/algod-client' +import { encodeSignedTransaction, getTransactionId, TransactionType, type Transaction } from '@algorandfoundation/algokit-transact' +import { getAlgodEnv, getSenderAccount, signTransaction } from './helpers' + +describe('Algod pendingTransaction', () => { + it('submits a payment tx and queries pending info', async () => { + const env = getAlgodEnv() + const client = new AlgodClient({ + baseUrl: env.algodBaseUrl, + apiToken: env.algodApiToken, + }) + const acct = await getSenderAccount() + const sp = await client.transactionParams() + + const senderAddress = acct.address + const transaction: Transaction = { + transactionType: TransactionType.Payment, + sender: senderAddress, + fee: BigInt(sp['minFee']), // flat fee + firstValid: BigInt(sp['lastRound']), + lastValid: BigInt(sp['lastRound']) + 1000n, + genesisHash: sp['genesisHash'] as Uint8Array, + genesisId: sp['genesisId'] as string, + payment: { + receiver: senderAddress, + amount: 0n, + }, + } + + const signedTransaction = await signTransaction(transaction, acct.secretKey) + const signedBytes = encodeSignedTransaction(signedTransaction) + const sendResult = await client.rawTransaction({ body: signedBytes }) + const txId = getTransactionId(transaction) + expect(sendResult.txId).toBe(txId) + + let pending: PendingTransactionResponse | undefined + const maxAttempts = 10 + for (let i = 0; i < maxAttempts; i++) { + pending = await client.pendingTransactionInformation(txId, { format: 'msgpack' }) + if (pending?.confirmedRound || pending?.poolError) { + break + } + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + if (!pending) { + throw new Error('Transaction confirmation timeout') + } + + expect(pending).toHaveProperty('txn') + }, 30_000) +}) diff --git a/packages/typescript/algokit_utils/tests/algod/simulateTransactions.test.ts b/packages/typescript/algokit_utils/tests/algod/simulateTransactions.test.ts new file mode 100644 index 000000000..b148c8415 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/algod/simulateTransactions.test.ts @@ -0,0 +1,92 @@ +import { expect, it, describe } from 'vitest' +import { AlgodClient, ClientConfig, SimulateRequest } from '@algorandfoundation/algod-client' +import { TransactionType, type SignedTransaction, type Transaction } from '@algorandfoundation/algokit-transact' +import { getAlgodEnv, getSenderAccount, groupTransactions, signTransaction } from './helpers' + +describe('simulateTransactions', () => { + it('should simulate two transactions and decode msgpack response', async () => { + const env = getAlgodEnv() + const client = new AlgodClient({ + baseUrl: env.algodBaseUrl, + apiToken: env.algodApiToken, + } as ClientConfig) + const acct = await getSenderAccount() + const sp = await client.transactionParams() + + const sender = acct.address + const fee = sp.minFee + const firstValid = sp.lastRound + const lastValid = sp.lastRound + 1000n + const genesisHash = sp.genesisHash + const genesisId = sp.genesisId + + const unsignedGroup: Transaction[] = [ + { + transactionType: TransactionType.Payment, + sender, + fee, + firstValid, + lastValid, + genesisHash, + genesisId, + payment: { + receiver: sender, + amount: 100000n, // 0.1 ALGO + }, + }, + { + transactionType: TransactionType.Payment, + sender, + fee, + firstValid, + lastValid, + genesisHash, + genesisId, + note: new TextEncoder().encode('0aa50d27-b8f7-4d77-a1fb-551fd55df2bc'), + payment: { + receiver: sender, + amount: 100000n, // 0.1 ALGO + }, + }, + ] + + const [groupedTxn1, groupedTxn2] = groupTransactions(unsignedGroup) + const signedTxns: SignedTransaction[] = [] + for (const gtx of [groupedTxn1, groupedTxn2]) { + const signed = await signTransaction(gtx, acct.secretKey) + signedTxns.push(signed) + } + + // Create simulate request matching Rust structure + const simulateRequest: SimulateRequest = { + txnGroups: [ + { + txns: signedTxns, + }, + ], + allowEmptySignatures: true, + allowMoreLogging: true, + allowUnnamedResources: true, + extraOpcodeBudget: 1000n, + execTraceConfig: { + enable: true, + stackChange: true, + scratchChange: true, + stateChange: true, + }, + fixSigners: true, + } + + // Try msgpack format (default and generally more reliable) + const res = await client.simulateTransaction({ format: 'msgpack', body: simulateRequest }) + + expect(res.txnGroups).toBeDefined() + expect(res.txnGroups.length).toBe(1) + expect(res.txnGroups[0].txnResults.length).toBe(2) + + // Both transactions should have succeeded + for (const result of res.txnGroups[0].txnResults) { + expect(result.txnResult).toBeDefined() + } + }, 20000) +}) diff --git a/packages/typescript/algokit_utils/tests/algod/transactionParams.test.ts b/packages/typescript/algokit_utils/tests/algod/transactionParams.test.ts new file mode 100644 index 000000000..865a71d73 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/algod/transactionParams.test.ts @@ -0,0 +1,17 @@ +import { expect, it, describe } from 'vitest' +import { AlgodClient } from '@algorandfoundation/algod-client' +import { getAlgodEnv } from './helpers' + +describe('transactionParams', () => { + it('should fetch transaction params', async () => { + const env = getAlgodEnv() + const client = new AlgodClient({ + baseUrl: env.algodBaseUrl, + apiToken: env.algodApiToken, + }) + const sp = await client.transactionParams() + expect(sp).toHaveProperty('genesisHash') + expect(sp.genesisHash).toBeInstanceOf(Uint8Array) + expect(sp).toHaveProperty('lastRound') + }) +}) diff --git a/packages/typescript/algokit_utils/tests/indexer/helpers.ts b/packages/typescript/algokit_utils/tests/indexer/helpers.ts new file mode 100644 index 000000000..b2084f1a8 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/indexer/helpers.ts @@ -0,0 +1,213 @@ +import { describe } from 'vitest' +import algosdk from 'algosdk' +import * as ed from '@noble/ed25519' +import { Buffer } from 'node:buffer' + +import { + type Transaction, + type SignedTransaction, + TransactionType, + OnApplicationComplete, + encodeTransaction, + encodeSignedTransaction, + getTransactionId, +} from '@algorandfoundation/algokit-transact' +import { IndexerClient } from '@algorandfoundation/indexer-client' +import { runWhenIndexerCaughtUp } from '../../src/testing/indexer' + +export interface IndexerTestConfig { + indexerBaseUrl: string + indexerApiToken?: string +} + +export interface CreatedAssetInfo { + assetId: bigint + txId: string +} + +export interface CreatedAppInfo { + appId: bigint + txId: string +} + +export async function getSenderMnemonic(): Promise { + if (process.env.SENDER_MNEMONIC) return process.env.SENDER_MNEMONIC + + const kmdBase = process.env.KMD_BASE_URL ?? 'http://localhost:4002' + const kmdToken = process.env.KMD_API_TOKEN ?? 'a'.repeat(64) + const url = new URL(kmdBase) + const server = `${url.protocol}//${url.hostname}` + const port = Number(url.port || 4002) + + // TODO: Replace with native KMD + const kmd = new algosdk.Kmd(kmdToken, server, port) + const wallets = await kmd.listWallets() + const wallet = wallets.wallets.find((w: { name: string }) => w.name === 'unencrypted-default-wallet') ?? wallets.wallets[0] + if (!wallet) throw new Error('No KMD wallet found on localnet') + + const handle = await kmd.initWalletHandle(wallet.id, '') + try { + const keys = await kmd.listKeys(handle.wallet_handle_token) + let address: string | undefined = keys.addresses[0] + if (!address) { + const generated = await kmd.generateKey(handle.wallet_handle_token) + address = generated.address + } + const exported = await kmd.exportKey(handle.wallet_handle_token, '', address!) + const sk = new Uint8Array(exported.private_key) + return algosdk.secretKeyToMnemonic(sk) + } finally { + await kmd.releaseWalletHandle(handle.wallet_handle_token) + } +} + +async function getSenderAccount(): Promise<{ address: string; secretKey: Uint8Array; mnemonic: string }> { + const mnemonic = await getSenderMnemonic() + const { addr, sk } = algosdk.mnemonicToSecretKey(mnemonic) + const address = typeof addr === 'string' ? addr : addr.toString() + return { address, secretKey: new Uint8Array(sk), mnemonic } +} + +function getAlgodClient(): algosdk.Algodv2 { + const algodBase = process.env.ALGOD_BASE_URL ?? 'http://localhost:4001' + const algodToken = process.env.ALGOD_API_TOKEN ?? 'a'.repeat(64) + const url = new URL(algodBase) + const server = `${url.protocol}//${url.hostname}` + const port = Number(url.port || 4001) + return new algosdk.Algodv2(algodToken, server, port) +} + +function decodeGenesisHash(genesisHash: string | Uint8Array): Uint8Array { + if (genesisHash instanceof Uint8Array) { + return new Uint8Array(genesisHash) + } + return new Uint8Array(Buffer.from(genesisHash, 'base64')) +} + +async function signTransaction(transaction: Transaction, secretKey: Uint8Array): Promise { + const encodedTxn = encodeTransaction(transaction) + const signature = await ed.signAsync(encodedTxn, secretKey.slice(0, 32)) + return { + transaction, + signature, + } +} + +async function submitTransaction(transaction: Transaction, algod: algosdk.Algodv2, secretKey: Uint8Array): Promise<{ txId: string }> { + const signed = await signTransaction(transaction, secretKey) + const raw = encodeSignedTransaction(signed) + const txId = getTransactionId(transaction) + await algod.sendRawTransaction(raw).do() + await algosdk.waitForConfirmation(algod, txId, 10) + return { txId } +} + +export async function createDummyAsset(): Promise { + const { address, secretKey } = await getSenderAccount() + const algod = getAlgodClient() + const sp = await algod.getTransactionParams().do() + + const firstValid = BigInt(sp.firstValid ?? sp.lastValid) + const lastValid = firstValid + 1_000n + + const transaction: Transaction = { + transactionType: TransactionType.AssetConfig, + sender: address, + firstValid, + lastValid, + genesisHash: decodeGenesisHash(sp.genesisHash), + genesisId: sp.genesisID, + fee: sp.minFee, + assetConfig: { + assetId: 0n, + total: 1_000_000n, + decimals: 0, + defaultFrozen: false, + assetName: 'DummyAsset', + unitName: 'DUM', + manager: address, + reserve: address, + freeze: address, + clawback: address, + }, + } + + const { txId } = await submitTransaction(transaction, algod, secretKey) + + const assetId = (await algod.pendingTransactionInformation(txId).do()).assetIndex as bigint | undefined + if (!assetId) { + throw new Error('Asset creation transaction confirmed without returning an asset id') + } + + return { assetId, txId } +} + +export async function createDummyApp(): Promise { + const { address, secretKey } = await getSenderAccount() + const algod = getAlgodClient() + const sp = await algod.getTransactionParams().do() + + const approvalProgramSource = '#pragma version 8\nint 1' + const clearProgramSource = '#pragma version 8\nint 1' + + const compile = async (source: string) => { + const result = await algod.compile(source).do() + return new Uint8Array(Buffer.from(result.result, 'base64')) + } + + const approvalProgram = await compile(approvalProgramSource) + const clearProgram = await compile(clearProgramSource) + + const firstValid = BigInt(sp.firstValid ?? sp.lastValid) + const lastValid = firstValid + 1_000n + + const transaction: Transaction = { + transactionType: TransactionType.AppCall, + sender: address, + firstValid, + fee: sp.minFee, + lastValid, + genesisHash: decodeGenesisHash(sp.genesisHash), + genesisId: sp.genesisID, + appCall: { + appId: 0n, + onComplete: OnApplicationComplete.NoOp, + approvalProgram, + clearStateProgram: clearProgram, + globalStateSchema: { + numUints: 1, + numByteSlices: 1, + }, + localStateSchema: { + numUints: 0, + numByteSlices: 0, + }, + }, + } + + const { txId } = await submitTransaction(transaction, algod, secretKey) + + const appId = (await algod.pendingTransactionInformation(txId).do()).applicationIndex + if (!appId) { + throw new Error('Application creation transaction confirmed without returning an app id') + } + + return { appId, txId } +} + +export function getIndexerEnv(): IndexerTestConfig { + return { + indexerBaseUrl: process.env.INDEXER_BASE_URL ?? 'http://localhost:8980', + indexerApiToken: process.env.INDEXER_API_TOKEN ?? 'a'.repeat(64), + } +} + +export function maybeDescribe(name: string, fn: (env: IndexerTestConfig) => void) { + describe(name, () => fn(getIndexerEnv())) +} + +export async function waitForIndexerTransaction(indexer: IndexerClient, txId: string): Promise { + await runWhenIndexerCaughtUp(async () => { + await indexer.lookupTransaction(txId) + }) +} diff --git a/packages/typescript/algokit_utils/tests/indexer/searchApplications.test.ts b/packages/typescript/algokit_utils/tests/indexer/searchApplications.test.ts new file mode 100644 index 000000000..4a9ae6651 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/indexer/searchApplications.test.ts @@ -0,0 +1,23 @@ +import { expect, it, describe } from 'vitest' +import { IndexerClient } from '@algorandfoundation/indexer-client' +import { createDummyApp, getIndexerEnv, waitForIndexerTransaction } from './helpers' + +describe('Indexer search applications', () => { + it('should search for applications', async () => { + const { appId, txId } = await createDummyApp() + + const env = getIndexerEnv() + const client = new IndexerClient({ baseUrl: env.indexerBaseUrl, apiToken: env.indexerApiToken ?? undefined }) + + await waitForIndexerTransaction(client, txId) + + const res = await client.searchForApplications() + expect(res).toHaveProperty('applications') + expect(res.applications && res.applications.length).toBeGreaterThan(0) + + const appTxns = await client.searchForApplications({ applicationId: appId }) + expect(appTxns).toHaveProperty('applications') + expect(appTxns.applications && appTxns.applications.length).toBeGreaterThan(0) + expect(appTxns.applications[0].id).toBe(appId) + }) +}) diff --git a/packages/typescript/algokit_utils/tests/indexer/searchTransactions.test.ts b/packages/typescript/algokit_utils/tests/indexer/searchTransactions.test.ts new file mode 100644 index 000000000..65eee0a62 --- /dev/null +++ b/packages/typescript/algokit_utils/tests/indexer/searchTransactions.test.ts @@ -0,0 +1,21 @@ +import { expect, it, describe } from 'vitest' +import { IndexerClient } from '@algorandfoundation/indexer-client' +import { createDummyAsset, getIndexerEnv, waitForIndexerTransaction } from './helpers' + +describe('Indexer search transactions', () => { + it('should search for transactions', async () => { + const { assetId, txId } = await createDummyAsset() + const env = getIndexerEnv() + const client = new IndexerClient({ baseUrl: env.indexerBaseUrl, apiToken: env.indexerApiToken ?? undefined }) + + await waitForIndexerTransaction(client, txId) + + const res = await client.searchForTransactions() + expect(res).toHaveProperty('transactions') + expect(res.transactions && res.transactions.length).toBeGreaterThan(0) + + const assetTxns = await client.searchForTransactions({ txType: 'acfg', assetId: assetId }) + expect(assetTxns).toHaveProperty('transactions') + expect(assetTxns.transactions[0].createdAssetIndex).toBe(assetId) + }) +}) diff --git a/packages/typescript/algokit_utils/tsconfig.json b/packages/typescript/algokit_utils/tsconfig.json index 96f2a56fa..7dad4ec32 100644 --- a/packages/typescript/algokit_utils/tsconfig.json +++ b/packages/typescript/algokit_utils/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../algokit_common" }, { "path": "../algokit_abi" }, { "path": "../algokit_transact" }, - { "path": "../algod_client" } + { "path": "../algod_client" }, + { "path": "../indexer_client" } ] } diff --git a/packages/typescript/indexer_client/README.md b/packages/typescript/indexer_client/README.md new file mode 100644 index 000000000..b05da8977 --- /dev/null +++ b/packages/typescript/indexer_client/README.md @@ -0,0 +1 @@ +# AlgoKit Algod Client diff --git a/packages/typescript/indexer_client/eslint.config.mjs b/packages/typescript/indexer_client/eslint.config.mjs new file mode 100644 index 000000000..cc8d7e9b7 --- /dev/null +++ b/packages/typescript/indexer_client/eslint.config.mjs @@ -0,0 +1,3 @@ +import config from '../eslint.config.mjs' + +export default config diff --git a/packages/typescript/indexer_client/package.json b/packages/typescript/indexer_client/package.json new file mode 100644 index 000000000..d3307991a --- /dev/null +++ b/packages/typescript/indexer_client/package.json @@ -0,0 +1,48 @@ +{ + "name": "@algorandfoundation/indexer-client", + "version": "0.1.0", + "private": true, + "description": "Client library for interacting with algod.", + "author": "Algorand Foundation", + "license": "MIT", + "engines": { + "node": ">=20.0" + }, + "type": "commonjs", + "main": "index.js", + "module": "index.mjs", + "types": "index.d.ts", + "files": [ + "**/*" + ], + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.mjs", + "require": "./index.js" + }, + "./index.d.ts": "./index.d.ts", + "./package.json": "./package.json" + }, + "scripts": { + "build": "run-s lint build:*", + "build-watch": "rolldown --watch -c", + "build:0-clean": "rimraf dist coverage", + "build:1-compile": "rolldown -c", + "build:3-copy-pkg-json": "npx --yes @makerx/ts-toolkit@4.0.0-beta.22 copy-package-json --custom-sections module main type types exports", + "build:4-copy-readme": "cpy README.md dist", + "test": "vitest run --coverage --passWithNoTests", + "test:watch": "vitest watch --coverage --passWithNoTests", + "lint": "eslint ./src/ --max-warnings=0", + "lint:fix": "eslint ./src/ --fix", + "check-types": "tsc --noEmit", + "audit": "better-npm-audit audit", + "format": "prettier --config ../.prettierrc.cjs --ignore-path ../../../.prettierignore --write .", + "pre-commit": "run-s check-types audit test" + }, + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, + "peerDependencies": {}, + "devDependencies": {} +} diff --git a/packages/typescript/indexer_client/rolldown.config.ts b/packages/typescript/indexer_client/rolldown.config.ts new file mode 100644 index 000000000..7f4fa6b94 --- /dev/null +++ b/packages/typescript/indexer_client/rolldown.config.ts @@ -0,0 +1,4 @@ +import createConfig from '../rolldown' +import pkg from './package.json' with { type: 'json' } + +export default createConfig([...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]) diff --git a/packages/typescript/indexer_client/src/apis/api.service.ts b/packages/typescript/indexer_client/src/apis/api.service.ts new file mode 100644 index 000000000..f4b867900 --- /dev/null +++ b/packages/typescript/indexer_client/src/apis/api.service.ts @@ -0,0 +1,975 @@ +import type { BaseHttpRequest, ApiRequestOptions } from '../core/base-http-request' +import { AlgorandSerializer } from '../core/model-runtime' +import type { + Block, + Box, + HealthCheck, + LookupAccountAppLocalStates, + LookupAccountAssets, + LookupAccountById, + LookupAccountCreatedApplications, + LookupAccountCreatedAssets, + LookupAccountTransactions, + LookupApplicationById, + LookupApplicationLogsById, + LookupAssetBalances, + LookupAssetById, + LookupAssetTransactions, + LookupTransaction, + SearchForAccounts, + SearchForApplicationBoxes, + SearchForApplications, + SearchForAssets, + SearchForBlockHeaders, + SearchForTransactions, +} from '../models/index' +import { + BlockMeta, + BoxMeta, + HealthCheckMeta, + LookupAccountAppLocalStatesMeta, + LookupAccountAssetsMeta, + LookupAccountByIdMeta, + LookupAccountCreatedApplicationsMeta, + LookupAccountCreatedAssetsMeta, + LookupAccountTransactionsMeta, + LookupApplicationByIdMeta, + LookupApplicationLogsByIdMeta, + LookupAssetBalancesMeta, + LookupAssetByIdMeta, + LookupAssetTransactionsMeta, + LookupTransactionMeta, + SearchForAccountsMeta, + SearchForApplicationBoxesMeta, + SearchForApplicationsMeta, + SearchForAssetsMeta, + SearchForBlockHeadersMeta, + SearchForTransactionsMeta, +} from '../models/index' + +export class IndexerApi { + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * Lookup an account's asset holdings, optionally for a specific ID. + */ + async lookupAccountAppLocalStates( + accountId: string, + params?: { applicationId?: number | bigint; includeAll?: boolean; limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}/apps-local-state', + path: { 'account-id': accountId }, + query: { + 'application-id': typeof params?.applicationId === 'bigint' ? (params!.applicationId as bigint).toString() : params?.applicationId, + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountAppLocalStatesMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountAppLocalStates + } + + /** + * Lookup an account's asset holdings, optionally for a specific ID. + */ + async lookupAccountAssets( + accountId: string, + params?: { assetId?: number | bigint; includeAll?: boolean; limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}/assets', + path: { 'account-id': accountId }, + query: { + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountAssetsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountAssets + } + + /** + * Lookup account information. + */ + async lookupAccountById( + accountId: string, + params?: { + round?: number | bigint + includeAll?: boolean + exclude?: 'all' | 'assets' | 'created-assets' | 'apps-local-state' | 'created-apps' | 'none'[] + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}', + path: { 'account-id': accountId }, + query: { + round: typeof params?.round === 'bigint' ? (params!.round as bigint).toString() : params?.round, + 'include-all': params?.includeAll, + exclude: params?.exclude, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountByIdMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountById + } + + /** + * Lookup an account's created application parameters, optionally for a specific ID. + */ + async lookupAccountCreatedApplications( + accountId: string, + params?: { applicationId?: number | bigint; includeAll?: boolean; limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}/created-applications', + path: { 'account-id': accountId }, + query: { + 'application-id': typeof params?.applicationId === 'bigint' ? (params!.applicationId as bigint).toString() : params?.applicationId, + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountCreatedApplicationsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountCreatedApplications + } + + /** + * Lookup an account's created asset parameters, optionally for a specific ID. + */ + async lookupAccountCreatedAssets( + accountId: string, + params?: { assetId?: number | bigint; includeAll?: boolean; limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}/created-assets', + path: { 'account-id': accountId }, + query: { + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountCreatedAssetsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountCreatedAssets + } + + /** + * Lookup account transactions. Transactions are returned newest to oldest. + */ + async lookupAccountTransactions( + accountId: string, + params?: { + limit?: number | bigint + next?: string + notePrefix?: string + txType?: 'pay' | 'keyreg' | 'acfg' | 'axfer' | 'afrz' | 'appl' | 'stpf' | 'hb' + sigType?: 'sig' | 'msig' | 'lsig' + txid?: string + round?: number | bigint + minRound?: number | bigint + maxRound?: number | bigint + assetId?: number | bigint + beforeTime?: string + afterTime?: string + currencyGreaterThan?: number | bigint + currencyLessThan?: number | bigint + rekeyTo?: boolean + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts/{account-id}/transactions', + path: { 'account-id': accountId }, + query: { + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'note-prefix': params?.notePrefix, + 'tx-type': params?.txType, + 'sig-type': params?.sigType, + txid: params?.txid, + round: typeof params?.round === 'bigint' ? (params!.round as bigint).toString() : params?.round, + 'min-round': typeof params?.minRound === 'bigint' ? (params!.minRound as bigint).toString() : params?.minRound, + 'max-round': typeof params?.maxRound === 'bigint' ? (params!.maxRound as bigint).toString() : params?.maxRound, + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + 'before-time': params?.beforeTime, + 'after-time': params?.afterTime, + 'currency-greater-than': + typeof params?.currencyGreaterThan === 'bigint' + ? (params!.currencyGreaterThan as bigint).toString() + : params?.currencyGreaterThan, + 'currency-less-than': + typeof params?.currencyLessThan === 'bigint' ? (params!.currencyLessThan as bigint).toString() : params?.currencyLessThan, + 'rekey-to': params?.rekeyTo, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAccountTransactionsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAccountTransactions + } + + /** + * Given an application ID and box name, returns base64 encoded box name and value. Box names must be in the goal app call arg form 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, encode base 64 and use 'b64' prefix as in 'b64:A=='. For printable strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'. + */ + async lookupApplicationBoxByIdandName( + applicationId: number | bigint, + params?: { name: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}/box', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, + query: { name: params?.name }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = BoxMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Box + } + + /** + * Lookup application. + */ + async lookupApplicationById( + applicationId: number | bigint, + params?: { includeAll?: boolean }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, + query: { 'include-all': params?.includeAll }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupApplicationByIdMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupApplicationById + } + + /** + * Lookup application logs. + */ + async lookupApplicationLogsById( + applicationId: number | bigint, + params?: { + limit?: number | bigint + next?: string + txid?: string + minRound?: number | bigint + maxRound?: number | bigint + senderAddress?: string + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}/logs', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, + query: { + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + txid: params?.txid, + 'min-round': typeof params?.minRound === 'bigint' ? (params!.minRound as bigint).toString() : params?.minRound, + 'max-round': typeof params?.maxRound === 'bigint' ? (params!.maxRound as bigint).toString() : params?.maxRound, + 'sender-address': params?.senderAddress, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupApplicationLogsByIdMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupApplicationLogsById + } + + /** + * Lookup the list of accounts who hold this asset + */ + async lookupAssetBalances( + assetId: number | bigint, + params?: { + includeAll?: boolean + limit?: number | bigint + next?: string + currencyGreaterThan?: number | bigint + currencyLessThan?: number | bigint + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/assets/{asset-id}/balances', + path: { 'asset-id': typeof assetId === 'bigint' ? assetId.toString() : assetId }, + query: { + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'currency-greater-than': + typeof params?.currencyGreaterThan === 'bigint' + ? (params!.currencyGreaterThan as bigint).toString() + : params?.currencyGreaterThan, + 'currency-less-than': + typeof params?.currencyLessThan === 'bigint' ? (params!.currencyLessThan as bigint).toString() : params?.currencyLessThan, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAssetBalancesMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAssetBalances + } + + /** + * Lookup asset information. + */ + async lookupAssetById( + assetId: number | bigint, + params?: { includeAll?: boolean }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/assets/{asset-id}', + path: { 'asset-id': typeof assetId === 'bigint' ? assetId.toString() : assetId }, + query: { 'include-all': params?.includeAll }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAssetByIdMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAssetById + } + + /** + * Lookup transactions for an asset. Transactions are returned oldest to newest. + */ + async lookupAssetTransactions( + assetId: number | bigint, + params?: { + limit?: number | bigint + next?: string + notePrefix?: string + txType?: 'pay' | 'keyreg' | 'acfg' | 'axfer' | 'afrz' | 'appl' | 'stpf' | 'hb' + sigType?: 'sig' | 'msig' | 'lsig' + txid?: string + round?: number | bigint + minRound?: number | bigint + maxRound?: number | bigint + beforeTime?: string + afterTime?: string + currencyGreaterThan?: number | bigint + currencyLessThan?: number | bigint + address?: string + addressRole?: 'sender' | 'receiver' | 'freeze-target' + excludeCloseTo?: boolean + rekeyTo?: boolean + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/assets/{asset-id}/transactions', + path: { 'asset-id': typeof assetId === 'bigint' ? assetId.toString() : assetId }, + query: { + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'note-prefix': params?.notePrefix, + 'tx-type': params?.txType, + 'sig-type': params?.sigType, + txid: params?.txid, + round: typeof params?.round === 'bigint' ? (params!.round as bigint).toString() : params?.round, + 'min-round': typeof params?.minRound === 'bigint' ? (params!.minRound as bigint).toString() : params?.minRound, + 'max-round': typeof params?.maxRound === 'bigint' ? (params!.maxRound as bigint).toString() : params?.maxRound, + 'before-time': params?.beforeTime, + 'after-time': params?.afterTime, + 'currency-greater-than': + typeof params?.currencyGreaterThan === 'bigint' + ? (params!.currencyGreaterThan as bigint).toString() + : params?.currencyGreaterThan, + 'currency-less-than': + typeof params?.currencyLessThan === 'bigint' ? (params!.currencyLessThan as bigint).toString() : params?.currencyLessThan, + address: params?.address, + 'address-role': params?.addressRole, + 'exclude-close-to': params?.excludeCloseTo, + 'rekey-to': params?.rekeyTo, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupAssetTransactionsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupAssetTransactions + } + + /** + * Lookup block. + */ + async lookupBlock(roundNumber: number | bigint, params?: { headerOnly?: boolean }, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/blocks/{round-number}', + path: { 'round-number': typeof roundNumber === 'bigint' ? roundNumber.toString() : roundNumber }, + query: { 'header-only': params?.headerOnly }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = BlockMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as Block + } + + /** + * Lookup a single transaction. + */ + async lookupTransaction(txid: string, requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/transactions/{txid}', + path: { txid: txid }, + query: {}, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = LookupTransactionMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as LookupTransaction + } + + async makeHealthCheck(requestOptions?: ApiRequestOptions): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/health', + path: {}, + query: {}, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = HealthCheckMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as HealthCheck + } + + /** + * Search for accounts. + */ + async searchForAccounts( + params?: { + assetId?: number | bigint + limit?: number | bigint + next?: string + currencyGreaterThan?: number | bigint + includeAll?: boolean + exclude?: 'all' | 'assets' | 'created-assets' | 'apps-local-state' | 'created-apps' | 'none'[] + currencyLessThan?: number | bigint + authAddr?: string + round?: number | bigint + applicationId?: number | bigint + onlineOnly?: boolean + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/accounts', + path: {}, + query: { + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'currency-greater-than': + typeof params?.currencyGreaterThan === 'bigint' + ? (params!.currencyGreaterThan as bigint).toString() + : params?.currencyGreaterThan, + 'include-all': params?.includeAll, + exclude: params?.exclude, + 'currency-less-than': + typeof params?.currencyLessThan === 'bigint' ? (params!.currencyLessThan as bigint).toString() : params?.currencyLessThan, + 'auth-addr': params?.authAddr, + round: typeof params?.round === 'bigint' ? (params!.round as bigint).toString() : params?.round, + 'application-id': typeof params?.applicationId === 'bigint' ? (params!.applicationId as bigint).toString() : params?.applicationId, + 'online-only': params?.onlineOnly, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForAccountsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForAccounts + } + + /** + * Given an application ID, returns the box names of that application sorted lexicographically. + */ + async searchForApplicationBoxes( + applicationId: number | bigint, + params?: { limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications/{application-id}/boxes', + path: { 'application-id': typeof applicationId === 'bigint' ? applicationId.toString() : applicationId }, + query: { limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, next: params?.next }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForApplicationBoxesMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForApplicationBoxes + } + + /** + * Search for applications + */ + async searchForApplications( + params?: { applicationId?: number | bigint; creator?: string; includeAll?: boolean; limit?: number | bigint; next?: string }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/applications', + path: {}, + query: { + 'application-id': typeof params?.applicationId === 'bigint' ? (params!.applicationId as bigint).toString() : params?.applicationId, + creator: params?.creator, + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForApplicationsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForApplications + } + + /** + * Search for assets. + */ + async searchForAssets( + params?: { + includeAll?: boolean + limit?: number | bigint + next?: string + creator?: string + name?: string + unit?: string + assetId?: number | bigint + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/assets', + path: {}, + query: { + 'include-all': params?.includeAll, + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + creator: params?.creator, + name: params?.name, + unit: params?.unit, + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForAssetsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForAssets + } + + /** + * Search for block headers. Block headers are returned in ascending round order. Transactions are not included in the output. + */ + async searchForBlockHeaders( + params?: { + limit?: number | bigint + next?: string + minRound?: number | bigint + maxRound?: number | bigint + beforeTime?: string + afterTime?: string + proposers?: string[] + expired?: string[] + absent?: string[] + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/block-headers', + path: {}, + query: { + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'min-round': typeof params?.minRound === 'bigint' ? (params!.minRound as bigint).toString() : params?.minRound, + 'max-round': typeof params?.maxRound === 'bigint' ? (params!.maxRound as bigint).toString() : params?.maxRound, + 'before-time': params?.beforeTime, + 'after-time': params?.afterTime, + proposers: params?.proposers, + expired: params?.expired, + absent: params?.absent, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForBlockHeadersMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForBlockHeaders + } + + /** + * Search for transactions. Transactions are returned oldest to newest unless the address parameter is used, in which case results are returned newest to oldest. + */ + async searchForTransactions( + params?: { + limit?: number | bigint + next?: string + notePrefix?: string + txType?: 'pay' | 'keyreg' | 'acfg' | 'axfer' | 'afrz' | 'appl' | 'stpf' | 'hb' + sigType?: 'sig' | 'msig' | 'lsig' + groupId?: string + txid?: string + round?: number | bigint + minRound?: number | bigint + maxRound?: number | bigint + assetId?: number | bigint + beforeTime?: string + afterTime?: string + currencyGreaterThan?: number | bigint + currencyLessThan?: number | bigint + address?: string + addressRole?: 'sender' | 'receiver' | 'freeze-target' + excludeCloseTo?: boolean + rekeyTo?: boolean + applicationId?: number | bigint + }, + requestOptions?: ApiRequestOptions, + ): Promise { + const headers: Record = {} + const responseFormat: 'json' | 'msgpack' = 'json' + headers['Accept'] = responseFormat === 'json' ? 'application/json' : 'application/msgpack' + + const serializedBody = undefined + const mediaType = undefined + + const payload = await this.httpRequest.request({ + method: 'GET', + url: '/v2/transactions', + path: {}, + query: { + limit: typeof params?.limit === 'bigint' ? (params!.limit as bigint).toString() : params?.limit, + next: params?.next, + 'note-prefix': params?.notePrefix, + 'tx-type': params?.txType, + 'sig-type': params?.sigType, + 'group-id': params?.groupId, + txid: params?.txid, + round: typeof params?.round === 'bigint' ? (params!.round as bigint).toString() : params?.round, + 'min-round': typeof params?.minRound === 'bigint' ? (params!.minRound as bigint).toString() : params?.minRound, + 'max-round': typeof params?.maxRound === 'bigint' ? (params!.maxRound as bigint).toString() : params?.maxRound, + 'asset-id': typeof params?.assetId === 'bigint' ? (params!.assetId as bigint).toString() : params?.assetId, + 'before-time': params?.beforeTime, + 'after-time': params?.afterTime, + 'currency-greater-than': + typeof params?.currencyGreaterThan === 'bigint' + ? (params!.currencyGreaterThan as bigint).toString() + : params?.currencyGreaterThan, + 'currency-less-than': + typeof params?.currencyLessThan === 'bigint' ? (params!.currencyLessThan as bigint).toString() : params?.currencyLessThan, + address: params?.address, + 'address-role': params?.addressRole, + 'exclude-close-to': params?.excludeCloseTo, + 'rekey-to': params?.rekeyTo, + 'application-id': typeof params?.applicationId === 'bigint' ? (params!.applicationId as bigint).toString() : params?.applicationId, + }, + headers, + body: serializedBody, + mediaType: mediaType, + ...(requestOptions ?? {}), + }) + + const responseMeta = SearchForTransactionsMeta + if (responseMeta) { + return AlgorandSerializer.decode(payload, responseMeta, responseFormat) + } + return payload as SearchForTransactions + } +} diff --git a/packages/typescript/indexer_client/src/apis/index.ts b/packages/typescript/indexer_client/src/apis/index.ts new file mode 100644 index 000000000..c4f7e8d69 --- /dev/null +++ b/packages/typescript/indexer_client/src/apis/index.ts @@ -0,0 +1,2 @@ +// Barrel file for services +export { IndexerApi } from './api.service' diff --git a/packages/typescript/indexer_client/src/client.ts b/packages/typescript/indexer_client/src/client.ts new file mode 100644 index 000000000..feda9e609 --- /dev/null +++ b/packages/typescript/indexer_client/src/client.ts @@ -0,0 +1,10 @@ +import type { ClientConfig } from './core/client-config' +import type { BaseHttpRequest } from './core/base-http-request' +import { FetchHttpRequest } from './core/fetch-http-request' +import { IndexerApi } from './apis/api.service' + +export class IndexerClient extends IndexerApi { + constructor(config: ClientConfig, request?: BaseHttpRequest) { + super(request ?? new FetchHttpRequest(config)) + } +} diff --git a/packages/typescript/indexer_client/src/core/api-error.ts b/packages/typescript/indexer_client/src/core/api-error.ts new file mode 100644 index 000000000..8293ffb76 --- /dev/null +++ b/packages/typescript/indexer_client/src/core/api-error.ts @@ -0,0 +1,12 @@ +export class ApiError extends Error { + public readonly url: string + public readonly status: number + public readonly body: T | undefined + + constructor(url: string, status: number, body?: T) { + super(`Request to ${url} failed with status ${status}`) + this.url = url + this.status = status + this.body = body + } +} diff --git a/packages/typescript/indexer_client/src/core/base-http-request.ts b/packages/typescript/indexer_client/src/core/base-http-request.ts new file mode 100644 index 000000000..110606ade --- /dev/null +++ b/packages/typescript/indexer_client/src/core/base-http-request.ts @@ -0,0 +1,22 @@ +import type { ClientConfig } from './client-config' + +export type QueryValue = string | number | bigint | boolean +export type QueryParams = Record + +export type BodyValue = Uint8Array | Record | unknown[] | string | number | boolean | null + +export interface ApiRequestOptions { + method: string + url: string + path?: Record + query?: QueryParams + headers?: Record + body?: BodyValue + mediaType?: string + responseHeader?: string +} + +export abstract class BaseHttpRequest { + constructor(public readonly config: ClientConfig) {} + abstract request(options: ApiRequestOptions): Promise +} diff --git a/packages/typescript/indexer_client/src/core/client-config.ts b/packages/typescript/indexer_client/src/core/client-config.ts new file mode 100644 index 000000000..9f3a1a5de --- /dev/null +++ b/packages/typescript/indexer_client/src/core/client-config.ts @@ -0,0 +1,14 @@ +/* Minimal client runtime config holder */ +export type BaseURL = string + +export interface ClientConfig { + // Prefer idiomatic camelCase going forward + baseUrl: BaseURL + credentials?: 'include' | 'omit' | 'same-origin' + token?: string | (() => string | Promise) + apiToken?: string + username?: string + password?: string + headers?: Record | (() => Record | Promise>) + encodePath?: (path: string) => string +} diff --git a/packages/typescript/indexer_client/src/core/codecs.ts b/packages/typescript/indexer_client/src/core/codecs.ts new file mode 100644 index 000000000..a70a67bc7 --- /dev/null +++ b/packages/typescript/indexer_client/src/core/codecs.ts @@ -0,0 +1,13 @@ +import { encode as msgpackEncode, decode as msgpackDecode } from '@msgpack/msgpack' + +export function encodeMsgPack(value: unknown): Uint8Array { + return msgpackEncode(value, { + sortKeys: true, + ignoreUndefined: true, + useBigInt64: true, + }) +} + +export function decodeMsgPack(buffer: Uint8Array): T { + return msgpackDecode(buffer, { useBigInt64: true }) as T +} diff --git a/packages/typescript/indexer_client/src/core/fetch-http-request.ts b/packages/typescript/indexer_client/src/core/fetch-http-request.ts new file mode 100644 index 000000000..d57c1e667 --- /dev/null +++ b/packages/typescript/indexer_client/src/core/fetch-http-request.ts @@ -0,0 +1,8 @@ +import { BaseHttpRequest, type ApiRequestOptions } from './base-http-request' +import { request } from './request' + +export class FetchHttpRequest extends BaseHttpRequest { + async request(options: ApiRequestOptions): Promise { + return request(this.config, options) + } +} diff --git a/packages/typescript/indexer_client/src/core/model-runtime.ts b/packages/typescript/indexer_client/src/core/model-runtime.ts new file mode 100644 index 000000000..87c8bfaf9 --- /dev/null +++ b/packages/typescript/indexer_client/src/core/model-runtime.ts @@ -0,0 +1,264 @@ +import { + encodeSignedTransaction as transactEncodeSignedTransaction, + decodeSignedTransaction as transactDecodeSignedTransaction, + type SignedTransaction, +} from '@algorandfoundation/algokit-transact' +import { encodeMsgPack, decodeMsgPack } from './codecs' +import { toBase64, fromBase64 } from './serialization' + +export type BodyFormat = 'json' | 'msgpack' + +export interface ScalarFieldType { + readonly kind: 'scalar' + readonly isBytes?: boolean + readonly isBigint?: boolean +} + +export interface CodecFieldType { + readonly kind: 'codec' + readonly codecKey: string +} + +export interface ModelFieldType { + readonly kind: 'model' + readonly meta: ModelMetadata | (() => ModelMetadata) +} + +export interface ArrayFieldType { + readonly kind: 'array' + readonly item: FieldType +} + +export interface RecordFieldType { + readonly kind: 'record' + readonly value: FieldType +} + +export type FieldType = ScalarFieldType | CodecFieldType | ModelFieldType | ArrayFieldType | RecordFieldType + +export interface FieldMetadata { + readonly name: string + readonly wireKey: string + readonly optional: boolean + readonly nullable: boolean + readonly type: FieldType +} + +export type ModelKind = 'object' | 'array' | 'passthrough' + +export interface ModelMetadata { + readonly name: string + readonly kind: ModelKind + readonly fields?: readonly FieldMetadata[] + readonly arrayItems?: FieldType + readonly codecKey?: string + readonly additionalProperties?: FieldType + readonly passThrough?: FieldType +} + +export interface TypeCodec { + encode(value: TValue, format: BodyFormat): unknown + decode(value: unknown, format: BodyFormat): TValue +} + +const codecRegistry = new Map>() + +export function registerCodec(key: string, codec: TypeCodec): void { + codecRegistry.set(key, codec as TypeCodec) +} + +export function getCodec(key: string): TypeCodec | undefined { + return codecRegistry.get(key) as TypeCodec | undefined +} + +export class AlgorandSerializer { + static encode(value: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): Uint8Array | string { + const wire = this.transform(value, meta, { direction: 'encode', format }) + if (format === 'msgpack') { + return wire instanceof Uint8Array ? wire : encodeMsgPack(wire) + } + return typeof wire === 'string' ? wire : JSON.stringify(wire) + } + + static decode(payload: unknown, meta: ModelMetadata, format: BodyFormat = 'msgpack'): T { + let wire: unknown = payload + if (format === 'msgpack') { + if (payload instanceof Uint8Array) { + wire = decodeMsgPack(payload) + } + } else if (typeof payload === 'string') { + wire = JSON.parse(payload) + } + return this.transform(wire, meta, { direction: 'decode', format }) as T + } + + private static transform(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + if (value === undefined || value === null) { + return value + } + + if (meta.codecKey) { + return this.applyCodec(value, meta.codecKey, ctx) + } + + switch (meta.kind) { + case 'object': + return this.transformObject(value, meta, ctx) + case 'array': + return this.transformType(value, { kind: 'array', item: meta.arrayItems ?? { kind: 'scalar' } }, ctx) + case 'passthrough': + default: + return this.transformType(value, meta.passThrough ?? { kind: 'scalar' }, ctx) + } + } + + private static transformObject(value: unknown, meta: ModelMetadata, ctx: TransformContext): unknown { + const fields = meta.fields ?? [] + if (ctx.direction === 'encode') { + const src = value as Record + const out: Record = {} + for (const field of fields) { + const fieldValue = src[field.name] + if (fieldValue === undefined) continue + const encoded = this.transformType(fieldValue, field.type, ctx) + if (encoded === undefined && fieldValue === undefined) continue + out[field.wireKey] = encoded + } + if (meta.additionalProperties) { + for (const [key, val] of Object.entries(src)) { + if (fields.some((f) => f.name === key)) continue + out[key] = this.transformType(val, meta.additionalProperties, ctx) + } + } + return out + } + + const src = value as Record + const out: Record = {} + const fieldByWire = new Map(fields.map((field) => [field.wireKey, field])) + + for (const [wireKey, wireValue] of Object.entries(src)) { + const field = fieldByWire.get(wireKey) + if (field) { + const decoded = this.transformType(wireValue, field.type, ctx) + out[field.name] = decoded + continue + } + if (meta.additionalProperties) { + out[wireKey] = this.transformType(wireValue, meta.additionalProperties, ctx) + continue + } + out[wireKey] = wireValue + } + + return out + } + + private static transformType(value: unknown, type: FieldType, ctx: TransformContext): unknown { + if (value === undefined || value === null) return value + + switch (type.kind) { + case 'scalar': + return this.transformScalar(value, type, ctx) + case 'codec': + return this.applyCodec(value, type.codecKey, ctx) + case 'model': + return this.transform(value, typeof type.meta === 'function' ? type.meta() : type.meta, ctx) + case 'array': + if (!Array.isArray(value)) return value + return value.map((item) => this.transformType(item, type.item, ctx)) + case 'record': + if (typeof value !== 'object' || value === null) return value + return Object.fromEntries( + Object.entries(value as Record).map(([k, v]) => [k, this.transformType(v, type.value, ctx)]), + ) + default: + return value + } + } + + private static transformScalar(value: unknown, meta: ScalarFieldType, ctx: TransformContext): unknown { + if (ctx.direction === 'encode') { + if (meta.isBytes && ctx.format === 'json') { + if (value instanceof Uint8Array) return toBase64(value) + } + if (meta.isBigint && ctx.format === 'json') { + if (typeof value === 'bigint') return value.toString() + if (typeof value === 'number') return Math.trunc(value).toString() + if (typeof value === 'string') return value + } + return value + } + + if (meta.isBytes && ctx.format === 'json' && typeof value === 'string') { + return fromBase64(value) + } + + if (meta.isBigint) { + if (typeof value === 'string') { + try { + return BigInt(value) + } catch { + return value + } + } + if (typeof value === 'number' && Number.isInteger(value)) { + return BigInt(value) + } + } + + return value + } + + private static applyCodec(value: unknown, codecKey: string, ctx: TransformContext): unknown { + const codec = codecRegistry.get(codecKey) + if (!codec) { + throw new Error(`Codec for "${codecKey}" is not registered`) + } + return ctx.direction === 'encode' ? codec.encode(value, ctx.format) : codec.decode(value, ctx.format) + } +} + +type TransformDirection = 'encode' | 'decode' + +interface TransformContext { + readonly direction: TransformDirection + readonly format: BodyFormat +} + +const encodeSignedTransactionImpl = (value: unknown): Uint8Array => transactEncodeSignedTransaction(value as SignedTransaction) +const decodeSignedTransactionImpl = (value: Uint8Array): SignedTransaction => transactDecodeSignedTransaction(value) + +class SignedTransactionCodec implements TypeCodec { + encode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value + if (format === 'json') { + if (value instanceof Uint8Array) return toBase64(value) + return toBase64(encodeSignedTransactionImpl(value)) + } + if (value instanceof Uint8Array) { + // Already canonical bytes; decode to structured map so parent encoding keeps map semantics + return decodeMsgPack(value) + } + // Convert signed transaction object into canonical map representation + return decodeMsgPack(encodeSignedTransactionImpl(value)) + } + + decode(value: unknown, format: BodyFormat): unknown { + if (value == null) return value + if (format === 'json') { + if (typeof value === 'string') return decodeSignedTransactionImpl(fromBase64(value)) + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value) + return value + } + if (value instanceof Uint8Array) return decodeSignedTransactionImpl(value) + // Value is a decoded map; re-encode to bytes before handing to transact decoder + try { + return decodeSignedTransactionImpl(encodeMsgPack(value)) + } catch { + return value + } + } +} + +registerCodec('SignedTransaction', new SignedTransactionCodec()) diff --git a/packages/typescript/indexer_client/src/core/request.ts b/packages/typescript/indexer_client/src/core/request.ts new file mode 100644 index 000000000..201bbb69e --- /dev/null +++ b/packages/typescript/indexer_client/src/core/request.ts @@ -0,0 +1,122 @@ +import type { ClientConfig } from './client-config' +import { ApiError } from './api-error' +import { decodeMsgPack, encodeMsgPack } from './codecs' +import type { QueryParams, BodyValue } from './base-http-request' + +const encodeURIPath = (path: string): string => encodeURI(path).replace(/%5B/g, '[').replace(/%5D/g, ']') + +export async function request( + config: ClientConfig, + options: { + method: string + url: string + path?: Record + query?: QueryParams + headers?: Record + body?: BodyValue + mediaType?: string + responseHeader?: string + }, +): Promise { + let rawPath = options.url + if (options.path) { + for (const [key, value] of Object.entries(options.path)) { + const raw = typeof value === 'bigint' ? value.toString() : String(value) + const replace = config.encodePath ? config.encodePath(raw) : encodeURIPath(raw) + rawPath = rawPath.replace(`{${key}}`, replace) + } + } + + const url = new URL(rawPath, config.baseUrl) + + if (options.query) { + for (const [key, value] of Object.entries(options.query)) { + if (value === undefined || value === null) continue + if (Array.isArray(value)) { + for (const item of value) { + url.searchParams.append(key, item.toString()) + } + } else { + url.searchParams.append(key, value.toString()) + } + } + } + + const headers: Record = { + ...(typeof config.headers === 'function' ? await config.headers() : (config.headers ?? {})), + ...(options.headers ?? {}), + } + + const apiToken = config.apiToken + if (apiToken) { + headers['X-Indexer-API-Token'] = apiToken + } + + const token = typeof config.token === 'function' ? await config.token() : config.token + if (token) headers['Authorization'] = `Bearer ${token}` + if (!token && config.username && config.password) { + headers['Authorization'] = `Basic ${btoa(`${config.username}:${config.password}`)}` + } + + let body: BodyValue | undefined = undefined + if (options.body != null) { + if (options.body instanceof Uint8Array || typeof options.body === 'string') { + body = options.body + } else if (options.mediaType?.includes('msgpack')) { + body = encodeMsgPack(options.body) + } else if (options.mediaType?.includes('json')) { + body = JSON.stringify(options.body) + } else { + body = options.body + } + } + + const response = await fetch(url.toString(), { + method: options.method, + headers, + body, + credentials: config.credentials, + }) + + if (!response.ok) { + let errorBody: unknown + try { + const ct = response.headers.get('content-type') ?? '' + if (ct.includes('application/msgpack')) { + errorBody = decodeMsgPack(new Uint8Array(await response.arrayBuffer())) + } else if (ct.includes('application/json')) { + errorBody = JSON.parse(await response.text()) + } else { + errorBody = await response.text() + } + } catch { + errorBody = undefined + } + throw new ApiError(url.toString(), response.status, errorBody) + } + + if (options.responseHeader) { + const value = response.headers.get(options.responseHeader) + return value as unknown as T + } + + const contentType = response.headers.get('content-type') ?? '' + + if (contentType.includes('application/msgpack')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T + } + + if (contentType.includes('application/octet-stream') || contentType.includes('application/x-binary')) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T + } + + if (contentType.includes('application/json')) { + return (await response.text()) as unknown as T + } + + if (!contentType) { + return new Uint8Array(await response.arrayBuffer()) as unknown as T + } + + return (await response.text()) as unknown as T +} diff --git a/packages/typescript/indexer_client/src/core/serialization.ts b/packages/typescript/indexer_client/src/core/serialization.ts new file mode 100644 index 000000000..6be054287 --- /dev/null +++ b/packages/typescript/indexer_client/src/core/serialization.ts @@ -0,0 +1,26 @@ +export function toBase64(bytes: Uint8Array): string { + if (typeof Buffer !== 'undefined') { + return Buffer.from(bytes).toString('base64') + } + const globalRef: Record = globalThis as unknown as Record + const btoaFn = globalRef.btoa as ((value: string) => string) | undefined + if (typeof btoaFn === 'function') { + return btoaFn(String.fromCharCode(...bytes)) + } + throw new Error('Base64 encoding not supported in this environment') +} + +export function fromBase64(s: string): Uint8Array { + if (typeof Buffer !== 'undefined') { + return new Uint8Array(Buffer.from(s, 'base64')) + } + const globalRef: Record = globalThis as unknown as Record + const atobFn = globalRef.atob as ((value: string) => string) | undefined + if (typeof atobFn === 'function') { + const bin = atobFn(s) + const out = new Uint8Array(bin.length) + for (let i = 0; i < bin.length; i += 1) out[i] = bin.charCodeAt(i) + return out + } + throw new Error('Base64 decoding not supported in this environment') +} diff --git a/packages/typescript/indexer_client/src/index.ts b/packages/typescript/indexer_client/src/index.ts new file mode 100644 index 000000000..915506d52 --- /dev/null +++ b/packages/typescript/indexer_client/src/index.ts @@ -0,0 +1,12 @@ +export * from './core/client-config' +export * from './core/base-http-request' +export * from './core/fetch-http-request' +export * from './core/api-error' +export * from './core/serialization' +export * from './core/codecs' +export * from './core/model-runtime' + +// Generated +export * from './models' +export * from './apis' +export * from './client' diff --git a/packages/typescript/indexer_client/src/models/account-participation.ts b/packages/typescript/indexer_client/src/models/account-participation.ts new file mode 100644 index 000000000..372aa8708 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/account-participation.ts @@ -0,0 +1,85 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * AccountParticipation describes the parameters used by this account in consensus protocol. + */ +export type AccountParticipation = { + /** + * Selection public key (if any) currently registered for this round. + */ + selectionParticipationKey: Uint8Array + + /** + * First round for which this participation is valid. + */ + voteFirstValid: bigint + + /** + * Number of subkeys in each batch of participation keys. + */ + voteKeyDilution: bigint + + /** + * Last round for which this participation is valid. + */ + voteLastValid: bigint + + /** + * root participation public key (if any) currently registered for this round. + */ + voteParticipationKey: Uint8Array + + /** + * Root of the state proof key (if any) + */ + stateProofKey?: Uint8Array +} + +export const AccountParticipationMeta: ModelMetadata = { + name: 'AccountParticipation', + kind: 'object', + fields: [ + { + name: 'selectionParticipationKey', + wireKey: 'selection-participation-key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'voteFirstValid', + wireKey: 'vote-first-valid', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteKeyDilution', + wireKey: 'vote-key-dilution', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteLastValid', + wireKey: 'vote-last-valid', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteParticipationKey', + wireKey: 'vote-participation-key', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'stateProofKey', + wireKey: 'state-proof-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/account-state-delta.ts b/packages/typescript/indexer_client/src/models/account-state-delta.ts new file mode 100644 index 000000000..ad93395f1 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/account-state-delta.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateDelta } from './state-delta' +import { StateDeltaMeta } from './state-delta' + +/** + * Application state delta. + */ +export type AccountStateDelta = { + address: string + delta: StateDelta +} + +export const AccountStateDeltaMeta: ModelMetadata = { + name: 'AccountStateDelta', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'delta', + wireKey: 'delta', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => StateDeltaMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/account.ts b/packages/typescript/indexer_client/src/models/account.ts new file mode 100644 index 000000000..b2f68170c --- /dev/null +++ b/packages/typescript/indexer_client/src/models/account.ts @@ -0,0 +1,397 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountParticipation } from './account-participation' +import { AccountParticipationMeta } from './account-participation' +import type { Application } from './application' +import { ApplicationMeta } from './application' +import type { ApplicationLocalState } from './application-local-state' +import { ApplicationLocalStateMeta } from './application-local-state' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { Asset } from './asset' +import { AssetMeta } from './asset' +import type { AssetHolding } from './asset-holding' +import { AssetHoldingMeta } from './asset-holding' + +/** + * Account information at a given round. + * + * Definition: + * data/basics/userBalance.go : AccountData + */ +export type Account = { + /** + * the account public key + */ + address: string + + /** + * total number of MicroAlgos in the account + */ + amount: bigint + + /** + * MicroAlgo balance required by the account. + * + * The requirement grows based on asset and application usage. + */ + minBalance: bigint + + /** + * specifies the amount of MicroAlgos in the account, without the pending rewards. + */ + amountWithoutPendingRewards: bigint + + /** + * application local data stored in this account. + * + * Note the raw object uses `map[int] -> AppLocalState` for this type. + */ + appsLocalState?: ApplicationLocalState[] + appsTotalSchema?: ApplicationStateSchema + + /** + * the sum of all extra application program pages for this account. + */ + appsTotalExtraPages?: bigint + + /** + * assets held by this account. + * + * Note the raw object uses `map[int] -> AssetHolding` for this type. + */ + assets?: AssetHolding[] + + /** + * parameters of applications created by this account including app global data. + * + * Note: the raw account uses `map[int] -> AppParams` for this type. + */ + createdApps?: Application[] + + /** + * parameters of assets created by this account. + * + * Note: the raw account uses `map[int] -> Asset` for this type. + */ + createdAssets?: Asset[] + participation?: AccountParticipation + + /** + * can the account receive block incentives if its balance is in range at proposal time. + */ + incentiveEligible?: boolean + + /** + * amount of MicroAlgos of pending rewards in this account. + */ + pendingRewards: bigint + + /** + * used as part of the rewards computation. Only applicable to accounts which are participating. + */ + rewardBase?: bigint + + /** + * total rewards of MicroAlgos the account has received, including pending rewards. + */ + rewards: bigint + + /** + * The round for which this information is relevant. + */ + round: bigint + + /** + * voting status of the account's MicroAlgos + * * Offline - indicates that the associated account is delegated. + * * Online - indicates that the associated account used as part of the delegation pool. + * * NotParticipating - indicates that the associated account is neither a delegator nor a delegate. + */ + status: string + + /** + * the type of signature used by this account, must be one of: + * * sig + * * msig + * * lsig + * * or null if unknown + */ + sigType?: 'sig' | 'msig' | 'lsig' + + /** + * The count of all applications that have been opted in, equivalent to the count of application local data (AppLocalState objects) stored in this account. + */ + totalAppsOptedIn: bigint + + /** + * The count of all assets that have been opted in, equivalent to the count of AssetHolding objects held by this account. + */ + totalAssetsOptedIn: bigint + + /** + * For app-accounts only. The total number of bytes allocated for the keys and values of boxes which belong to the associated application. + */ + totalBoxBytes: bigint + + /** + * For app-accounts only. The total number of boxes which belong to the associated application. + */ + totalBoxes: bigint + + /** + * The count of all apps (AppParams objects) created by this account. + */ + totalCreatedApps: bigint + + /** + * The count of all assets (AssetParams objects) created by this account. + */ + totalCreatedAssets: bigint + + /** + * The address against which signing should be checked. If empty, the address of the current account is used. This field can be updated in any transaction by setting the RekeyTo field. + */ + authAddr?: string + + /** + * The round in which this account last proposed the block. + */ + lastProposed?: bigint + + /** + * The round in which this account last went online, or explicitly renewed their online status. + */ + lastHeartbeat?: bigint + + /** + * Whether or not this account is currently closed. + */ + deleted?: boolean + + /** + * Round during which this account first appeared in a transaction. + */ + createdAtRound?: bigint + + /** + * Round during which this account was most recently closed. + */ + closedAtRound?: bigint +} + +export const AccountMeta: ModelMetadata = { + name: 'Account', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'minBalance', + wireKey: 'min-balance', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'amountWithoutPendingRewards', + wireKey: 'amount-without-pending-rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'appsLocalState', + wireKey: 'apps-local-state', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationLocalStateMeta } }, + }, + { + name: 'appsTotalSchema', + wireKey: 'apps-total-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'appsTotalExtraPages', + wireKey: 'apps-total-extra-pages', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assets', + wireKey: 'assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetHoldingMeta } }, + }, + { + name: 'createdApps', + wireKey: 'created-apps', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationMeta } }, + }, + { + name: 'createdAssets', + wireKey: 'created-assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetMeta } }, + }, + { + name: 'participation', + wireKey: 'participation', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AccountParticipationMeta }, + }, + { + name: 'incentiveEligible', + wireKey: 'incentive-eligible', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'pendingRewards', + wireKey: 'pending-rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'rewardBase', + wireKey: 'reward-base', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'rewards', + wireKey: 'rewards', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'status', + wireKey: 'status', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sigType', + wireKey: 'sig-type', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalAppsOptedIn', + wireKey: 'total-apps-opted-in', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalAssetsOptedIn', + wireKey: 'total-assets-opted-in', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalBoxBytes', + wireKey: 'total-box-bytes', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalBoxes', + wireKey: 'total-boxes', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalCreatedApps', + wireKey: 'total-created-apps', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'totalCreatedAssets', + wireKey: 'total-created-assets', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'authAddr', + wireKey: 'auth-addr', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lastProposed', + wireKey: 'last-proposed', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'lastHeartbeat', + wireKey: 'last-heartbeat', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'createdAtRound', + wireKey: 'created-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closedAtRound', + wireKey: 'closed-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/application-local-state.ts b/packages/typescript/indexer_client/src/models/application-local-state.ts new file mode 100644 index 000000000..5de31364b --- /dev/null +++ b/packages/typescript/indexer_client/src/models/application-local-state.ts @@ -0,0 +1,81 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { TealKeyValueStore } from './teal-key-value-store' +import { TealKeyValueStoreMeta } from './teal-key-value-store' + +/** + * Stores local state associated with an application. + */ +export type ApplicationLocalState = { + /** + * The application which this local state is for. + */ + id: bigint + + /** + * Whether or not the application local state is currently deleted from its account. + */ + deleted?: boolean + + /** + * Round when the account opted into the application. + */ + optedInAtRound?: bigint + + /** + * Round when account closed out of the application. + */ + closedOutAtRound?: bigint + schema: ApplicationStateSchema + keyValue?: TealKeyValueStore +} + +export const ApplicationLocalStateMeta: ModelMetadata = { + name: 'ApplicationLocalState', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'optedInAtRound', + wireKey: 'opted-in-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closedOutAtRound', + wireKey: 'closed-out-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'schema', + wireKey: 'schema', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'keyValue', + wireKey: 'key-value', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TealKeyValueStoreMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/application-log-data.ts b/packages/typescript/indexer_client/src/models/application-log-data.ts new file mode 100644 index 000000000..d8a9db725 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/application-log-data.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Stores the global information associated with an application. + */ +export type ApplicationLogData = { + /** + * Transaction ID + */ + txid: string + + /** + * Logs for the application being executed by the transaction. + */ + logs: Uint8Array[] +} + +export const ApplicationLogDataMeta: ModelMetadata = { + name: 'ApplicationLogData', + kind: 'object', + fields: [ + { + name: 'txid', + wireKey: 'txid', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'logs', + wireKey: 'logs', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/application-params.ts b/packages/typescript/indexer_client/src/models/application-params.ts new file mode 100644 index 000000000..1d274cc85 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/application-params.ts @@ -0,0 +1,101 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationStateSchema } from './application-state-schema' +import { ApplicationStateSchemaMeta } from './application-state-schema' +import type { TealKeyValueStore } from './teal-key-value-store' +import { TealKeyValueStoreMeta } from './teal-key-value-store' + +/** + * Stores the global information associated with an application. + */ +export type ApplicationParams = { + /** + * The address that created this application. This is the address where the parameters and global state for this application can be found. + */ + creator?: string + + /** + * approval program. + */ + approvalProgram?: Uint8Array + + /** + * clear state program. + */ + clearStateProgram?: Uint8Array + + /** + * the number of extra program pages available to this app. + */ + extraProgramPages?: number + localStateSchema?: ApplicationStateSchema + globalStateSchema?: ApplicationStateSchema + globalState?: TealKeyValueStore + + /** + * the number of updates to the application programs + */ + version?: bigint +} + +export const ApplicationParamsMeta: ModelMetadata = { + name: 'ApplicationParams', + kind: 'object', + fields: [ + { + name: 'creator', + wireKey: 'creator', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'approvalProgram', + wireKey: 'approval-program', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'clearStateProgram', + wireKey: 'clear-state-program', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'extraProgramPages', + wireKey: 'extra-program-pages', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'localStateSchema', + wireKey: 'local-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'globalStateSchema', + wireKey: 'global-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationStateSchemaMeta }, + }, + { + name: 'globalState', + wireKey: 'global-state', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TealKeyValueStoreMeta }, + }, + { + name: 'version', + wireKey: 'version', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/application-state-schema.ts b/packages/typescript/indexer_client/src/models/application-state-schema.ts new file mode 100644 index 000000000..1320aa294 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/application-state-schema.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Specifies maximums on the number of each type that may be stored. + */ +export type ApplicationStateSchema = { + /** + * number of uints. + */ + numUint: number + + /** + * number of byte slices. + */ + numByteSlice: number +} + +export const ApplicationStateSchemaMeta: ModelMetadata = { + name: 'ApplicationStateSchema', + kind: 'object', + fields: [ + { + name: 'numUint', + wireKey: 'num-uint', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'numByteSlice', + wireKey: 'num-byte-slice', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/application.ts b/packages/typescript/indexer_client/src/models/application.ts new file mode 100644 index 000000000..afc04d8f2 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/application.ts @@ -0,0 +1,71 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationParams } from './application-params' +import { ApplicationParamsMeta } from './application-params' + +/** + * Application index and its parameters + */ +export type Application = { + /** + * application index. + */ + id: bigint + + /** + * Whether or not this application is currently deleted. + */ + deleted?: boolean + + /** + * Round when this application was created. + */ + createdAtRound?: bigint + + /** + * Round when this application was deleted. + */ + deletedAtRound?: bigint + params: ApplicationParams +} + +export const ApplicationMeta: ModelMetadata = { + name: 'Application', + kind: 'object', + fields: [ + { + name: 'id', + wireKey: 'id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'createdAtRound', + wireKey: 'created-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'deletedAtRound', + wireKey: 'deleted-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'params', + wireKey: 'params', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => ApplicationParamsMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/asset-holding.ts b/packages/typescript/indexer_client/src/models/asset-holding.ts new file mode 100644 index 000000000..bbe00c216 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/asset-holding.ts @@ -0,0 +1,88 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Describes an asset held by an account. + * + * Definition: + * data/basics/userBalance.go : AssetHolding + */ +export type AssetHolding = { + /** + * number of units held. + */ + amount: bigint + + /** + * Asset ID of the holding. + */ + assetId: bigint + + /** + * whether or not the holding is frozen. + */ + isFrozen: boolean + + /** + * Whether or not the asset holding is currently deleted from its account. + */ + deleted?: boolean + + /** + * Round during which the account opted into this asset holding. + */ + optedInAtRound?: bigint + + /** + * Round during which the account opted out of this asset holding. + */ + optedOutAtRound?: bigint +} + +export const AssetHoldingMeta: ModelMetadata = { + name: 'AssetHolding', + kind: 'object', + fields: [ + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assetId', + wireKey: 'asset-id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'isFrozen', + wireKey: 'is-frozen', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'optedInAtRound', + wireKey: 'opted-in-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'optedOutAtRound', + wireKey: 'opted-out-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/asset-params.ts b/packages/typescript/indexer_client/src/models/asset-params.ts new file mode 100644 index 000000000..dca04d637 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/asset-params.ts @@ -0,0 +1,198 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * AssetParams specifies the parameters for an asset. + * + * \[apar\] when part of an AssetConfig transaction. + * + * Definition: + * data/transactions/asset.go : AssetParams + */ +export type AssetParams = { + /** + * Address of account used to clawback holdings of this asset. If empty, clawback is not permitted. + */ + clawback?: string + + /** + * The address that created this asset. This is the address where the parameters for this asset can be found, and also the address where unwanted asset units can be sent in the worst case. + */ + creator: string + + /** + * The number of digits to use after the decimal point when displaying this asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. If 2, the base unit of the asset is in hundredths, and so on. This value must be between 0 and 19 (inclusive). + */ + decimals: number + + /** + * Whether holdings of this asset are frozen by default. + */ + defaultFrozen?: boolean + + /** + * Address of account used to freeze holdings of this asset. If empty, freezing is not permitted. + */ + freeze?: string + + /** + * Address of account used to manage the keys of this asset and to destroy it. + */ + manager?: string + + /** + * A commitment to some unspecified asset metadata. The format of this metadata is up to the application. + */ + metadataHash?: Uint8Array + + /** + * Name of this asset, as supplied by the creator. Included only when the asset name is composed of printable utf-8 characters. + */ + name?: string + + /** + * Base64 encoded name of this asset, as supplied by the creator. + */ + nameB64?: Uint8Array + + /** + * Address of account holding reserve (non-minted) units of this asset. + */ + reserve?: string + + /** + * The total number of units of this asset. + */ + total: bigint + + /** + * Name of a unit of this asset, as supplied by the creator. Included only when the name of a unit of this asset is composed of printable utf-8 characters. + */ + unitName?: string + + /** + * Base64 encoded name of a unit of this asset, as supplied by the creator. + */ + unitNameB64?: Uint8Array + + /** + * URL where more information about the asset can be retrieved. Included only when the URL is composed of printable utf-8 characters. + */ + url?: string + + /** + * Base64 encoded URL where more information about the asset can be retrieved. + */ + urlB64?: Uint8Array +} + +export const AssetParamsMeta: ModelMetadata = { + name: 'AssetParams', + kind: 'object', + fields: [ + { + name: 'clawback', + wireKey: 'clawback', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'creator', + wireKey: 'creator', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'decimals', + wireKey: 'decimals', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'defaultFrozen', + wireKey: 'default-frozen', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'freeze', + wireKey: 'freeze', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'manager', + wireKey: 'manager', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'metadataHash', + wireKey: 'metadata-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'name', + wireKey: 'name', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nameB64', + wireKey: 'name-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'reserve', + wireKey: 'reserve', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'total', + wireKey: 'total', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'unitName', + wireKey: 'unit-name', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'unitNameB64', + wireKey: 'unit-name-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'url', + wireKey: 'url', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'urlB64', + wireKey: 'url-b64', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/asset.ts b/packages/typescript/indexer_client/src/models/asset.ts new file mode 100644 index 000000000..e5984148b --- /dev/null +++ b/packages/typescript/indexer_client/src/models/asset.ts @@ -0,0 +1,71 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetParams } from './asset-params' +import { AssetParamsMeta } from './asset-params' + +/** + * Specifies both the unique identifier and the parameters for an asset + */ +export type Asset = { + /** + * unique asset identifier + */ + index: bigint + + /** + * Whether or not this asset is currently deleted. + */ + deleted?: boolean + + /** + * Round during which this asset was created. + */ + createdAtRound?: bigint + + /** + * Round during which this asset was destroyed. + */ + destroyedAtRound?: bigint + params: AssetParams +} + +export const AssetMeta: ModelMetadata = { + name: 'Asset', + kind: 'object', + fields: [ + { + name: 'index', + wireKey: 'index', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'createdAtRound', + wireKey: 'created-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'destroyedAtRound', + wireKey: 'destroyed-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'params', + wireKey: 'params', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AssetParamsMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/block-rewards.ts b/packages/typescript/indexer_client/src/models/block-rewards.ts new file mode 100644 index 000000000..39c826b70 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/block-rewards.ts @@ -0,0 +1,85 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields relating to rewards, + */ +export type BlockRewards = { + /** + * \[fees\] accepts transaction fees, it can only spend to the incentive pool. + */ + feeSink: string + + /** + * \[rwcalr\] number of leftover MicroAlgos after the distribution of rewards-rate MicroAlgos for every reward unit in the next round. + */ + rewardsCalculationRound: bigint + + /** + * \[earn\] How many rewards, in MicroAlgos, have been distributed to each RewardUnit of MicroAlgos since genesis. + */ + rewardsLevel: bigint + + /** + * \[rwd\] accepts periodic injections from the fee-sink and continually redistributes them as rewards. + */ + rewardsPool: string + + /** + * \[rate\] Number of new MicroAlgos added to the participation stake from rewards at the next round. + */ + rewardsRate: bigint + + /** + * \[frac\] Number of leftover MicroAlgos after the distribution of RewardsRate/rewardUnits MicroAlgos for every reward unit in the next round. + */ + rewardsResidue: bigint +} + +export const BlockRewardsMeta: ModelMetadata = { + name: 'BlockRewards', + kind: 'object', + fields: [ + { + name: 'feeSink', + wireKey: 'fee-sink', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rewardsCalculationRound', + wireKey: 'rewards-calculation-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rewardsLevel', + wireKey: 'rewards-level', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rewardsPool', + wireKey: 'rewards-pool', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rewardsRate', + wireKey: 'rewards-rate', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rewardsResidue', + wireKey: 'rewards-residue', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/block-upgrade-state.ts b/packages/typescript/indexer_client/src/models/block-upgrade-state.ts new file mode 100644 index 000000000..82a6ddaa6 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/block-upgrade-state.ts @@ -0,0 +1,73 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields relating to a protocol upgrade. + */ +export type BlockUpgradeState = { + /** + * \[proto\] The current protocol version. + */ + currentProtocol: string + + /** + * \[nextproto\] The next proposed protocol version. + */ + nextProtocol?: string + + /** + * \[nextyes\] Number of blocks which approved the protocol upgrade. + */ + nextProtocolApprovals?: bigint + + /** + * \[nextswitch\] Round on which the protocol upgrade will take effect. + */ + nextProtocolSwitchOn?: bigint + + /** + * \[nextbefore\] Deadline round for this protocol upgrade (No votes will be consider after this round). + */ + nextProtocolVoteBefore?: bigint +} + +export const BlockUpgradeStateMeta: ModelMetadata = { + name: 'BlockUpgradeState', + kind: 'object', + fields: [ + { + name: 'currentProtocol', + wireKey: 'current-protocol', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextProtocol', + wireKey: 'next-protocol', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextProtocolApprovals', + wireKey: 'next-protocol-approvals', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextProtocolSwitchOn', + wireKey: 'next-protocol-switch-on', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextProtocolVoteBefore', + wireKey: 'next-protocol-vote-before', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/block-upgrade-vote.ts b/packages/typescript/indexer_client/src/models/block-upgrade-vote.ts new file mode 100644 index 000000000..2d0a91d76 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/block-upgrade-vote.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields relating to voting for a protocol upgrade. + */ +export type BlockUpgradeVote = { + /** + * \[upgradeyes\] Indicates a yes vote for the current proposal. + */ + upgradeApprove?: boolean + + /** + * \[upgradedelay\] Indicates the time between acceptance and execution. + */ + upgradeDelay?: bigint + + /** + * \[upgradeprop\] Indicates a proposed upgrade. + */ + upgradePropose?: string +} + +export const BlockUpgradeVoteMeta: ModelMetadata = { + name: 'BlockUpgradeVote', + kind: 'object', + fields: [ + { + name: 'upgradeApprove', + wireKey: 'upgrade-approve', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeDelay', + wireKey: 'upgrade-delay', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradePropose', + wireKey: 'upgrade-propose', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/block.ts b/packages/typescript/indexer_client/src/models/block.ts new file mode 100644 index 000000000..32ba63040 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/block.ts @@ -0,0 +1,266 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { BlockRewards } from './block-rewards' +import { BlockRewardsMeta } from './block-rewards' +import type { BlockUpgradeState } from './block-upgrade-state' +import { BlockUpgradeStateMeta } from './block-upgrade-state' +import type { BlockUpgradeVote } from './block-upgrade-vote' +import { BlockUpgradeVoteMeta } from './block-upgrade-vote' +import type { ParticipationUpdates } from './participation-updates' +import { ParticipationUpdatesMeta } from './participation-updates' +import type { StateProofTracking } from './state-proof-tracking' +import { StateProofTrackingMeta } from './state-proof-tracking' +import type { Transaction } from './transaction' +import { TransactionMeta } from './transaction' + +/** + * Block information. + * + * Definition: + * data/bookkeeping/block.go : Block + */ +export type Block = { + /** + * the proposer of this block. + */ + proposer?: string + + /** + * the sum of all fees paid by transactions in this block. + */ + feesCollected?: bigint + + /** + * the potential bonus payout for this block. + */ + bonus?: bigint + + /** + * the actual amount transferred to the proposer from the fee sink. + */ + proposerPayout?: bigint + + /** + * \[gh\] hash to which this block belongs. + */ + genesisHash: Uint8Array + + /** + * \[gen\] ID to which this block belongs. + */ + genesisId: string + + /** + * \[prev\] Previous block hash. + */ + previousBlockHash: Uint8Array + + /** + * \[prev512\] Previous block hash, using SHA-512. + */ + previousBlockHash512?: Uint8Array + rewards?: BlockRewards + + /** + * \[rnd\] Current round on which this block was appended to the chain. + */ + round: bigint + + /** + * \[seed\] Sortition seed. + */ + seed: Uint8Array + + /** + * Tracks the status of state proofs. + */ + stateProofTracking?: StateProofTracking[] + + /** + * \[ts\] Block creation timestamp in seconds since epoch + */ + timestamp: bigint + + /** + * \[txns\] list of transactions corresponding to a given round. + */ + transactions?: Transaction[] + + /** + * \[txn\] TransactionsRoot authenticates the set of transactions appearing in the block. More specifically, it's the root of a merkle tree whose leaves are the block's Txids, in lexicographic order. For the empty block, it's 0. Note that the TxnRoot does not authenticate the signatures on the transactions, only the transactions themselves. Two blocks with the same transactions but in a different order and with different signatures will have the same TxnRoot. + */ + transactionsRoot: Uint8Array + + /** + * \[txn256\] TransactionsRootSHA256 is an auxiliary TransactionRoot, built using a vector commitment instead of a merkle tree, and SHA256 hash function instead of the default SHA512_256. This commitment can be used on environments where only the SHA256 function exists. + */ + transactionsRootSha256: Uint8Array + + /** + * \[txn512\] TransactionsRootSHA512 is an auxiliary TransactionRoot, built using a vector commitment instead of a merkle tree, and SHA512 hash function instead of the default SHA512_256. + */ + transactionsRootSha512?: Uint8Array + + /** + * \[tc\] TxnCounter counts the number of transactions committed in the ledger, from the time at which support for this feature was introduced. + * + * Specifically, TxnCounter is the number of the next transaction that will be committed after this block. It is 0 when no transactions have ever been committed (since TxnCounter started being supported). + */ + txnCounter?: bigint + upgradeState?: BlockUpgradeState + upgradeVote?: BlockUpgradeVote + participationUpdates?: ParticipationUpdates +} + +export const BlockMeta: ModelMetadata = { + name: 'Block', + kind: 'object', + fields: [ + { + name: 'proposer', + wireKey: 'proposer', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'feesCollected', + wireKey: 'fees-collected', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'bonus', + wireKey: 'bonus', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'proposerPayout', + wireKey: 'proposer-payout', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'genesisHash', + wireKey: 'genesis-hash', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'genesisId', + wireKey: 'genesis-id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'previousBlockHash', + wireKey: 'previous-block-hash', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'previousBlockHash512', + wireKey: 'previous-block-hash-512', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'rewards', + wireKey: 'rewards', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => BlockRewardsMeta }, + }, + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'seed', + wireKey: 'seed', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'stateProofTracking', + wireKey: 'state-proof-tracking', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => StateProofTrackingMeta } }, + }, + { + name: 'timestamp', + wireKey: 'timestamp', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'transactions', + wireKey: 'transactions', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionMeta } }, + }, + { + name: 'transactionsRoot', + wireKey: 'transactions-root', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'transactionsRootSha256', + wireKey: 'transactions-root-sha256', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'transactionsRootSha512', + wireKey: 'transactions-root-sha512', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'txnCounter', + wireKey: 'txn-counter', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'upgradeState', + wireKey: 'upgrade-state', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => BlockUpgradeStateMeta }, + }, + { + name: 'upgradeVote', + wireKey: 'upgrade-vote', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => BlockUpgradeVoteMeta }, + }, + { + name: 'participationUpdates', + wireKey: 'participation-updates', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ParticipationUpdatesMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/box-descriptor.ts b/packages/typescript/indexer_client/src/models/box-descriptor.ts new file mode 100644 index 000000000..4d6fe2bf1 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/box-descriptor.ts @@ -0,0 +1,25 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Box descriptor describes an app box without a value. + */ +export type BoxDescriptor = { + /** + * Base64 encoded box name + */ + name: Uint8Array +} + +export const BoxDescriptorMeta: ModelMetadata = { + name: 'BoxDescriptor', + kind: 'object', + fields: [ + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/box-reference.ts b/packages/typescript/indexer_client/src/models/box-reference.ts new file mode 100644 index 000000000..e5427cb25 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/box-reference.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * BoxReference names a box by its name and the application ID it belongs to. + */ +export type BoxReference = { + /** + * Application ID to which the box belongs, or zero if referring to the called application. + */ + app: bigint + + /** + * Base64 encoded box name + */ + name: Uint8Array +} + +export const BoxReferenceMeta: ModelMetadata = { + name: 'BoxReference', + kind: 'object', + fields: [ + { + name: 'app', + wireKey: 'app', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/box.ts b/packages/typescript/indexer_client/src/models/box.ts new file mode 100644 index 000000000..24680cd2f --- /dev/null +++ b/packages/typescript/indexer_client/src/models/box.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Box name and its content. + */ +export type Box = { + /** + * The round for which this information is relevant + */ + round: bigint + + /** + * \[name\] box name, base64 encoded + */ + name: Uint8Array + + /** + * \[value\] box value, base64 encoded. + */ + value: Uint8Array +} + +export const BoxMeta: ModelMetadata = { + name: 'Box', + kind: 'object', + fields: [ + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'name', + wireKey: 'name', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/eval-delta-key-value.ts b/packages/typescript/indexer_client/src/models/eval-delta-key-value.ts new file mode 100644 index 000000000..80097c504 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/eval-delta-key-value.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { EvalDelta } from './eval-delta' +import { EvalDeltaMeta } from './eval-delta' + +/** + * Key-value pairs for StateDelta. + */ +export type EvalDeltaKeyValue = { + key: string + value: EvalDelta +} + +export const EvalDeltaKeyValueMeta: ModelMetadata = { + name: 'EvalDeltaKeyValue', + kind: 'object', + fields: [ + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => EvalDeltaMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/eval-delta.ts b/packages/typescript/indexer_client/src/models/eval-delta.ts new file mode 100644 index 000000000..e3bfc1a54 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/eval-delta.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents a TEAL value delta. + */ +export type EvalDelta = { + /** + * \[at\] delta action. + */ + action: number + + /** + * \[bs\] bytes value. + */ + bytes?: string + + /** + * \[ui\] uint value. + */ + uint?: bigint +} + +export const EvalDeltaMeta: ModelMetadata = { + name: 'EvalDelta', + kind: 'object', + fields: [ + { + name: 'action', + wireKey: 'action', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'bytes', + wireKey: 'bytes', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'uint', + wireKey: 'uint', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/hash-factory.ts b/packages/typescript/indexer_client/src/models/hash-factory.ts new file mode 100644 index 000000000..6062e1dae --- /dev/null +++ b/packages/typescript/indexer_client/src/models/hash-factory.ts @@ -0,0 +1,22 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type HashFactory = { + /** + * \[t\] + */ + hashType?: bigint +} + +export const HashFactoryMeta: ModelMetadata = { + name: 'HashFactory', + kind: 'object', + fields: [ + { + name: 'hashType', + wireKey: 'hash-type', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/hashtype.ts b/packages/typescript/indexer_client/src/models/hashtype.ts new file mode 100644 index 000000000..7265f003b --- /dev/null +++ b/packages/typescript/indexer_client/src/models/hashtype.ts @@ -0,0 +1,14 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * The type of hash function used to create the proof, must be one of: + * * sha512_256 + * * sha256 + */ +export type Hashtype = 'sha512_256' | 'sha256' + +export const HashtypeMeta: ModelMetadata = { + name: 'Hashtype', + kind: 'passthrough', + passThrough: { kind: 'scalar' }, +} diff --git a/packages/typescript/indexer_client/src/models/hb-proof-fields.ts b/packages/typescript/indexer_client/src/models/hb-proof-fields.ts new file mode 100644 index 000000000..199949172 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/hb-proof-fields.ts @@ -0,0 +1,73 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * \[hbprf\] HbProof is a signature using HeartbeatAddress's partkey, thereby showing it is online. + */ +export type HbProofFields = { + /** + * \[s\] Signature of the heartbeat message. + */ + hbSig?: Uint8Array + + /** + * \[p\] Public key of the heartbeat message. + */ + hbPk?: Uint8Array + + /** + * \[p2\] Key for new-style two-level ephemeral signature. + */ + hbPk2?: Uint8Array + + /** + * \[p1s\] Signature of OneTimeSignatureSubkeyOffsetID(PK, Batch, Offset) under the key PK2. + */ + hbPk1sig?: Uint8Array + + /** + * \[p2s\] Signature of OneTimeSignatureSubkeyBatchID(PK2, Batch) under the master key (OneTimeSignatureVerifier). + */ + hbPk2sig?: Uint8Array +} + +export const HbProofFieldsMeta: ModelMetadata = { + name: 'HbProofFields', + kind: 'object', + fields: [ + { + name: 'hbSig', + wireKey: 'hb-sig', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbPk', + wireKey: 'hb-pk', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbPk2', + wireKey: 'hb-pk2', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbPk1sig', + wireKey: 'hb-pk1sig', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbPk2sig', + wireKey: 'hb-pk2sig', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/health-check.ts b/packages/typescript/indexer_client/src/models/health-check.ts new file mode 100644 index 000000000..8b4d632c6 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/health-check.ts @@ -0,0 +1,73 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * A health check response. + */ +export type HealthCheck = { + /** + * Current version. + */ + version: string + data?: Record + round: bigint + isMigrating: boolean + dbAvailable: boolean + message: string + errors?: string[] +} + +export const HealthCheckMeta: ModelMetadata = { + name: 'HealthCheck', + kind: 'object', + fields: [ + { + name: 'version', + wireKey: 'version', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'data', + wireKey: 'data', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'round', + wireKey: 'round', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'isMigrating', + wireKey: 'is-migrating', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'dbAvailable', + wireKey: 'db-available', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'message', + wireKey: 'message', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'errors', + wireKey: 'errors', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/holding-ref.ts b/packages/typescript/indexer_client/src/models/holding-ref.ts new file mode 100644 index 000000000..2481d157d --- /dev/null +++ b/packages/typescript/indexer_client/src/models/holding-ref.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * HoldingRef names a holding by referring to an Address and Asset it belongs to. + */ +export type HoldingRef = { + /** + * \[d\] Address in access list, or the sender of the transaction. + */ + address: string + + /** + * \[s\] Asset ID for asset in access list. + */ + asset: bigint +} + +export const HoldingRefMeta: ModelMetadata = { + name: 'HoldingRef', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'asset', + wireKey: 'asset', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/index.ts b/packages/typescript/indexer_client/src/models/index.ts new file mode 100644 index 000000000..6bd24affe --- /dev/null +++ b/packages/typescript/indexer_client/src/models/index.ts @@ -0,0 +1,150 @@ +export type { Hashtype } from './hashtype' +export { HashtypeMeta } from './hashtype' +export type { Account } from './account' +export { AccountMeta } from './account' +export type { AccountParticipation } from './account-participation' +export { AccountParticipationMeta } from './account-participation' +export type { ApplicationStateSchema } from './application-state-schema' +export { ApplicationStateSchemaMeta } from './application-state-schema' +export type { ApplicationLocalState } from './application-local-state' +export { ApplicationLocalStateMeta } from './application-local-state' +export type { TealKeyValueStore } from './teal-key-value-store' +export { TealKeyValueStoreMeta } from './teal-key-value-store' +export type { TealKeyValue } from './teal-key-value' +export { TealKeyValueMeta } from './teal-key-value' +export type { TealValue } from './teal-value' +export { TealValueMeta } from './teal-value' +export type { Application } from './application' +export { ApplicationMeta } from './application' +export type { ApplicationParams } from './application-params' +export { ApplicationParamsMeta } from './application-params' +export type { ApplicationLogData } from './application-log-data' +export { ApplicationLogDataMeta } from './application-log-data' +export type { Asset } from './asset' +export { AssetMeta } from './asset' +export type { AssetHolding } from './asset-holding' +export { AssetHoldingMeta } from './asset-holding' +export type { AssetParams } from './asset-params' +export { AssetParamsMeta } from './asset-params' +export type { Block } from './block' +export { BlockMeta } from './block' +export type { BlockRewards } from './block-rewards' +export { BlockRewardsMeta } from './block-rewards' +export type { BlockUpgradeState } from './block-upgrade-state' +export { BlockUpgradeStateMeta } from './block-upgrade-state' +export type { BlockUpgradeVote } from './block-upgrade-vote' +export { BlockUpgradeVoteMeta } from './block-upgrade-vote' +export type { Box } from './box' +export { BoxMeta } from './box' +export type { BoxDescriptor } from './box-descriptor' +export { BoxDescriptorMeta } from './box-descriptor' +export type { BoxReference } from './box-reference' +export { BoxReferenceMeta } from './box-reference' +export type { HealthCheck } from './health-check' +export { HealthCheckMeta } from './health-check' +export type { HoldingRef } from './holding-ref' +export { HoldingRefMeta } from './holding-ref' +export type { LocalsRef } from './locals-ref' +export { LocalsRefMeta } from './locals-ref' +export type { MiniAssetHolding } from './mini-asset-holding' +export { MiniAssetHoldingMeta } from './mini-asset-holding' +export type { OnCompletion } from './on-completion' +export { OnCompletionMeta } from './on-completion' +export type { ParticipationUpdates } from './participation-updates' +export { ParticipationUpdatesMeta } from './participation-updates' +export type { ResourceRef } from './resource-ref' +export { ResourceRefMeta } from './resource-ref' +export type { StateDelta } from './state-delta' +export { StateDeltaMeta } from './state-delta' +export type { AccountStateDelta } from './account-state-delta' +export { AccountStateDeltaMeta } from './account-state-delta' +export type { EvalDeltaKeyValue } from './eval-delta-key-value' +export { EvalDeltaKeyValueMeta } from './eval-delta-key-value' +export type { EvalDelta } from './eval-delta' +export { EvalDeltaMeta } from './eval-delta' +export type { StateSchema } from './state-schema' +export { StateSchemaMeta } from './state-schema' +export type { Transaction } from './transaction' +export { TransactionMeta } from './transaction' +export type { TransactionApplication } from './transaction-application' +export { TransactionApplicationMeta } from './transaction-application' +export type { TransactionAssetConfig } from './transaction-asset-config' +export { TransactionAssetConfigMeta } from './transaction-asset-config' +export type { TransactionAssetFreeze } from './transaction-asset-freeze' +export { TransactionAssetFreezeMeta } from './transaction-asset-freeze' +export type { TransactionStateProof } from './transaction-state-proof' +export { TransactionStateProofMeta } from './transaction-state-proof' +export type { TransactionHeartbeat } from './transaction-heartbeat' +export { TransactionHeartbeatMeta } from './transaction-heartbeat' +export type { TransactionAssetTransfer } from './transaction-asset-transfer' +export { TransactionAssetTransferMeta } from './transaction-asset-transfer' +export type { TransactionKeyreg } from './transaction-keyreg' +export { TransactionKeyregMeta } from './transaction-keyreg' +export type { TransactionPayment } from './transaction-payment' +export { TransactionPaymentMeta } from './transaction-payment' +export type { TransactionSignature } from './transaction-signature' +export { TransactionSignatureMeta } from './transaction-signature' +export type { TransactionSignatureLogicsig } from './transaction-signature-logicsig' +export { TransactionSignatureLogicsigMeta } from './transaction-signature-logicsig' +export type { TransactionSignatureMultisig } from './transaction-signature-multisig' +export { TransactionSignatureMultisigMeta } from './transaction-signature-multisig' +export type { TransactionSignatureMultisigSubsignature } from './transaction-signature-multisig-subsignature' +export { TransactionSignatureMultisigSubsignatureMeta } from './transaction-signature-multisig-subsignature' +export type { StateProofFields } from './state-proof-fields' +export { StateProofFieldsMeta } from './state-proof-fields' +export type { HbProofFields } from './hb-proof-fields' +export { HbProofFieldsMeta } from './hb-proof-fields' +export type { IndexerStateProofMessage } from './indexer-state-proof-message' +export { IndexerStateProofMessageMeta } from './indexer-state-proof-message' +export type { StateProofReveal } from './state-proof-reveal' +export { StateProofRevealMeta } from './state-proof-reveal' +export type { StateProofSigSlot } from './state-proof-sig-slot' +export { StateProofSigSlotMeta } from './state-proof-sig-slot' +export type { StateProofSignature } from './state-proof-signature' +export { StateProofSignatureMeta } from './state-proof-signature' +export type { StateProofParticipant } from './state-proof-participant' +export { StateProofParticipantMeta } from './state-proof-participant' +export type { StateProofVerifier } from './state-proof-verifier' +export { StateProofVerifierMeta } from './state-proof-verifier' +export type { StateProofTracking } from './state-proof-tracking' +export { StateProofTrackingMeta } from './state-proof-tracking' +export type { MerkleArrayProof } from './merkle-array-proof' +export { MerkleArrayProofMeta } from './merkle-array-proof' +export type { HashFactory } from './hash-factory' +export { HashFactoryMeta } from './hash-factory' +export type { SearchForAccounts } from './search-for-accounts' +export { SearchForAccountsMeta } from './search-for-accounts' +export type { LookupAccountById } from './lookup-account-by-id' +export { LookupAccountByIdMeta } from './lookup-account-by-id' +export type { LookupAccountAssets } from './lookup-account-assets' +export { LookupAccountAssetsMeta } from './lookup-account-assets' +export type { LookupAccountCreatedAssets } from './lookup-account-created-assets' +export { LookupAccountCreatedAssetsMeta } from './lookup-account-created-assets' +export type { LookupAccountAppLocalStates } from './lookup-account-app-local-states' +export { LookupAccountAppLocalStatesMeta } from './lookup-account-app-local-states' +export type { LookupAccountCreatedApplications } from './lookup-account-created-applications' +export { LookupAccountCreatedApplicationsMeta } from './lookup-account-created-applications' +export type { LookupAccountTransactions } from './lookup-account-transactions' +export { LookupAccountTransactionsMeta } from './lookup-account-transactions' +export type { SearchForApplications } from './search-for-applications' +export { SearchForApplicationsMeta } from './search-for-applications' +export type { LookupApplicationById } from './lookup-application-by-id' +export { LookupApplicationByIdMeta } from './lookup-application-by-id' +export type { SearchForApplicationBoxes } from './search-for-application-boxes' +export { SearchForApplicationBoxesMeta } from './search-for-application-boxes' +export type { LookupApplicationLogsById } from './lookup-application-logs-by-id' +export { LookupApplicationLogsByIdMeta } from './lookup-application-logs-by-id' +export type { SearchForAssets } from './search-for-assets' +export { SearchForAssetsMeta } from './search-for-assets' +export type { LookupAssetById } from './lookup-asset-by-id' +export { LookupAssetByIdMeta } from './lookup-asset-by-id' +export type { LookupAssetBalances } from './lookup-asset-balances' +export { LookupAssetBalancesMeta } from './lookup-asset-balances' +export type { LookupAssetTransactions } from './lookup-asset-transactions' +export { LookupAssetTransactionsMeta } from './lookup-asset-transactions' +export type { SearchForBlockHeaders } from './search-for-block-headers' +export { SearchForBlockHeadersMeta } from './search-for-block-headers' +export type { LookupTransaction } from './lookup-transaction' +export { LookupTransactionMeta } from './lookup-transaction' +export type { SearchForTransactions } from './search-for-transactions' +export { SearchForTransactionsMeta } from './search-for-transactions' diff --git a/packages/typescript/indexer_client/src/models/indexer-state-proof-message.ts b/packages/typescript/indexer_client/src/models/indexer-state-proof-message.ts new file mode 100644 index 000000000..218f38620 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/indexer-state-proof-message.ts @@ -0,0 +1,70 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type IndexerStateProofMessage = { + /** + * \[b\] + */ + blockHeadersCommitment?: Uint8Array + + /** + * \[v\] + */ + votersCommitment?: Uint8Array + + /** + * \[P\] + */ + lnProvenWeight?: bigint + + /** + * \[f\] + */ + firstAttestedRound?: bigint + + /** + * \[l\] + */ + latestAttestedRound?: bigint +} + +export const IndexerStateProofMessageMeta: ModelMetadata = { + name: 'IndexerStateProofMessage', + kind: 'object', + fields: [ + { + name: 'blockHeadersCommitment', + wireKey: 'block-headers-commitment', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'votersCommitment', + wireKey: 'voters-commitment', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'lnProvenWeight', + wireKey: 'ln-proven-weight', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'firstAttestedRound', + wireKey: 'first-attested-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'latestAttestedRound', + wireKey: 'latest-attested-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/locals-ref.ts b/packages/typescript/indexer_client/src/models/locals-ref.ts new file mode 100644 index 000000000..d4054dec3 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/locals-ref.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * LocalsRef names a local state by referring to an Address and App it belongs to. + */ +export type LocalsRef = { + /** + * \[d\] Address in access list, or the sender of the transaction. + */ + address: string + + /** + * \[p\] Application ID for app in access list, or zero if referring to the called application. + */ + app: bigint +} + +export const LocalsRefMeta: ModelMetadata = { + name: 'LocalsRef', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'app', + wireKey: 'app', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-app-local-states.ts b/packages/typescript/indexer_client/src/models/lookup-account-app-local-states.ts new file mode 100644 index 000000000..3039ae959 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-app-local-states.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationLocalState } from './application-local-state' +import { ApplicationLocalStateMeta } from './application-local-state' + +export type LookupAccountAppLocalStates = { + appsLocalStates: ApplicationLocalState[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const LookupAccountAppLocalStatesMeta: ModelMetadata = { + name: 'LookupAccountAppLocalStates', + kind: 'object', + fields: [ + { + name: 'appsLocalStates', + wireKey: 'apps-local-states', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationLocalStateMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-assets.ts b/packages/typescript/indexer_client/src/models/lookup-account-assets.ts new file mode 100644 index 000000000..85a1a176a --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-assets.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetHolding } from './asset-holding' +import { AssetHoldingMeta } from './asset-holding' + +export type LookupAccountAssets = { + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + assets: AssetHolding[] +} + +export const LookupAccountAssetsMeta: ModelMetadata = { + name: 'LookupAccountAssets', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assets', + wireKey: 'assets', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetHoldingMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-by-id.ts b/packages/typescript/indexer_client/src/models/lookup-account-by-id.ts new file mode 100644 index 000000000..cd39cc33e --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-by-id.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Account } from './account' +import { AccountMeta } from './account' + +export type LookupAccountById = { + account: Account + + /** + * Round at which the results were computed. + */ + currentRound: bigint +} + +export const LookupAccountByIdMeta: ModelMetadata = { + name: 'LookupAccountById', + kind: 'object', + fields: [ + { + name: 'account', + wireKey: 'account', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AccountMeta }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-created-applications.ts b/packages/typescript/indexer_client/src/models/lookup-account-created-applications.ts new file mode 100644 index 000000000..7b535c1d0 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-created-applications.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Application } from './application' +import { ApplicationMeta } from './application' + +export type LookupAccountCreatedApplications = { + applications: Application[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const LookupAccountCreatedApplicationsMeta: ModelMetadata = { + name: 'LookupAccountCreatedApplications', + kind: 'object', + fields: [ + { + name: 'applications', + wireKey: 'applications', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-created-assets.ts b/packages/typescript/indexer_client/src/models/lookup-account-created-assets.ts new file mode 100644 index 000000000..b29db2e0f --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-created-assets.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Asset } from './asset' +import { AssetMeta } from './asset' + +export type LookupAccountCreatedAssets = { + assets: Asset[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const LookupAccountCreatedAssetsMeta: ModelMetadata = { + name: 'LookupAccountCreatedAssets', + kind: 'object', + fields: [ + { + name: 'assets', + wireKey: 'assets', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-account-transactions.ts b/packages/typescript/indexer_client/src/models/lookup-account-transactions.ts new file mode 100644 index 000000000..66b50627a --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-account-transactions.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Transaction } from './transaction' +import { TransactionMeta } from './transaction' + +export type LookupAccountTransactions = { + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + transactions: Transaction[] +} + +export const LookupAccountTransactionsMeta: ModelMetadata = { + name: 'LookupAccountTransactions', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'transactions', + wireKey: 'transactions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-application-by-id.ts b/packages/typescript/indexer_client/src/models/lookup-application-by-id.ts new file mode 100644 index 000000000..65e517083 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-application-by-id.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Application } from './application' +import { ApplicationMeta } from './application' + +export type LookupApplicationById = { + application?: Application + + /** + * Round at which the results were computed. + */ + currentRound: bigint +} + +export const LookupApplicationByIdMeta: ModelMetadata = { + name: 'LookupApplicationById', + kind: 'object', + fields: [ + { + name: 'application', + wireKey: 'application', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => ApplicationMeta }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-application-logs-by-id.ts b/packages/typescript/indexer_client/src/models/lookup-application-logs-by-id.ts new file mode 100644 index 000000000..a79636e86 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-application-logs-by-id.ts @@ -0,0 +1,56 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { ApplicationLogData } from './application-log-data' +import { ApplicationLogDataMeta } from './application-log-data' + +export type LookupApplicationLogsById = { + /** + * \[appidx\] application index. + */ + applicationId: bigint + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + logData?: ApplicationLogData[] +} + +export const LookupApplicationLogsByIdMeta: ModelMetadata = { + name: 'LookupApplicationLogsById', + kind: 'object', + fields: [ + { + name: 'applicationId', + wireKey: 'application-id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'logData', + wireKey: 'log-data', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationLogDataMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-asset-balances.ts b/packages/typescript/indexer_client/src/models/lookup-asset-balances.ts new file mode 100644 index 000000000..a8a6fc6e0 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-asset-balances.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { MiniAssetHolding } from './mini-asset-holding' +import { MiniAssetHoldingMeta } from './mini-asset-holding' + +export type LookupAssetBalances = { + balances: MiniAssetHolding[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const LookupAssetBalancesMeta: ModelMetadata = { + name: 'LookupAssetBalances', + kind: 'object', + fields: [ + { + name: 'balances', + wireKey: 'balances', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => MiniAssetHoldingMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-asset-by-id.ts b/packages/typescript/indexer_client/src/models/lookup-asset-by-id.ts new file mode 100644 index 000000000..ca529b682 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-asset-by-id.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Asset } from './asset' +import { AssetMeta } from './asset' + +export type LookupAssetById = { + asset: Asset + + /** + * Round at which the results were computed. + */ + currentRound: bigint +} + +export const LookupAssetByIdMeta: ModelMetadata = { + name: 'LookupAssetById', + kind: 'object', + fields: [ + { + name: 'asset', + wireKey: 'asset', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => AssetMeta }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-asset-transactions.ts b/packages/typescript/indexer_client/src/models/lookup-asset-transactions.ts new file mode 100644 index 000000000..4f15cfc49 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-asset-transactions.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Transaction } from './transaction' +import { TransactionMeta } from './transaction' + +export type LookupAssetTransactions = { + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + transactions: Transaction[] +} + +export const LookupAssetTransactionsMeta: ModelMetadata = { + name: 'LookupAssetTransactions', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'transactions', + wireKey: 'transactions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/lookup-transaction.ts b/packages/typescript/indexer_client/src/models/lookup-transaction.ts new file mode 100644 index 000000000..8efd83134 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/lookup-transaction.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Transaction } from './transaction' +import { TransactionMeta } from './transaction' + +export type LookupTransaction = { + transaction: Transaction + + /** + * Round at which the results were computed. + */ + currentRound: bigint +} + +export const LookupTransactionMeta: ModelMetadata = { + name: 'LookupTransaction', + kind: 'object', + fields: [ + { + name: 'transaction', + wireKey: 'transaction', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => TransactionMeta }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/merkle-array-proof.ts b/packages/typescript/indexer_client/src/models/merkle-array-proof.ts new file mode 100644 index 000000000..45f9847d7 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/merkle-array-proof.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { HashFactory } from './hash-factory' +import { HashFactoryMeta } from './hash-factory' + +export type MerkleArrayProof = { + /** + * \[pth\] + */ + path?: Uint8Array[] + hashFactory?: HashFactory + + /** + * \[td\] + */ + treeDepth?: bigint +} + +export const MerkleArrayProofMeta: ModelMetadata = { + name: 'MerkleArrayProof', + kind: 'object', + fields: [ + { + name: 'path', + wireKey: 'path', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + { + name: 'hashFactory', + wireKey: 'hash-factory', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => HashFactoryMeta }, + }, + { + name: 'treeDepth', + wireKey: 'tree-depth', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/mini-asset-holding.ts b/packages/typescript/indexer_client/src/models/mini-asset-holding.ts new file mode 100644 index 000000000..dfcb44a09 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/mini-asset-holding.ts @@ -0,0 +1,74 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * A simplified version of AssetHolding + */ +export type MiniAssetHolding = { + address: string + amount: bigint + isFrozen: boolean + + /** + * Whether or not this asset holding is currently deleted from its account. + */ + deleted?: boolean + + /** + * Round during which the account opted into the asset. + */ + optedInAtRound?: bigint + + /** + * Round during which the account opted out of the asset. + */ + optedOutAtRound?: bigint +} + +export const MiniAssetHoldingMeta: ModelMetadata = { + name: 'MiniAssetHolding', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'isFrozen', + wireKey: 'is-frozen', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'deleted', + wireKey: 'deleted', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'optedInAtRound', + wireKey: 'opted-in-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'optedOutAtRound', + wireKey: 'opted-out-at-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/on-completion.ts b/packages/typescript/indexer_client/src/models/on-completion.ts new file mode 100644 index 000000000..fa132ea4f --- /dev/null +++ b/packages/typescript/indexer_client/src/models/on-completion.ts @@ -0,0 +1,20 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * \[apan\] defines the what additional actions occur with the transaction. + * + * Valid types: + * * noop + * * optin + * * closeout + * * clear + * * update + * * delete + */ +export type OnCompletion = 'noop' | 'optin' | 'closeout' | 'clear' | 'update' | 'delete' + +export const OnCompletionMeta: ModelMetadata = { + name: 'OnCompletion', + kind: 'passthrough', + passThrough: { kind: 'scalar' }, +} diff --git a/packages/typescript/indexer_client/src/models/participation-updates.ts b/packages/typescript/indexer_client/src/models/participation-updates.ts new file mode 100644 index 000000000..02e4f546c --- /dev/null +++ b/packages/typescript/indexer_client/src/models/participation-updates.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Participation account data that needs to be checked/acted on by the network. + */ +export type ParticipationUpdates = { + /** + * \[partupdrmv\] a list of online accounts that needs to be converted to offline since their participation key expired. + */ + expiredParticipationAccounts?: string[] + + /** + * \[partupabs\] a list of online accounts that need to be suspended. + */ + absentParticipationAccounts?: string[] +} + +export const ParticipationUpdatesMeta: ModelMetadata = { + name: 'ParticipationUpdates', + kind: 'object', + fields: [ + { + name: 'expiredParticipationAccounts', + wireKey: 'expired-participation-accounts', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'absentParticipationAccounts', + wireKey: 'absent-participation-accounts', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/resource-ref.ts b/packages/typescript/indexer_client/src/models/resource-ref.ts new file mode 100644 index 000000000..b0080ddd3 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/resource-ref.ts @@ -0,0 +1,81 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { BoxReference } from './box-reference' +import { BoxReferenceMeta } from './box-reference' +import type { HoldingRef } from './holding-ref' +import { HoldingRefMeta } from './holding-ref' +import type { LocalsRef } from './locals-ref' +import { LocalsRefMeta } from './locals-ref' + +/** + * ResourceRef names a single resource. Only one of the fields should be set. + */ +export type ResourceRef = { + /** + * \[d\] Account whose balance record is accessible by the executing ApprovalProgram or ClearStateProgram. + */ + address?: string + + /** + * \[p\] Application id whose GlobalState may be read by the executing + * ApprovalProgram or ClearStateProgram. + */ + applicationId?: bigint + + /** + * \[s\] Asset whose AssetParams may be read by the executing + * ApprovalProgram or ClearStateProgram. + */ + assetId?: bigint + box?: BoxReference + holding?: HoldingRef + local?: LocalsRef +} + +export const ResourceRefMeta: ModelMetadata = { + name: 'ResourceRef', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'applicationId', + wireKey: 'application-id', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assetId', + wireKey: 'asset-id', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'box', + wireKey: 'box', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => BoxReferenceMeta }, + }, + { + name: 'holding', + wireKey: 'holding', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => HoldingRefMeta }, + }, + { + name: 'local', + wireKey: 'local', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => LocalsRefMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-accounts.ts b/packages/typescript/indexer_client/src/models/search-for-accounts.ts new file mode 100644 index 000000000..4109c48ae --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-accounts.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Account } from './account' +import { AccountMeta } from './account' + +export type SearchForAccounts = { + accounts: Account[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const SearchForAccountsMeta: ModelMetadata = { + name: 'SearchForAccounts', + kind: 'object', + fields: [ + { + name: 'accounts', + wireKey: 'accounts', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-application-boxes.ts b/packages/typescript/indexer_client/src/models/search-for-application-boxes.ts new file mode 100644 index 000000000..aeabf21a5 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-application-boxes.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { BoxDescriptor } from './box-descriptor' +import { BoxDescriptorMeta } from './box-descriptor' + +export type SearchForApplicationBoxes = { + /** + * \[appidx\] application index. + */ + applicationId: bigint + boxes: BoxDescriptor[] + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const SearchForApplicationBoxesMeta: ModelMetadata = { + name: 'SearchForApplicationBoxes', + kind: 'object', + fields: [ + { + name: 'applicationId', + wireKey: 'application-id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'boxes', + wireKey: 'boxes', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => BoxDescriptorMeta } }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-applications.ts b/packages/typescript/indexer_client/src/models/search-for-applications.ts new file mode 100644 index 000000000..c94b7ce14 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-applications.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Application } from './application' +import { ApplicationMeta } from './application' + +export type SearchForApplications = { + applications: Application[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const SearchForApplicationsMeta: ModelMetadata = { + name: 'SearchForApplications', + kind: 'object', + fields: [ + { + name: 'applications', + wireKey: 'applications', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ApplicationMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-assets.ts b/packages/typescript/indexer_client/src/models/search-for-assets.ts new file mode 100644 index 000000000..6e5e1b201 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-assets.ts @@ -0,0 +1,45 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Asset } from './asset' +import { AssetMeta } from './asset' + +export type SearchForAssets = { + assets: Asset[] + + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string +} + +export const SearchForAssetsMeta: ModelMetadata = { + name: 'SearchForAssets', + kind: 'object', + fields: [ + { + name: 'assets', + wireKey: 'assets', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AssetMeta } }, + }, + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-block-headers.ts b/packages/typescript/indexer_client/src/models/search-for-block-headers.ts new file mode 100644 index 000000000..c5a93235b --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-block-headers.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Block } from './block' +import { BlockMeta } from './block' + +export type SearchForBlockHeaders = { + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + blocks: Block[] +} + +export const SearchForBlockHeadersMeta: ModelMetadata = { + name: 'SearchForBlockHeaders', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'blocks', + wireKey: 'blocks', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => BlockMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/search-for-transactions.ts b/packages/typescript/indexer_client/src/models/search-for-transactions.ts new file mode 100644 index 000000000..3f42e82c7 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/search-for-transactions.ts @@ -0,0 +1,44 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { Transaction } from './transaction' +import { TransactionMeta } from './transaction' + +export type SearchForTransactions = { + /** + * Round at which the results were computed. + */ + currentRound: bigint + + /** + * Used for pagination, when making another request provide this token with the next parameter. + */ + nextToken?: string + transactions: Transaction[] +} + +export const SearchForTransactionsMeta: ModelMetadata = { + name: 'SearchForTransactions', + kind: 'object', + fields: [ + { + name: 'currentRound', + wireKey: 'current-round', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextToken', + wireKey: 'next-token', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'transactions', + wireKey: 'transactions', + optional: false, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-delta.ts b/packages/typescript/indexer_client/src/models/state-delta.ts new file mode 100644 index 000000000..ef811cfea --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-delta.ts @@ -0,0 +1,14 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { EvalDeltaKeyValue } from './eval-delta-key-value' +import { EvalDeltaKeyValueMeta } from './eval-delta-key-value' + +/** + * Application state delta. + */ +export type StateDelta = EvalDeltaKeyValue[] + +export const StateDeltaMeta: ModelMetadata = { + name: 'StateDelta', + kind: 'array', + arrayItems: { kind: 'model', meta: () => EvalDeltaKeyValueMeta }, +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-fields.ts b/packages/typescript/indexer_client/src/models/state-proof-fields.ts new file mode 100644 index 000000000..637b7df30 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-fields.ts @@ -0,0 +1,96 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { MerkleArrayProof } from './merkle-array-proof' +import { MerkleArrayProofMeta } from './merkle-array-proof' +import type { StateProofReveal } from './state-proof-reveal' +import { StateProofRevealMeta } from './state-proof-reveal' + +/** + * \[sp\] represents a state proof. + * + * Definition: + * crypto/stateproof/structs.go : StateProof + */ +export type StateProofFields = { + /** + * \[c\] + */ + sigCommit?: Uint8Array + + /** + * \[w\] + */ + signedWeight?: bigint + sigProofs?: MerkleArrayProof + partProofs?: MerkleArrayProof + + /** + * \[v\] Salt version of the merkle signature. + */ + saltVersion?: bigint + + /** + * \[r\] Note that this is actually stored as a map[uint64] - Reveal in the actual msgp + */ + reveals?: StateProofReveal[] + + /** + * \[pr\] Sequence of reveal positions. + */ + positionsToReveal?: bigint[] +} + +export const StateProofFieldsMeta: ModelMetadata = { + name: 'StateProofFields', + kind: 'object', + fields: [ + { + name: 'sigCommit', + wireKey: 'sig-commit', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'signedWeight', + wireKey: 'signed-weight', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'sigProofs', + wireKey: 'sig-proofs', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => MerkleArrayProofMeta }, + }, + { + name: 'partProofs', + wireKey: 'part-proofs', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => MerkleArrayProofMeta }, + }, + { + name: 'saltVersion', + wireKey: 'salt-version', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'reveals', + wireKey: 'reveals', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => StateProofRevealMeta } }, + }, + { + name: 'positionsToReveal', + wireKey: 'positions-to-reveal', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBigint: true } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-participant.ts b/packages/typescript/indexer_client/src/models/state-proof-participant.ts new file mode 100644 index 000000000..f1db65ced --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-participant.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateProofVerifier } from './state-proof-verifier' +import { StateProofVerifierMeta } from './state-proof-verifier' + +export type StateProofParticipant = { + verifier?: StateProofVerifier + + /** + * \[w\] + */ + weight?: bigint +} + +export const StateProofParticipantMeta: ModelMetadata = { + name: 'StateProofParticipant', + kind: 'object', + fields: [ + { + name: 'verifier', + wireKey: 'verifier', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateProofVerifierMeta }, + }, + { + name: 'weight', + wireKey: 'weight', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-reveal.ts b/packages/typescript/indexer_client/src/models/state-proof-reveal.ts new file mode 100644 index 000000000..12a1f1179 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-reveal.ts @@ -0,0 +1,42 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateProofParticipant } from './state-proof-participant' +import { StateProofParticipantMeta } from './state-proof-participant' +import type { StateProofSigSlot } from './state-proof-sig-slot' +import { StateProofSigSlotMeta } from './state-proof-sig-slot' + +export type StateProofReveal = { + /** + * The position in the signature and participants arrays corresponding to this entry. + */ + position?: bigint + sigSlot?: StateProofSigSlot + participant?: StateProofParticipant +} + +export const StateProofRevealMeta: ModelMetadata = { + name: 'StateProofReveal', + kind: 'object', + fields: [ + { + name: 'position', + wireKey: 'position', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'sigSlot', + wireKey: 'sig-slot', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateProofSigSlotMeta }, + }, + { + name: 'participant', + wireKey: 'participant', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateProofParticipantMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-sig-slot.ts b/packages/typescript/indexer_client/src/models/state-proof-sig-slot.ts new file mode 100644 index 000000000..b69d3281e --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-sig-slot.ts @@ -0,0 +1,33 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { StateProofSignature } from './state-proof-signature' +import { StateProofSignatureMeta } from './state-proof-signature' + +export type StateProofSigSlot = { + signature?: StateProofSignature + + /** + * \[l\] The total weight of signatures in the lower-numbered slots. + */ + lowerSigWeight?: bigint +} + +export const StateProofSigSlotMeta: ModelMetadata = { + name: 'StateProofSigSlot', + kind: 'object', + fields: [ + { + name: 'signature', + wireKey: 'signature', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateProofSignatureMeta }, + }, + { + name: 'lowerSigWeight', + wireKey: 'lower-sig-weight', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-signature.ts b/packages/typescript/indexer_client/src/models/state-proof-signature.ts new file mode 100644 index 000000000..19992e49a --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-signature.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { MerkleArrayProof } from './merkle-array-proof' +import { MerkleArrayProofMeta } from './merkle-array-proof' + +export type StateProofSignature = { + falconSignature?: Uint8Array + merkleArrayIndex?: bigint + proof?: MerkleArrayProof + + /** + * \[vkey\] + */ + verifyingKey?: Uint8Array +} + +export const StateProofSignatureMeta: ModelMetadata = { + name: 'StateProofSignature', + kind: 'object', + fields: [ + { + name: 'falconSignature', + wireKey: 'falcon-signature', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'merkleArrayIndex', + wireKey: 'merkle-array-index', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'proof', + wireKey: 'proof', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => MerkleArrayProofMeta }, + }, + { + name: 'verifyingKey', + wireKey: 'verifying-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-tracking.ts b/packages/typescript/indexer_client/src/models/state-proof-tracking.ts new file mode 100644 index 000000000..c1e32280d --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-tracking.ts @@ -0,0 +1,58 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type StateProofTracking = { + /** + * State Proof Type. Note the raw object uses map with this as key. + */ + type?: bigint + + /** + * \[v\] Root of a vector commitment containing online accounts that will help sign the proof. + */ + votersCommitment?: Uint8Array + + /** + * \[t\] The total number of microalgos held by the online accounts during the StateProof round. + */ + onlineTotalWeight?: bigint + + /** + * \[n\] Next round for which we will accept a state proof transaction. + */ + nextRound?: number +} + +export const StateProofTrackingMeta: ModelMetadata = { + name: 'StateProofTracking', + kind: 'object', + fields: [ + { + name: 'type', + wireKey: 'type', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'votersCommitment', + wireKey: 'voters-commitment', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'onlineTotalWeight', + wireKey: 'online-total-weight', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'nextRound', + wireKey: 'next-round', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-proof-verifier.ts b/packages/typescript/indexer_client/src/models/state-proof-verifier.ts new file mode 100644 index 000000000..c13fc06d3 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-proof-verifier.ts @@ -0,0 +1,34 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type StateProofVerifier = { + /** + * \[cmt\] Represents the root of the vector commitment tree. + */ + commitment?: Uint8Array + + /** + * \[lf\] Key lifetime. + */ + keyLifetime?: bigint +} + +export const StateProofVerifierMeta: ModelMetadata = { + name: 'StateProofVerifier', + kind: 'object', + fields: [ + { + name: 'commitment', + wireKey: 'commitment', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'keyLifetime', + wireKey: 'key-lifetime', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/state-schema.ts b/packages/typescript/indexer_client/src/models/state-schema.ts new file mode 100644 index 000000000..76f5f2151 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/state-schema.ts @@ -0,0 +1,37 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents a \[apls\] local-state or \[apgs\] global-state schema. These schemas determine how much storage may be used in a local-state or global-state for an application. The more space used, the larger minimum balance must be maintained in the account holding the data. + */ +export type StateSchema = { + /** + * Maximum number of TEAL uints that may be stored in the key/value store. + */ + numUint: number + + /** + * Maximum number of TEAL byte slices that may be stored in the key/value store. + */ + numByteSlice: number +} + +export const StateSchemaMeta: ModelMetadata = { + name: 'StateSchema', + kind: 'object', + fields: [ + { + name: 'numUint', + wireKey: 'num-uint', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'numByteSlice', + wireKey: 'num-byte-slice', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/teal-key-value-store.ts b/packages/typescript/indexer_client/src/models/teal-key-value-store.ts new file mode 100644 index 000000000..d0ad997b3 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/teal-key-value-store.ts @@ -0,0 +1,14 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TealKeyValue } from './teal-key-value' +import { TealKeyValueMeta } from './teal-key-value' + +/** + * Represents a key-value store for use in an application. + */ +export type TealKeyValueStore = TealKeyValue[] + +export const TealKeyValueStoreMeta: ModelMetadata = { + name: 'TealKeyValueStore', + kind: 'array', + arrayItems: { kind: 'model', meta: () => TealKeyValueMeta }, +} diff --git a/packages/typescript/indexer_client/src/models/teal-key-value.ts b/packages/typescript/indexer_client/src/models/teal-key-value.ts new file mode 100644 index 000000000..73575a78d --- /dev/null +++ b/packages/typescript/indexer_client/src/models/teal-key-value.ts @@ -0,0 +1,32 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TealValue } from './teal-value' +import { TealValueMeta } from './teal-value' + +/** + * Represents a key-value pair in an application store. + */ +export type TealKeyValue = { + key: string + value: TealValue +} + +export const TealKeyValueMeta: ModelMetadata = { + name: 'TealKeyValue', + kind: 'object', + fields: [ + { + name: 'key', + wireKey: 'key', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'value', + wireKey: 'value', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => TealValueMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/teal-value.ts b/packages/typescript/indexer_client/src/models/teal-value.ts new file mode 100644 index 000000000..a069b2e6e --- /dev/null +++ b/packages/typescript/indexer_client/src/models/teal-value.ts @@ -0,0 +1,49 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Represents a TEAL value. + */ +export type TealValue = { + /** + * type of the value. Value `1` refers to **bytes**, value `2` refers to **uint** + */ + type: number + + /** + * bytes value. + */ + bytes: Uint8Array + + /** + * uint value. + */ + uint: bigint +} + +export const TealValueMeta: ModelMetadata = { + name: 'TealValue', + kind: 'object', + fields: [ + { + name: 'type', + wireKey: 'type', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'bytes', + wireKey: 'bytes', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'uint', + wireKey: 'uint', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-application.ts b/packages/typescript/indexer_client/src/models/transaction-application.ts new file mode 100644 index 000000000..f1783a1a5 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-application.ts @@ -0,0 +1,180 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { BoxReference } from './box-reference' +import { BoxReferenceMeta } from './box-reference' +import type { OnCompletion } from './on-completion' +import { OnCompletionMeta } from './on-completion' +import type { ResourceRef } from './resource-ref' +import { ResourceRefMeta } from './resource-ref' +import type { StateSchema } from './state-schema' +import { StateSchemaMeta } from './state-schema' + +/** + * Fields for application transactions. + * + * Definition: + * data/transactions/application.go : ApplicationCallTxnFields + */ +export type TransactionApplication = { + /** + * \[apid\] ID of the application being configured or empty if creating. + */ + applicationId: bigint + onCompletion: OnCompletion + + /** + * \[apaa\] transaction specific arguments accessed from the application's approval-program and clear-state-program. + */ + applicationArgs?: string[] + + /** + * \[al\] Access unifies `accounts`, `foreign-apps`, `foreign-assets`, and `box-references` under a single list. If access is non-empty, these lists must be empty. If access is empty, those lists may be non-empty. + */ + access?: ResourceRef[] + + /** + * \[apat\] List of accounts in addition to the sender that may be accessed from the application's approval-program and clear-state-program. + */ + accounts?: string[] + + /** + * \[apbx\] the boxes that can be accessed by this transaction (and others in the same group). + */ + boxReferences?: BoxReference[] + + /** + * \[apfa\] Lists the applications in addition to the application-id whose global states may be accessed by this application's approval-program and clear-state-program. The access is read-only. + */ + foreignApps?: bigint[] + + /** + * \[apas\] lists the assets whose parameters may be accessed by this application's ApprovalProgram and ClearStateProgram. The access is read-only. + */ + foreignAssets?: bigint[] + localStateSchema?: StateSchema + globalStateSchema?: StateSchema + + /** + * \[apap\] Logic executed for every application transaction, except when on-completion is set to "clear". It can read and write global state for the application, as well as account-specific local state. Approval programs may reject the transaction. + */ + approvalProgram?: Uint8Array + + /** + * \[apsu\] Logic executed for application transactions with on-completion set to "clear". It can read and write global state for the application, as well as account-specific local state. Clear state programs cannot reject the transaction. + */ + clearStateProgram?: Uint8Array + + /** + * \[epp\] specifies the additional app program len requested in pages. + */ + extraProgramPages?: number + + /** + * \[aprv\] the lowest application version for which this transaction should immediately fail. 0 indicates that no version check should be performed. + */ + rejectVersion?: bigint +} + +export const TransactionApplicationMeta: ModelMetadata = { + name: 'TransactionApplication', + kind: 'object', + fields: [ + { + name: 'applicationId', + wireKey: 'application-id', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'onCompletion', + wireKey: 'on-completion', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => OnCompletionMeta }, + }, + { + name: 'applicationArgs', + wireKey: 'application-args', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'access', + wireKey: 'access', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => ResourceRefMeta } }, + }, + { + name: 'accounts', + wireKey: 'accounts', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'boxReferences', + wireKey: 'box-references', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => BoxReferenceMeta } }, + }, + { + name: 'foreignApps', + wireKey: 'foreign-apps', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'foreignAssets', + wireKey: 'foreign-assets', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'localStateSchema', + wireKey: 'local-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateSchemaMeta }, + }, + { + name: 'globalStateSchema', + wireKey: 'global-state-schema', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateSchemaMeta }, + }, + { + name: 'approvalProgram', + wireKey: 'approval-program', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'clearStateProgram', + wireKey: 'clear-state-program', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'extraProgramPages', + wireKey: 'extra-program-pages', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rejectVersion', + wireKey: 'reject-version', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-asset-config.ts b/packages/typescript/indexer_client/src/models/transaction-asset-config.ts new file mode 100644 index 000000000..6de8eb53e --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-asset-config.ts @@ -0,0 +1,42 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AssetParams } from './asset-params' +import { AssetParamsMeta } from './asset-params' + +/** + * Fields for asset allocation, re-configuration, and destruction. + * + * + * A zero value for asset-id indicates asset creation. + * A zero value for the params indicates asset destruction. + * + * Definition: + * data/transactions/asset.go : AssetConfigTxnFields + */ +export type TransactionAssetConfig = { + /** + * \[xaid\] ID of the asset being configured or empty if creating. + */ + assetId?: bigint + params?: AssetParams +} + +export const TransactionAssetConfigMeta: ModelMetadata = { + name: 'TransactionAssetConfig', + kind: 'object', + fields: [ + { + name: 'assetId', + wireKey: 'asset-id', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'params', + wireKey: 'params', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => AssetParamsMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-asset-freeze.ts b/packages/typescript/indexer_client/src/models/transaction-asset-freeze.ts new file mode 100644 index 000000000..5dac2de50 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-asset-freeze.ts @@ -0,0 +1,52 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields for an asset freeze transaction. + * + * Definition: + * data/transactions/asset.go : AssetFreezeTxnFields + */ +export type TransactionAssetFreeze = { + /** + * \[fadd\] Address of the account whose asset is being frozen or thawed. + */ + address: string + + /** + * \[faid\] ID of the asset being frozen or thawed. + */ + assetId: bigint + + /** + * \[afrz\] The new freeze status. + */ + newFreezeStatus: boolean +} + +export const TransactionAssetFreezeMeta: ModelMetadata = { + name: 'TransactionAssetFreeze', + kind: 'object', + fields: [ + { + name: 'address', + wireKey: 'address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'assetId', + wireKey: 'asset-id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'newFreezeStatus', + wireKey: 'new-freeze-status', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-asset-transfer.ts b/packages/typescript/indexer_client/src/models/transaction-asset-transfer.ts new file mode 100644 index 000000000..c77fe36dc --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-asset-transfer.ts @@ -0,0 +1,88 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields for an asset transfer transaction. + * + * Definition: + * data/transactions/asset.go : AssetTransferTxnFields + */ +export type TransactionAssetTransfer = { + /** + * \[aamt\] Amount of asset to transfer. A zero amount transferred to self allocates that asset in the account's Assets map. + */ + amount: bigint + + /** + * \[xaid\] ID of the asset being transferred. + */ + assetId: bigint + + /** + * Number of assets transferred to the close-to account as part of the transaction. + */ + closeAmount?: bigint + + /** + * \[aclose\] Indicates that the asset should be removed from the account's Assets map, and specifies where the remaining asset holdings should be transferred. It's always valid to transfer remaining asset holdings to the creator account. + */ + closeTo?: string + + /** + * \[arcv\] Recipient address of the transfer. + */ + receiver: string + + /** + * \[asnd\] The effective sender during a clawback transactions. If this is not a zero value, the real transaction sender must be the Clawback address from the AssetParams. + */ + sender?: string +} + +export const TransactionAssetTransferMeta: ModelMetadata = { + name: 'TransactionAssetTransfer', + kind: 'object', + fields: [ + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'assetId', + wireKey: 'asset-id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closeAmount', + wireKey: 'close-amount', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closeTo', + wireKey: 'close-to', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'receiver', + wireKey: 'receiver', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sender', + wireKey: 'sender', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-heartbeat.ts b/packages/typescript/indexer_client/src/models/transaction-heartbeat.ts new file mode 100644 index 000000000..b0c665ea1 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-heartbeat.ts @@ -0,0 +1,74 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { HbProofFields } from './hb-proof-fields' +import { HbProofFieldsMeta } from './hb-proof-fields' + +/** + * Fields for a heartbeat transaction. + * + * Definition: + * data/transactions/heartbeat.go : HeartbeatTxnFields + */ +export type TransactionHeartbeat = { + /** + * \[hbad\] HbAddress is the account this txn is proving onlineness for. + */ + hbAddress: string + hbProof: HbProofFields + + /** + * \[hbsd\] HbSeed must be the block seed for the this transaction's firstValid block. + */ + hbSeed: Uint8Array + + /** + * \[hbvid\] HbVoteID must match the HbAddress account's current VoteID. + */ + hbVoteId: Uint8Array + + /** + * \[hbkd\] HbKeyDilution must match HbAddress account's current KeyDilution. + */ + hbKeyDilution: bigint +} + +export const TransactionHeartbeatMeta: ModelMetadata = { + name: 'TransactionHeartbeat', + kind: 'object', + fields: [ + { + name: 'hbAddress', + wireKey: 'hb-address', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'hbProof', + wireKey: 'hb-proof', + optional: false, + nullable: false, + type: { kind: 'model', meta: () => HbProofFieldsMeta }, + }, + { + name: 'hbSeed', + wireKey: 'hb-seed', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbVoteId', + wireKey: 'hb-vote-id', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'hbKeyDilution', + wireKey: 'hb-key-dilution', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-keyreg.ts b/packages/typescript/indexer_client/src/models/transaction-keyreg.ts new file mode 100644 index 000000000..1fa4ff925 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-keyreg.ts @@ -0,0 +1,100 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields for a keyreg transaction. + * + * Definition: + * data/transactions/keyreg.go : KeyregTxnFields + */ +export type TransactionKeyreg = { + /** + * \[nonpart\] Mark the account as participating or non-participating. + */ + nonParticipation?: boolean + + /** + * \[selkey\] Public key used with the Verified Random Function (VRF) result during committee selection. + */ + selectionParticipationKey?: Uint8Array + + /** + * \[votefst\] First round this participation key is valid. + */ + voteFirstValid?: bigint + + /** + * \[votekd\] Number of subkeys in each batch of participation keys. + */ + voteKeyDilution?: bigint + + /** + * \[votelst\] Last round this participation key is valid. + */ + voteLastValid?: bigint + + /** + * \[votekey\] Participation public key used in key registration transactions. + */ + voteParticipationKey?: Uint8Array + + /** + * \[sprfkey\] State proof key used in key registration transactions. + */ + stateProofKey?: Uint8Array +} + +export const TransactionKeyregMeta: ModelMetadata = { + name: 'TransactionKeyreg', + kind: 'object', + fields: [ + { + name: 'nonParticipation', + wireKey: 'non-participation', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'selectionParticipationKey', + wireKey: 'selection-participation-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'voteFirstValid', + wireKey: 'vote-first-valid', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteKeyDilution', + wireKey: 'vote-key-dilution', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteLastValid', + wireKey: 'vote-last-valid', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'voteParticipationKey', + wireKey: 'vote-participation-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'stateProofKey', + wireKey: 'state-proof-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-payment.ts b/packages/typescript/indexer_client/src/models/transaction-payment.ts new file mode 100644 index 000000000..ddc4184bd --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-payment.ts @@ -0,0 +1,64 @@ +import type { ModelMetadata } from '../core/model-runtime' + +/** + * Fields for a payment transaction. + * + * Definition: + * data/transactions/payment.go : PaymentTxnFields + */ +export type TransactionPayment = { + /** + * \[amt\] number of MicroAlgos intended to be transferred. + */ + amount: bigint + + /** + * Number of MicroAlgos that were sent to the close-remainder-to address when closing the sender account. + */ + closeAmount?: bigint + + /** + * \[close\] when set, indicates that the sending account should be closed and all remaining funds be transferred to this address. + */ + closeRemainderTo?: string + + /** + * \[rcv\] receiver's address. + */ + receiver: string +} + +export const TransactionPaymentMeta: ModelMetadata = { + name: 'TransactionPayment', + kind: 'object', + fields: [ + { + name: 'amount', + wireKey: 'amount', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'closeAmount', + wireKey: 'close-amount', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'closeRemainderTo', + wireKey: 'close-remainder-to', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'receiver', + wireKey: 'receiver', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-signature-logicsig.ts b/packages/typescript/indexer_client/src/models/transaction-signature-logicsig.ts new file mode 100644 index 000000000..d5f43cabf --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-signature-logicsig.ts @@ -0,0 +1,70 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TransactionSignatureMultisig } from './transaction-signature-multisig' +import { TransactionSignatureMultisigMeta } from './transaction-signature-multisig' + +/** + * \[lsig\] Programatic transaction signature. + * + * Definition: + * data/transactions/logicsig.go + */ +export type TransactionSignatureLogicsig = { + /** + * \[arg\] Logic arguments, base64 encoded. + */ + args?: string[] + + /** + * \[l\] Program signed by a signature or multi signature, or hashed to be the address of an account. Base64 encoded TEAL program. + */ + logic: Uint8Array + multisigSignature?: TransactionSignatureMultisig + logicMultisigSignature?: TransactionSignatureMultisig + + /** + * \[sig\] ed25519 signature. + */ + signature?: Uint8Array +} + +export const TransactionSignatureLogicsigMeta: ModelMetadata = { + name: 'TransactionSignatureLogicsig', + kind: 'object', + fields: [ + { + name: 'args', + wireKey: 'args', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar' } }, + }, + { + name: 'logic', + wireKey: 'logic', + optional: false, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'multisigSignature', + wireKey: 'multisig-signature', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionSignatureMultisigMeta }, + }, + { + name: 'logicMultisigSignature', + wireKey: 'logic-multisig-signature', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionSignatureMultisigMeta }, + }, + { + name: 'signature', + wireKey: 'signature', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-signature-multisig-subsignature.ts b/packages/typescript/indexer_client/src/models/transaction-signature-multisig-subsignature.ts new file mode 100644 index 000000000..3630db5eb --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-signature-multisig-subsignature.ts @@ -0,0 +1,34 @@ +import type { ModelMetadata } from '../core/model-runtime' + +export type TransactionSignatureMultisigSubsignature = { + /** + * \[pk\] + */ + publicKey?: Uint8Array + + /** + * \[s\] + */ + signature?: Uint8Array +} + +export const TransactionSignatureMultisigSubsignatureMeta: ModelMetadata = { + name: 'TransactionSignatureMultisigSubsignature', + kind: 'object', + fields: [ + { + name: 'publicKey', + wireKey: 'public-key', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'signature', + wireKey: 'signature', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-signature-multisig.ts b/packages/typescript/indexer_client/src/models/transaction-signature-multisig.ts new file mode 100644 index 000000000..a6e9c02df --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-signature-multisig.ts @@ -0,0 +1,54 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TransactionSignatureMultisigSubsignature } from './transaction-signature-multisig-subsignature' +import { TransactionSignatureMultisigSubsignatureMeta } from './transaction-signature-multisig-subsignature' + +/** + * structure holding multiple subsignatures. + * + * Definition: + * crypto/multisig.go : MultisigSig + */ +export type TransactionSignatureMultisig = { + /** + * \[subsig\] holds pairs of public key and signatures. + */ + subsignature?: TransactionSignatureMultisigSubsignature[] + + /** + * \[thr\] + */ + threshold?: bigint + + /** + * \[v\] + */ + version?: bigint +} + +export const TransactionSignatureMultisigMeta: ModelMetadata = { + name: 'TransactionSignatureMultisig', + kind: 'object', + fields: [ + { + name: 'subsignature', + wireKey: 'subsignature', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionSignatureMultisigSubsignatureMeta } }, + }, + { + name: 'threshold', + wireKey: 'threshold', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'version', + wireKey: 'version', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-signature.ts b/packages/typescript/indexer_client/src/models/transaction-signature.ts new file mode 100644 index 000000000..679d8f709 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-signature.ts @@ -0,0 +1,46 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { TransactionSignatureLogicsig } from './transaction-signature-logicsig' +import { TransactionSignatureLogicsigMeta } from './transaction-signature-logicsig' +import type { TransactionSignatureMultisig } from './transaction-signature-multisig' +import { TransactionSignatureMultisigMeta } from './transaction-signature-multisig' + +/** + * Validation signature associated with some data. Only one of the signatures should be provided. + */ +export type TransactionSignature = { + logicsig?: TransactionSignatureLogicsig + multisig?: TransactionSignatureMultisig + + /** + * \[sig\] Standard ed25519 signature. + */ + sig?: Uint8Array +} + +export const TransactionSignatureMeta: ModelMetadata = { + name: 'TransactionSignature', + kind: 'object', + fields: [ + { + name: 'logicsig', + wireKey: 'logicsig', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionSignatureLogicsigMeta }, + }, + { + name: 'multisig', + wireKey: 'multisig', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionSignatureMultisigMeta }, + }, + { + name: 'sig', + wireKey: 'sig', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction-state-proof.ts b/packages/typescript/indexer_client/src/models/transaction-state-proof.ts new file mode 100644 index 000000000..a254402f7 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction-state-proof.ts @@ -0,0 +1,48 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { IndexerStateProofMessage } from './indexer-state-proof-message' +import { IndexerStateProofMessageMeta } from './indexer-state-proof-message' +import type { StateProofFields } from './state-proof-fields' +import { StateProofFieldsMeta } from './state-proof-fields' + +/** + * Fields for a state proof transaction. + * + * Definition: + * data/transactions/stateproof.go : StateProofTxnFields + */ +export type TransactionStateProof = { + /** + * \[sptype\] Type of the state proof. Integer representing an entry defined in protocol/stateproof.go + */ + stateProofType?: bigint + stateProof?: StateProofFields + message?: IndexerStateProofMessage +} + +export const TransactionStateProofMeta: ModelMetadata = { + name: 'TransactionStateProof', + kind: 'object', + fields: [ + { + name: 'stateProofType', + wireKey: 'state-proof-type', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'stateProof', + wireKey: 'state-proof', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateProofFieldsMeta }, + }, + { + name: 'message', + wireKey: 'message', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => IndexerStateProofMessageMeta }, + }, + ], +} diff --git a/packages/typescript/indexer_client/src/models/transaction.ts b/packages/typescript/indexer_client/src/models/transaction.ts new file mode 100644 index 000000000..03bb09e96 --- /dev/null +++ b/packages/typescript/indexer_client/src/models/transaction.ts @@ -0,0 +1,430 @@ +import type { ModelMetadata } from '../core/model-runtime' +import type { AccountStateDelta } from './account-state-delta' +import { AccountStateDeltaMeta } from './account-state-delta' +import type { StateDelta } from './state-delta' +import { StateDeltaMeta } from './state-delta' +import type { TransactionApplication } from './transaction-application' +import { TransactionApplicationMeta } from './transaction-application' +import type { TransactionAssetConfig } from './transaction-asset-config' +import { TransactionAssetConfigMeta } from './transaction-asset-config' +import type { TransactionAssetFreeze } from './transaction-asset-freeze' +import { TransactionAssetFreezeMeta } from './transaction-asset-freeze' +import type { TransactionAssetTransfer } from './transaction-asset-transfer' +import { TransactionAssetTransferMeta } from './transaction-asset-transfer' +import type { TransactionHeartbeat } from './transaction-heartbeat' +import { TransactionHeartbeatMeta } from './transaction-heartbeat' +import type { TransactionKeyreg } from './transaction-keyreg' +import { TransactionKeyregMeta } from './transaction-keyreg' +import type { TransactionPayment } from './transaction-payment' +import { TransactionPaymentMeta } from './transaction-payment' +import type { TransactionSignature } from './transaction-signature' +import { TransactionSignatureMeta } from './transaction-signature' +import type { TransactionStateProof } from './transaction-state-proof' +import { TransactionStateProofMeta } from './transaction-state-proof' + +/** + * Contains all fields common to all transactions and serves as an envelope to all transactions type. Represents both regular and inner transactions. + * + * Definition: + * data/transactions/signedtxn.go : SignedTxn + * data/transactions/transaction.go : Transaction + */ +export type Transaction = { + applicationTransaction?: TransactionApplication + assetConfigTransaction?: TransactionAssetConfig + assetFreezeTransaction?: TransactionAssetFreeze + assetTransferTransaction?: TransactionAssetTransfer + stateProofTransaction?: TransactionStateProof + heartbeatTransaction?: TransactionHeartbeat + + /** + * \[sgnr\] this is included with signed transactions when the signing address does not equal the sender. The backend can use this to ensure that auth addr is equal to the accounts auth addr. + */ + authAddr?: string + + /** + * \[rc\] rewards applied to close-remainder-to account. + */ + closeRewards?: bigint + + /** + * \[ca\] closing amount for transaction. + */ + closingAmount?: bigint + + /** + * Round when the transaction was confirmed. + */ + confirmedRound?: bigint + + /** + * Specifies an application index (ID) if an application was created with this transaction. + */ + createdApplicationIndex?: bigint + + /** + * Specifies an asset index (ID) if an asset was created with this transaction. + */ + createdAssetIndex?: bigint + + /** + * \[fee\] Transaction fee. + */ + fee: bigint + + /** + * \[fv\] First valid round for this transaction. + */ + firstValid: number + + /** + * \[gh\] Hash of genesis block. + */ + genesisHash?: Uint8Array + + /** + * \[gen\] genesis block ID. + */ + genesisId?: string + + /** + * \[grp\] Base64 encoded byte array of a sha512/256 digest. When present indicates that this transaction is part of a transaction group and the value is the sha512/256 hash of the transactions in that group. + */ + group?: Uint8Array + + /** + * Transaction ID + */ + id?: string + + /** + * Offset into the round where this transaction was confirmed. + */ + intraRoundOffset?: bigint + keyregTransaction?: TransactionKeyreg + + /** + * \[lv\] Last valid round for this transaction. + */ + lastValid: number + + /** + * \[lx\] Base64 encoded 32-byte array. Lease enforces mutual exclusion of transactions. If this field is nonzero, then once the transaction is confirmed, it acquires the lease identified by the (Sender, Lease) pair of the transaction until the LastValid round passes. While this transaction possesses the lease, no other transaction specifying this lease can be confirmed. + */ + lease?: Uint8Array + + /** + * \[note\] Free form data. + */ + note?: Uint8Array + paymentTransaction?: TransactionPayment + + /** + * \[rr\] rewards applied to receiver account. + */ + receiverRewards?: bigint + + /** + * \[rekey\] when included in a valid transaction, the accounts auth addr will be updated with this value and future signatures must be signed with the key represented by this address. + */ + rekeyTo?: string + + /** + * Time when the block this transaction is in was confirmed. + */ + roundTime?: bigint + + /** + * \[snd\] Sender's address. + */ + sender: string + + /** + * \[rs\] rewards applied to sender account. + */ + senderRewards?: bigint + signature?: TransactionSignature + + /** + * \[type\] Indicates what type of transaction this is. Different types have different fields. + * + * Valid types, and where their fields are stored: + * * \[pay\] payment-transaction + * * \[keyreg\] keyreg-transaction + * * \[acfg\] asset-config-transaction + * * \[axfer\] asset-transfer-transaction + * * \[afrz\] asset-freeze-transaction + * * \[appl\] application-transaction + * * \[stpf\] state-proof-transaction + * * \[hb\] heartbeat-transaction + */ + txType: 'pay' | 'keyreg' | 'acfg' | 'axfer' | 'afrz' | 'appl' | 'stpf' | 'hb' + + /** + * \[ld\] Local state key/value changes for the application being executed by this transaction. + */ + localStateDelta?: AccountStateDelta[] + globalStateDelta?: StateDelta + + /** + * \[lg\] Logs for the application being executed by this transaction. + */ + logs?: Uint8Array[] + + /** + * Inner transactions produced by application execution. + */ + innerTxns?: Transaction[] +} + +export const TransactionMeta: ModelMetadata = { + name: 'Transaction', + kind: 'object', + fields: [ + { + name: 'applicationTransaction', + wireKey: 'application-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionApplicationMeta }, + }, + { + name: 'assetConfigTransaction', + wireKey: 'asset-config-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionAssetConfigMeta }, + }, + { + name: 'assetFreezeTransaction', + wireKey: 'asset-freeze-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionAssetFreezeMeta }, + }, + { + name: 'assetTransferTransaction', + wireKey: 'asset-transfer-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionAssetTransferMeta }, + }, + { + name: 'stateProofTransaction', + wireKey: 'state-proof-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionStateProofMeta }, + }, + { + name: 'heartbeatTransaction', + wireKey: 'heartbeat-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionHeartbeatMeta }, + }, + { + name: 'authAddr', + wireKey: 'auth-addr', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'closeRewards', + wireKey: 'close-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'closingAmount', + wireKey: 'closing-amount', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'confirmedRound', + wireKey: 'confirmed-round', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'createdApplicationIndex', + wireKey: 'created-application-index', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'createdAssetIndex', + wireKey: 'created-asset-index', + optional: true, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'fee', + wireKey: 'fee', + optional: false, + nullable: false, + type: { kind: 'scalar', isBigint: true }, + }, + { + name: 'firstValid', + wireKey: 'first-valid', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'genesisHash', + wireKey: 'genesis-hash', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'genesisId', + wireKey: 'genesis-id', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'group', + wireKey: 'group', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'id', + wireKey: 'id', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'intraRoundOffset', + wireKey: 'intra-round-offset', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'keyregTransaction', + wireKey: 'keyreg-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionKeyregMeta }, + }, + { + name: 'lastValid', + wireKey: 'last-valid', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'lease', + wireKey: 'lease', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'note', + wireKey: 'note', + optional: true, + nullable: false, + type: { kind: 'scalar', isBytes: true }, + }, + { + name: 'paymentTransaction', + wireKey: 'payment-transaction', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionPaymentMeta }, + }, + { + name: 'receiverRewards', + wireKey: 'receiver-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'rekeyTo', + wireKey: 'rekey-to', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'roundTime', + wireKey: 'round-time', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'sender', + wireKey: 'sender', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'senderRewards', + wireKey: 'sender-rewards', + optional: true, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'signature', + wireKey: 'signature', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => TransactionSignatureMeta }, + }, + { + name: 'txType', + wireKey: 'tx-type', + optional: false, + nullable: false, + type: { kind: 'scalar' }, + }, + { + name: 'localStateDelta', + wireKey: 'local-state-delta', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => AccountStateDeltaMeta } }, + }, + { + name: 'globalStateDelta', + wireKey: 'global-state-delta', + optional: true, + nullable: false, + type: { kind: 'model', meta: () => StateDeltaMeta }, + }, + { + name: 'logs', + wireKey: 'logs', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'scalar', isBytes: true } }, + }, + { + name: 'innerTxns', + wireKey: 'inner-txns', + optional: true, + nullable: false, + type: { kind: 'array', item: { kind: 'model', meta: () => TransactionMeta } }, + }, + ], +} diff --git a/packages/typescript/indexer_client/tsconfig.build.json b/packages/typescript/indexer_client/tsconfig.build.json new file mode 100644 index 000000000..0e149d398 --- /dev/null +++ b/packages/typescript/indexer_client/tsconfig.build.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "tests/**/*.*"] +} diff --git a/packages/typescript/indexer_client/tsconfig.json b/packages/typescript/indexer_client/tsconfig.json new file mode 100644 index 000000000..4671bbbcb --- /dev/null +++ b/packages/typescript/indexer_client/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "tsBuildInfoFile": "build/.tsbuildinfo" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/typescript/indexer_client/tsconfig.test.json b/packages/typescript/indexer_client/tsconfig.test.json new file mode 100644 index 000000000..4b8f61b29 --- /dev/null +++ b/packages/typescript/indexer_client/tsconfig.test.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "isolatedModules": true + } +} diff --git a/packages/typescript/package-lock.json b/packages/typescript/package-lock.json index 5a8296de8..c12699502 100644 --- a/packages/typescript/package-lock.json +++ b/packages/typescript/package-lock.json @@ -12,7 +12,8 @@ "algokit_abi", "algokit_transact", "algod_client", - "algokit_utils" + "algokit_utils", + "indexer_client" ], "dependencies": { "@msgpack/msgpack": "^3.1.2", @@ -28,6 +29,7 @@ "@types/node": "^20.19.17", "@typescript-eslint/eslint-plugin": "^8.44.0", "@vitest/coverage-v8": "^3.2.4", + "algosdk": "^3.5.0", "better-npm-audit": "^3.11.0", "cpy-cli": "^6.0.0", "eslint": "^9.35.0", @@ -50,20 +52,37 @@ "name": "@algorandfoundation/algod-client", "version": "0.1.0", "license": "MIT", + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, "devDependencies": {}, "engines": { "node": ">=20.0" } }, + "algod_client/algokit_transact/dist": { + "dev": true + }, "algod_client/dist": { "name": "@algorandfoundation/algod-client", "version": "0.1.0", "dev": true, "license": "MIT", + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, "engines": { "node": ">=20.0" } }, + "algod_client/dist/node_modules/@algorandfoundation/algokit-transact": { + "resolved": "algod_client/algokit_transact/dist", + "link": true + }, + "algod_client/node_modules/@algorandfoundation/algokit-transact": { + "resolved": "algokit_transact/dist", + "link": true + }, "algokit_abi": { "name": "@algorandfoundation/algokit-abi", "version": "0.1.0", @@ -120,7 +139,6 @@ "algokit_transact/dist": { "name": "@algorandfoundation/algokit-transact", "version": "0.1.0", - "dev": true, "license": "MIT", "engines": { "node": ">=20.0" @@ -144,7 +162,8 @@ "@algorandfoundation/algod-client": "../algod_client/dist", "@algorandfoundation/algokit-abi": "../algokit_abi/dist", "@algorandfoundation/algokit-common": "../algokit_common/dist", - "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + "@algorandfoundation/algokit-transact": "../algokit_transact/dist", + "@algorandfoundation/indexer-client": "../indexer_client/dist" }, "engines": { "node": ">=20.0" @@ -166,6 +185,45 @@ "resolved": "algokit_transact/dist", "link": true }, + "algokit_utils/node_modules/@algorandfoundation/indexer-client": { + "resolved": "indexer_client/dist", + "link": true + }, + "indexer_client": { + "name": "@algorandfoundation/indexer-client", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, + "devDependencies": {}, + "engines": { + "node": ">=20.0" + } + }, + "indexer_client/algokit_transact/dist": { + "dev": true + }, + "indexer_client/dist": { + "name": "@algorandfoundation/indexer-client", + "version": "0.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@algorandfoundation/algokit-transact": "../algokit_transact/dist" + }, + "engines": { + "node": ">=20.0" + } + }, + "indexer_client/dist/node_modules/@algorandfoundation/algokit-transact": { + "resolved": "indexer_client/algokit_transact/dist", + "link": true + }, + "indexer_client/node_modules/@algorandfoundation/algokit-transact": { + "resolved": "algokit_transact/dist", + "link": true + }, "node_modules/@algorandfoundation/algod-client": { "resolved": "algod_client", "link": true @@ -186,6 +244,10 @@ "resolved": "algokit_utils", "link": true }, + "node_modules/@algorandfoundation/indexer-client": { + "resolved": "indexer_client", + "link": true + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2221,6 +2283,43 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/algorand-msgpack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/algorand-msgpack/-/algorand-msgpack-1.1.0.tgz", + "integrity": "sha512-08k7pBQnkaUB5p+jL7f1TRaUIlTSDE0cesFu1mD7llLao+1cAhtvvZmGE3OnisTd0xOn118QMw74SRqddqaYvw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 14" + } + }, + "node_modules/algosdk": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-3.5.0.tgz", + "integrity": "sha512-9Q6lKAbl5Zz0VKjAMC7OYpnpDqmK/qa5LOALKxcF/jBs5k309lMezFC3ka0dSXPq64IKsUzhShSdOZrwYfr7tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "algorand-msgpack": "^1.1.0", + "hi-base32": "^0.5.1", + "js-sha256": "^0.9.0", + "js-sha3": "^0.8.0", + "js-sha512": "^0.8.0", + "json-bigint": "^1.0.0", + "tweetnacl": "^1.0.3", + "vlq": "^2.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/algosdk/node_modules/js-sha512": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", + "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -3481,6 +3580,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true, + "license": "MIT" + }, "node_modules/js-sha512": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", @@ -4973,6 +5086,13 @@ "license": "0BSD", "optional": true }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5282,6 +5402,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vlq": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", + "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 0059da599..b76f496eb 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -8,7 +8,8 @@ "algokit_abi", "algokit_transact", "algod_client", - "algokit_utils" + "algokit_utils", + "indexer_client" ], "scripts": { "build": "npm run build --workspaces --if-present", @@ -33,6 +34,7 @@ "@types/node": "^20.19.17", "@typescript-eslint/eslint-plugin": "^8.44.0", "@vitest/coverage-v8": "^3.2.4", + "algosdk": "^3.5.0", "better-npm-audit": "^3.11.0", "cpy-cli": "^6.0.0", "eslint": "^9.35.0", @@ -47,4 +49,4 @@ "vitest": "^3.2.4", "vitest-fetch-mock": "^0.4.5" } -} \ No newline at end of file +} diff --git a/tools/api_tools/Cargo.toml b/tools/api_tools/Cargo.toml index ccc383a4e..cec20bfd9 100644 --- a/tools/api_tools/Cargo.toml +++ b/tools/api_tools/Cargo.toml @@ -8,3 +8,4 @@ clap = { version = "4.5.40", features = ["derive"] } color-eyre = "0.6.5" duct = "1.0.0" shlex = "1.3.0" +once_cell = "1.19" diff --git a/tools/api_tools/src/main.rs b/tools/api_tools/src/main.rs index e3ed64e6e..68dd8b038 100644 --- a/tools/api_tools/src/main.rs +++ b/tools/api_tools/src/main.rs @@ -1,11 +1,24 @@ -use std::collections::HashMap; -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Output; +use std::{ + fs, + path::{Path, PathBuf}, +}; use clap::{Parser, Subcommand}; -use color_eyre::eyre::Result; +use color_eyre::eyre::{Result, eyre}; use duct::cmd; +use once_cell::sync::OnceCell; + +const DEFAULT_TS_PRESERVE: &[&str] = &[ + "__tests__", + "tests", + "eslint.config.mjs", + "package.json", + "README.md", + "rolldown.config.ts", + "tsconfig.json", + "tsconfig.build.json", + "tsconfig.test.json", +]; #[derive(Parser, Debug)] #[command(author, version, about = "API development tools", long_about = None)] @@ -41,6 +54,15 @@ enum Commands { /// Generate both algod and indexer API clients #[command(name = "generate-all")] GenerateAll, + /// Generate TypeScript algod client + #[command(name = "generate-ts-algod")] + GenerateTsAlgod, + /// Generate TypeScript indexer client + #[command(name = "generate-ts-indexer")] + GenerateTsIndexer, + /// Generate both TypeScript clients (algod and indexer) + #[command(name = "generate-ts-all")] + GenerateTsAll, /// Convert OpenAPI specifications (both algod and indexer) #[command(name = "convert-openapi")] ConvertOpenapi, @@ -52,36 +74,153 @@ enum Commands { ConvertIndexer, } -fn get_repo_root() -> PathBuf { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let repo_root = Path::new(manifest_dir) - .parent() // tools/ - .unwrap() - .parent() // repo root - .unwrap(); +fn repo_root() -> &'static Path { + static ROOT: OnceCell = OnceCell::new(); - PathBuf::from(repo_root) + ROOT.get_or_init(|| { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(|dir| dir.parent()) + .map(Path::to_path_buf) + .expect("invalid repository layout") + }) + .as_path() } -fn run( - command_str: &str, - dir: Option<&Path>, - env_vars: Option>, -) -> Result { - let parsed_command: Vec = shlex::Shlex::new(command_str).collect(); +fn run(command_str: &str, dir: Option<&Path>, env_vars: Option<&[(&str, &str)]>) -> Result<()> { + let mut tokens = shlex::Shlex::new(command_str); + let program = tokens + .next() + .ok_or_else(|| eyre!("command string must not be empty"))?; + let args: Vec<_> = tokens.collect(); + + let working_dir = dir + .map(|path| repo_root().join(path)) + .unwrap_or_else(|| repo_root().to_path_buf()); + + let mut expr = cmd(program, args).dir(&working_dir).stderr_to_stdout(); + + if let Some(vars) = env_vars { + expr = vars + .iter() + .fold(expr, |cmd, (key, value)| cmd.env(key, value)); + } + + expr.run()?; + Ok(()) +} - let working_dir = get_repo_root().join(dir.unwrap_or(Path::new(""))); - let mut command = cmd(&parsed_command[0], &parsed_command[1..]) - .dir(&working_dir) - .stderr_to_stdout(); +fn clean_ts_package_with_preserve(rel_dir: &str, preserve: &[&str]) -> Result<()> { + let pkg_dir = repo_root().join(rel_dir); + if !pkg_dir.exists() { + return Ok(()); + } - if let Some(env_vars) = env_vars { - for (key, value) in &env_vars { - command = command.env(key, value); + for entry in fs::read_dir(&pkg_dir)? { + let entry = entry?; + let name = entry.file_name(); + let name_str = name.to_string_lossy(); + if preserve.iter().any(|p| *p == name_str) { + continue; + } + let path = entry.path(); + if path.is_dir() { + fs::remove_dir_all(&path)?; + } else { + fs::remove_file(&path)?; } } + Ok(()) +} - Ok(command.run()?) +fn clean_ts_package(rel_dir: &str) -> Result<()> { + clean_ts_package_with_preserve(rel_dir, DEFAULT_TS_PRESERVE) +} + +#[derive(Clone, Copy)] +struct RsClientConfig { + spec: &'static str, + output_rel: &'static str, + package_name: &'static str, + description: &'static str, +} + +const ALGOD_RS_CLIENT: RsClientConfig = RsClientConfig { + spec: "algod", + output_rel: "crates/algod_client", + package_name: "algod_client", + description: "API client for algod interaction.", +}; + +const INDEXER_RS_CLIENT: RsClientConfig = RsClientConfig { + spec: "indexer", + output_rel: "crates/indexer_client", + package_name: "indexer_client", + description: "API client for indexer interaction.", +}; + +fn generate_rs_client(config: &RsClientConfig) -> Result<()> { + run( + &format!( + "uv run python -m rust_oas_generator.cli ../specs/{}.oas3.json --output ../../{}/ --package-name {} --description \"{}\"", + config.spec, config.output_rel, config.package_name, config.description + ), + Some(Path::new("api/oas_generator")), + None, + )?; + + run( + &format!( + "cargo fmt --manifest-path Cargo.toml -p {}", + config.package_name + ), + None, + None, + )?; + + Ok(()) +} + +#[derive(Clone, Copy)] +struct TsClientConfig { + spec: &'static str, + output_rel: &'static str, + package_name: &'static str, + description: &'static str, +} + +const ALGOD_TS_CLIENT: TsClientConfig = TsClientConfig { + spec: "algod", + output_rel: "packages/typescript/algod_client", + package_name: "algod_client", + description: "TypeScript client for algod interaction.", +}; + +const INDEXER_TS_CLIENT: TsClientConfig = TsClientConfig { + spec: "indexer", + output_rel: "packages/typescript/indexer_client", + package_name: "indexer_client", + description: "TypeScript client for indexer interaction.", +}; + +fn generate_ts_client(config: &TsClientConfig) -> Result<()> { + clean_ts_package(config.output_rel)?; + + let command = format!( + "uv run python -m ts_oas_generator.cli ../specs/{}.oas3.json --output ../../{}/ --package-name {} --description \"{}\" --verbose", + config.spec, config.output_rel, config.package_name, config.description + ); + run(&command, Some(Path::new("api/oas_generator")), None)?; + + run( + "npx --yes prettier --write src", + Some(Path::new(config.output_rel)), + None, + )?; + run("npm run build", Some(Path::new(config.output_rel)), None)?; + run("npm run test", Some(Path::new(config.output_rel)), None)?; + + Ok(()) } fn execute_command(command: &Commands) -> Result<()> { @@ -123,75 +262,38 @@ fn execute_command(command: &Commands) -> Result<()> { )?; } Commands::GenerateAlgod => { - // Generate the client - run( - "uv run python -m rust_oas_generator.cli ../specs/algod.oas3.json --output ../../crates/algod_client/ --package-name algod_client --description \"API client for algod interaction.\"", - Some(Path::new("api/oas_generator")), - None, - )?; - // Format the generated code - run( - "cargo fmt --manifest-path Cargo.toml -p algod_client", - None, - None, - )?; + generate_rs_client(&ALGOD_RS_CLIENT)?; } Commands::GenerateIndexer => { - // Generate the client - run( - "uv run python -m rust_oas_generator.cli ../specs/indexer.oas3.json --output ../../crates/indexer_client/ --package-name indexer_client --description \"API client for indexer interaction.\"", - Some(Path::new("api/oas_generator")), - None, - )?; - // Format the generated code - run( - "cargo fmt --manifest-path Cargo.toml -p indexer_client", - None, - None, - )?; + generate_rs_client(&INDEXER_RS_CLIENT)?; } Commands::GenerateAll => { - // Generate algod client - run( - "uv run python -m rust_oas_generator.cli ../specs/algod.oas3.json --output ../../crates/algod_client/ --package-name algod_client --description \"API client for algod interaction.\"", - Some(Path::new("api/oas_generator")), - None, - )?; - // Generate indexer client - run( - "uv run python -m rust_oas_generator.cli ../specs/indexer.oas3.json --output ../../crates/indexer_client/ --package-name indexer_client --description \"API client for indexer interaction.\"", - Some(Path::new("api/oas_generator")), - None, - )?; - // Format both generated codes - run( - "cargo fmt --manifest-path Cargo.toml -p algod_client", - None, - None, - )?; - run( - "cargo fmt --manifest-path Cargo.toml -p indexer_client", - None, - None, - )?; + generate_rs_client(&ALGOD_RS_CLIENT)?; + generate_rs_client(&INDEXER_RS_CLIENT)?; + } + Commands::GenerateTsAlgod => { + generate_ts_client(&ALGOD_TS_CLIENT)?; + } + Commands::GenerateTsIndexer => { + generate_ts_client(&INDEXER_TS_CLIENT)?; + } + Commands::GenerateTsAll => { + generate_ts_client(&ALGOD_TS_CLIENT)?; + generate_ts_client(&INDEXER_TS_CLIENT)?; } Commands::ConvertOpenapi => { - run( - "bun scripts/convert-openapi.ts", - Some(Path::new("api")), - None, - )?; + run("npm run convert-openapi", Some(Path::new("api")), None)?; } Commands::ConvertAlgod => { run( - "bun scripts/convert-openapi.ts --algod-only", + "npm run convert-openapi -- --algod-only", Some(Path::new("api")), None, )?; } Commands::ConvertIndexer => { run( - "bun scripts/convert-openapi.ts --indexer-only", + "npm run convert-openapi -- --indexer-only", Some(Path::new("api")), None, )?;