diff --git a/Cargo.toml b/Cargo.toml index b90e9fd..6e7f0b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,8 @@ members = [ "contracts/course/course_access", "contracts/user_profile", "contracts/test_contract", - "contracts/user_management" + "contracts/user_management", + "contracts/schema_export" ] [workspace.dependencies] diff --git a/contracts/schema_export/Cargo.toml b/contracts/schema_export/Cargo.toml new file mode 100644 index 0000000..226f013 --- /dev/null +++ b/contracts/schema_export/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "schema_export" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/contracts/schema_export/src/lib.rs b/contracts/schema_export/src/lib.rs new file mode 100644 index 0000000..398bed8 --- /dev/null +++ b/contracts/schema_export/src/lib.rs @@ -0,0 +1,30 @@ +#![no_std] + +use soroban_sdk::{contracttype, Env, String, Vec}; + +/// Helper module for exporting contract schemas +pub mod schema_export { + use super::*; + + /// Export contract metadata for frontend consumption + #[contracttype] + pub struct ContractMetadata { + pub name: String, + pub version: String, + pub methods: Vec, + } + + #[contracttype] + pub struct MethodInfo { + pub name: String, + pub params: Vec, + pub returns: String, + } + + #[contracttype] + pub struct ParamInfo { + pub name: String, + pub type_name: String, + pub required: bool, + } +} \ No newline at end of file diff --git a/schemas/contracts_metadata.json b/schemas/contracts_metadata.json new file mode 100644 index 0000000..7c57d37 --- /dev/null +++ b/schemas/contracts_metadata.json @@ -0,0 +1,23 @@ +{ + "contracts": { + "course_access": { + "wasm": "target/wasm32v1-none/release/course_access.wasm", + "schema": "schemas/course_access_schema.json", + "docs": "schemas/course_access_docs.md", + "typescript": "schemas/course_access-ts/" + }, + "course_registry": { + "wasm": "target/wasm32v1-none/release/course_registry.wasm", + "schema": "schemas/course_registry_schema.json", + "docs": "schemas/course_registry_docs.md", + "typescript": "schemas/course_registry-ts/" + }, + "user_management": { + "wasm": "target/wasm32v1-none/release/user_management.wasm", + "schema": "schemas/user_management_schema.json", + "docs": "schemas/user_management_docs.md", + "typescript": "schemas/user_management-ts/" + } + }, + "generated_at": "2025-10-03T10:48:29Z" +} diff --git a/schemas/course_access-ts/.gitignore b/schemas/course_access-ts/.gitignore new file mode 100644 index 0000000..72aae85 --- /dev/null +++ b/schemas/course_access-ts/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +out/ diff --git a/schemas/course_access-ts/README.md b/schemas/course_access-ts/README.md new file mode 100644 index 0000000..2002ab0 --- /dev/null +++ b/schemas/course_access-ts/README.md @@ -0,0 +1,54 @@ +# course_access-ts JS + +JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `course_access-ts` via Soroban RPC. + +This library was automatically generated by Soroban CLI using a command similar to: + +```bash +soroban contract bindings ts \ + --rpc-url INSERT_RPC_URL_HERE \ + --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \ + --contract-id INSERT_CONTRACT_ID_HERE \ + --output-dir ./path/to/course_access-ts +``` + +The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily. + +# To publish or not to publish + +This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command. + +But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path: + +```json +"dependencies": { + "course_access-ts": "./path/to/this/folder" +} +``` + +However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract. + +```json +"scripts": { + "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name course_access-ts" +} +``` + +Obviously you need to adjust the above command based on the actual command you used to generate the library. + +# Use it + +Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods: + +```js +import { Contract, networks } from "course_access-ts" + +const contract = new Contract({ + ...networks.futurenet, // for example; check which networks this library exports + rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers +}) + +contract.| +``` + +As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code. diff --git a/schemas/course_access-ts/package.json b/schemas/course_access-ts/package.json new file mode 100644 index 0000000..e7e9d1d --- /dev/null +++ b/schemas/course_access-ts/package.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.0", + "name": "course_access-ts", + "type": "module", + "exports": "./dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@stellar/stellar-sdk": "^14.1.1", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } +} diff --git a/schemas/course_access-ts/src/index.ts b/schemas/course_access-ts/src/index.ts new file mode 100644 index 0000000..07db9a4 --- /dev/null +++ b/schemas/course_access-ts/src/index.ts @@ -0,0 +1,874 @@ +import { Buffer } from "buffer"; +import { Address } from '@stellar/stellar-sdk'; +import { + AssembledTransaction, + Client as ContractClient, + ClientOptions as ContractClientOptions, + MethodOptions, + Result, + Spec as ContractSpec, +} from '@stellar/stellar-sdk/contract'; +import type { + u32, + i32, + u64, + i64, + u128, + i128, + u256, + i256, + Option, + Typepoint, + Duration, +} from '@stellar/stellar-sdk/contract'; +export * from '@stellar/stellar-sdk' +export * as contract from '@stellar/stellar-sdk/contract' +export * as rpc from '@stellar/stellar-sdk/rpc' + +if (typeof window !== 'undefined') { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || Buffer; +} + + + + +export const Errors = { + 1: {message:"UserAlreadyHasAccess"}, + 2: {message:"UserNoAccessCourse"}, + 3: {message:"Unauthorized"}, + 4: {message:"NameRequired"}, + 5: {message:"EmailRequired"}, + 6: {message:"CountryRequired"}, + 7: {message:"InvalidCourseId"}, + 8: {message:"InvalidUser"}, + 9: {message:"EmptyCourseId"}, + 10: {message:"InvalidTransferData"}, + 11: {message:"SameUserTransfer"}, + 12: {message:"Initialized"} +} + +/** + * Errors that can occur during contract versioning operations + */ +export const VersioningError = { + /** + * Invalid version format + */ + 1: {message:"InvalidVersion"}, + /** + * Version not found in history + */ + 2: {message:"VersionNotFound"}, + /** + * Migration not compatible + */ + 3: {message:"MigrationNotCompatible"}, + /** + * Migration already completed + */ + 4: {message:"MigrationAlreadyCompleted"}, + /** + * Unauthorized migration attempt + */ + 5: {message:"UnauthorizedMigration"}, + /** + * Migration failed + */ + 6: {message:"MigrationFailed"} +} + + +/** + * Represents access permission for a user to a specific course. + * + * This struct defines the relationship between a user and a course + * they have been granted access to. + */ +export interface CourseAccess { + /** + * The unique identifier of the course + */ +course_id: string; + /** + * The address of the user who has access + */ +user: string; +} + + +/** + * Contains all courses that a specific user has access to. + * + * This struct is used to efficiently query and return all courses + * accessible by a particular user. + */ +export interface UserCourses { + /** + * List of course IDs the user has access to + */ +courses: Array; + /** + * The address of the user + */ +user: string; +} + +/** + * Storage keys for different data types in the contract. + * + * This enum defines the various keys used to store and retrieve + * data from the contract's persistent storage. + */ +export type DataKey = {tag: "CourseAccess", values: readonly [string, string]} | {tag: "UserProfile", values: readonly [string]} | {tag: "UserCourses", values: readonly [string]} | {tag: "CourseUsers", values: readonly [string]}; + + +/** + * Represents a user's profile information. + * + * This struct contains all the personal and professional information + * that users can store on-chain as part of their profile. + */ +export interface UserProfile { + /** + * The user's country of residence + */ +country: string; + /** + * The user's email address + */ +email: string; + /** + * Optional learning goals or objectives + */ +goals: Option; + /** + * The user's full name + */ +name: string; + /** + * Optional profession or job title + */ +profession: Option; +} + + +/** + * Contains all users who have access to a specific course. + * + * This struct is used to efficiently query and return all users + * who have been granted access to a particular course. + */ +export interface CourseUsers { + /** + * The unique identifier of the course + */ +course: string; + /** + * List of user addresses who have access to the course + */ +users: Array; +} + +export interface Client { + /** + * Construct and simulate a initialize transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * One-time constructor to set owner and config addresses. + * + * Initializes the contract with the necessary external contract addresses. + * This function can only be called once during contract deployment. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `caller` - The address of the contract deployer/owner + * * `user_mgmt_addr` - Address of the user management contract + * * `course_registry_addr` - Address of the course registry contract + * + * # Panics + * + * * Fails if the contract has already been initialized + * * If any of the provided addresses are invalid + * + * # Examples + * + * ```rust + * // Initialize contract during deployment + * contract.initialize( + * env.clone(), + * deployer_address, + * user_mgmt_contract_address, + * course_registry_contract_address + * ); + * ``` + * + * # Edge Cases + * + * * **Double initialization**: Will panic if called more than once + * * **Invalid addresses**: Contract addresses must be valid + * * **Deployment only**: Should only be called during contract deployment + */ + initialize: ({caller, user_mgmt_addr, course_registry_addr}: {caller: string, user_mgmt_addr: string, course_registry_addr: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a grant_access transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Grant access to a specific user for a given course. + * + * Allows a user to access a specific course. Only authorized users + * (course creators or admins) can grant access. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course + * * `user` - The address of the user to grant access to + * + * # Panics + * + * * If course doesn't exist + * * If caller is not authorized (not course creator or admin) + * * If user already has access + * + * # Examples + * + * ```rust + * // Course creator granting access + * contract.grant_access( + * env.clone(), + * "course_123".try_into().unwrap(), + * student_address + * ); + * + * // Admin granting access + * contract.grant_access( + * env.clone(), + * "course_456".try_into().unwrap(), + * student_address + * ); + * ``` + * + * # Edge Cases + * + * * **Already has access**: Will panic if user already has access + * * **Non-existent course**: Will panic if course doesn't exist + * * **Permission denied**: Only course creators and admins can grant access + * * **User validation**: User address must be valid + */ + grant_access: ({course_id, user}: {course_id: string, user: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a revoke_access transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Revoke access for a specific user from a course. + * + * Removes a user's access to a specific course. Only authorized users + * (course creators or admins) can revoke access. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course + * * `user` - The address of the user to revoke access from + * + * # Returns + * + * Returns `true` if access was successfully revoked, `false` otherwise. + * + * # Panics + * + * * If course doesn't exist + * * If caller is not authorized (not course creator or admin) + * + * # Examples + * + * ```rust + * // Revoke access from a user + * let success = contract.revoke_access( + * env.clone(), + * "course_123".try_into().unwrap(), + * student_address + * ); + * + * if success { + * println!("Access revoked successfully"); + * } else { + * println!("User didn't have access"); + * } + * ``` + * + * # Edge Cases + * + * * **No access to revoke**: Returns `false` if user didn't have access + * * **Non-existent course**: Will panic if course doesn't exist + * * **Permission denied**: Only course creators and admins can revoke access + * * **Idempotent**: Safe to call multiple + */ + revoke_access: ({course_id, user}: {course_id: string, user: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a save_user_profile transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Save or update a user's profile on-chain. + * + * Stores user profile information in the contract storage. + * This includes personal and professional information. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `name` - The user's full name + * * `email` - The user's email address + * * `profession` - Optional profession/job title + * * `goals` - Optional learning goals or objectives + * * `country` - The user's country of residence + * + * # Panics + * + * * If name, email, or country are empty + * * If email format is invalid + * + * # Examples + * + * ```rust + * // Save user profile + * contract.save_user_profile( + * env.clone(), + * "John Doe".try_into().unwrap(), + * "john@example.com".try_into().unwrap(), + * Some("Software Developer".try_into().unwrap()), + * Some("Learn Rust programming".try_into().unwrap()), + * "US".try_into().unwrap() + * ); + * + * // Save minimal profile + * contract.save_user_profile( + * env.clone(), + * "Jane Smith".try_into().unwrap(), + * "jane@example.com".try_into().unwrap(), + * None, + * None, + * "CA".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Empty required fields**: Name, email, and coun + */ + save_user_profile: ({name, email, profession, goals, country}: {name: string, email: string, profession: Option, goals: Option, country: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a list_user_courses transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * List all courses a user has access to. + * + * Retrieves all courses that the specified user is enrolled in + * or has been granted access to. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `user` - The address of the user to query + * + * # Returns + * + * Returns a `UserCourses` struct containing the list of accessible courses. + * + * # Examples + * + * ```rust + * // Get user's accessible courses + * let user_courses = contract.list_user_courses(env.clone(), user_address); + * + * for course_id in user_courses.course_ids { + * println!("User has access to course: {}", course_id); + * } + * ``` + * + * # Edge Cases + * + * * **No access**: Returns empty list if user has no course access + * * **Non-existent user**: Returns empty list for non-existent users + * * **Public access**: Anyone can query user courses + * * **Revoked courses**: Only includes currently accessible courses + */ + list_user_courses: ({user}: {user: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a list_course_access transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * List all users who have access to a course. + * + * Retrieves all users who have been granted access to the specified course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course + * + * # Returns + * + * Returns a `CourseUsers` struct containing the list of users with access. + * + * # Examples + * + * ```rust + * // Get all users with access to a course + * let course_users = contract.list_course_access(env.clone(), "course_123".try_into().unwrap()); + * + * for user in course_users.users { + * println!("User with access: {}", user); + * } + * ``` + * + * # Edge Cases + * + * * **No users**: Returns empty list if no users have access + * * **Non-existent course**: Returns empty list for non-existent courses + * * **Public access**: Anyone can query course access + * * **Real-time data**: Always returns current access status + */ + list_course_access: ({course_id}: {course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a revoke_all_access transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Revoke all user access for a course. + * + * Removes access for all users from the specified course. + * Only admin or course creator is allowed to perform this operation. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `user` - The address of the user requesting the operation + * * `course_id` - The unique identifier of the course + * + * # Returns + * + * Returns the number of users affected by the revocation and emits an event. + * + * # Panics + * + * * If course doesn't exist + * * If caller is not authorized (not course creator or admin) + * + * # Examples + * + * ```rust + * // Revoke all access for a course + * let affected_users = contract.revoke_all_access( + * env.clone(), + * admin_address, + * "course_123".try_into().unwrap() + * ); + * + * println!("Revoked access for {} users", affected_users); + * ``` + * + * # Edge Cases + * + * * **No users**: Returns 0 if no users had access + * * **Non-existent course**: Will panic if course doesn't exist + * * **Permission denied**: Only course creators and admins can perform this + * * **Bulk operation**: Efficiently removes all access in one transaction + */ + revoke_all_access: ({user, course_id}: {user: string, course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a set_config transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Configure external contract addresses used for auth checks. + * + * Updates the addresses of external contracts that this contract + * depends on for authentication and authorization checks. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `caller` - The address of the user making the configuration change + * * `user_mgmt_addr` - Address of the user management contract + * * `course_registry_addr` - Address of the course registry contract + * + * # Panics + * + * * If caller is not the contract owner + * * If any of the provided addresses are invalid + * + * # Storage + * + * Stores the addresses in instance storage keys: ("user_mgmt_addr",) and ("course_registry_addr",) + * + * # Examples + * + * ```rust + * // Update contract addresses + * contract.set_config( + * env.clone(), + * contract_owner_address, + * new_user_mgmt_address, + * new_course_registry_address + * ); + * ``` + * + * # Edge Cases + * + * * **Owner only**: Only contract owner can update addresses + * * **Invalid addresses**: Will panic if addresses are invalid + * * **Runtime updates**: Can be called after contract deployment + * * **Immediate effect**: Change + */ + set_config: ({caller, user_mgmt_addr, course_registry_addr}: {caller: string, user_mgmt_addr: string, course_registry_addr: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_contract_version transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get the current contract version + * + * Returns the semantic version of the current contract deployment. + * This is useful for tracking contract upgrades and compatibility. + * + * # Arguments + * * `_env` - The Soroban environment (unused) + * + * # Returns + * * `String` - The current contract version + */ + get_contract_version: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_version_history transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get contract version history + * + * Returns a list of all versions that have been deployed for this contract. + * This helps track the evolution of the contract over time. + * + * # Arguments + * * `env` - The Soroban environment + * + * # Returns + * * `Vec` - Vector of version strings in chronological order + */ + get_version_history: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a is_version_compatible transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Check compatibility between contract versions + * + * Determines if data from one version can be safely used with another version. + * This is crucial for migration processes and backward compatibility. + * + * # Arguments + * * `env` - The Soroban environment + * * `from_version` - The source version to check compatibility from + * * `to_version` - The target version to check compatibility to + * + * # Returns + * * `bool` - True if the versions are compatible, false otherwise + */ + is_version_compatible: ({from_version, to_version}: {from_version: string, to_version: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a migrate_access_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Migrate access data between contract versions + * + * Performs data migration from one contract version to another. + * This function handles the transformation of course access data structures + * when upgrading contract versions. + * + * # Arguments + * * `env` - The Soroban environment + * * `caller` - The address performing the migration (must be admin) + * * `from_version` - The source version to migrate from + * * `to_version` - The target version to migrate to + * + * # Returns + * * `bool` - True if migration was successful, false otherwise + * + * # Events + * Emits a migration event upon successful completion + */ + migrate_access_data: ({caller, from_version, to_version}: {caller: string, from_version: string, to_version: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_migration_status transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get migration status for the current contract + * + * Returns information about the current migration status and any + * pending migrations that need to be completed. + * + * # Arguments + * * `env` - The Soroban environment + * + * # Returns + * * `String` - Migration status information + */ + get_migration_status: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a transfer_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + */ + transfer_course: ({course_id, from, to}: {course_id: string, from: string, to: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + +} +export class Client extends ContractClient { + static async deploy( + /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ + options: MethodOptions & + Omit & { + /** The hash of the Wasm blob, which must already be installed on-chain. */ + wasmHash: Buffer | string; + /** Salt used to generate the contract's ID. Passed through to {@link Operation.createCustomContract}. Default: random. */ + salt?: Buffer | Uint8Array; + /** The format used to decode `wasmHash`, if it's provided as a string. */ + format?: "hex" | "base64"; + } + ): Promise> { + return ContractClient.deploy(null, options) + } + constructor(public readonly options: ContractClientOptions) { + super( + new ContractSpec([ "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAADAAAAAAAAAAUVXNlckFscmVhZHlIYXNBY2Nlc3MAAAABAAAAAAAAABJVc2VyTm9BY2Nlc3NDb3Vyc2UAAAAAAAIAAAAAAAAADFVuYXV0aG9yaXplZAAAAAMAAAAAAAAADE5hbWVSZXF1aXJlZAAAAAQAAAAAAAAADUVtYWlsUmVxdWlyZWQAAAAAAAAFAAAAAAAAAA9Db3VudHJ5UmVxdWlyZWQAAAAABgAAAAAAAAAPSW52YWxpZENvdXJzZUlkAAAAAAcAAAAAAAAAC0ludmFsaWRVc2VyAAAAAAgAAAAAAAAADUVtcHR5Q291cnNlSWQAAAAAAAAJAAAAAAAAABNJbnZhbGlkVHJhbnNmZXJEYXRhAAAAAAoAAAAAAAAAEFNhbWVVc2VyVHJhbnNmZXIAAAALAAAAAAAAAAtJbml0aWFsaXplZAAAAAAM", + "AAAABAAAADtFcnJvcnMgdGhhdCBjYW4gb2NjdXIgZHVyaW5nIGNvbnRyYWN0IHZlcnNpb25pbmcgb3BlcmF0aW9ucwAAAAAAAAAAD1ZlcnNpb25pbmdFcnJvcgAAAAAGAAAAFkludmFsaWQgdmVyc2lvbiBmb3JtYXQAAAAAAA5JbnZhbGlkVmVyc2lvbgAAAAAAAQAAABxWZXJzaW9uIG5vdCBmb3VuZCBpbiBoaXN0b3J5AAAAD1ZlcnNpb25Ob3RGb3VuZAAAAAACAAAAGE1pZ3JhdGlvbiBub3QgY29tcGF0aWJsZQAAABZNaWdyYXRpb25Ob3RDb21wYXRpYmxlAAAAAAADAAAAG01pZ3JhdGlvbiBhbHJlYWR5IGNvbXBsZXRlZAAAAAAZTWlncmF0aW9uQWxyZWFkeUNvbXBsZXRlZAAAAAAAAAQAAAAeVW5hdXRob3JpemVkIG1pZ3JhdGlvbiBhdHRlbXB0AAAAAAAVVW5hdXRob3JpemVkTWlncmF0aW9uAAAAAAAABQAAABBNaWdyYXRpb24gZmFpbGVkAAAAD01pZ3JhdGlvbkZhaWxlZAAAAAAG", + "AAAAAQAAAKFSZXByZXNlbnRzIGFjY2VzcyBwZXJtaXNzaW9uIGZvciBhIHVzZXIgdG8gYSBzcGVjaWZpYyBjb3Vyc2UuCgpUaGlzIHN0cnVjdCBkZWZpbmVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIHVzZXIgYW5kIGEgY291cnNlCnRoZXkgaGF2ZSBiZWVuIGdyYW50ZWQgYWNjZXNzIHRvLgAAAAAAAAAAAAAMQ291cnNlQWNjZXNzAAAAAgAAACNUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQAAAAAJY291cnNlX2lkAAAAAAAAEAAAACZUaGUgYWRkcmVzcyBvZiB0aGUgdXNlciB3aG8gaGFzIGFjY2VzcwAAAAAABHVzZXIAAAAT", + "AAAAAQAAAJpDb250YWlucyBhbGwgY291cnNlcyB0aGF0IGEgc3BlY2lmaWMgdXNlciBoYXMgYWNjZXNzIHRvLgoKVGhpcyBzdHJ1Y3QgaXMgdXNlZCB0byBlZmZpY2llbnRseSBxdWVyeSBhbmQgcmV0dXJuIGFsbCBjb3Vyc2VzCmFjY2Vzc2libGUgYnkgYSBwYXJ0aWN1bGFyIHVzZXIuAAAAAAAAAAAAC1VzZXJDb3Vyc2VzAAAAAAIAAAApTGlzdCBvZiBjb3Vyc2UgSURzIHRoZSB1c2VyIGhhcyBhY2Nlc3MgdG8AAAAAAAAHY291cnNlcwAAAAPqAAAAEAAAABdUaGUgYWRkcmVzcyBvZiB0aGUgdXNlcgAAAAAEdXNlcgAAABM=", + "AAAAAgAAAKJTdG9yYWdlIGtleXMgZm9yIGRpZmZlcmVudCBkYXRhIHR5cGVzIGluIHRoZSBjb250cmFjdC4KClRoaXMgZW51bSBkZWZpbmVzIHRoZSB2YXJpb3VzIGtleXMgdXNlZCB0byBzdG9yZSBhbmQgcmV0cmlldmUKZGF0YSBmcm9tIHRoZSBjb250cmFjdCdzIHBlcnNpc3RlbnQgc3RvcmFnZS4AAAAAAAAAAAAHRGF0YUtleQAAAAAEAAAAAQAAAEBLZXkgZm9yIHN0b3JpbmcgY291cnNlIGFjY2VzczogKGNvdXJzZV9pZCwgdXNlcikgLT4gQ291cnNlQWNjZXNzAAAADENvdXJzZUFjY2VzcwAAAAIAAAAQAAAAEwAAAAEAAAAxS2V5IGZvciBzdG9yaW5nIHVzZXIgcHJvZmlsZTogdXNlciAtPiBVc2VyUHJvZmlsZQAAAAAAAAtVc2VyUHJvZmlsZQAAAAABAAAAEwAAAAEAAAA1S2V5IGZvciBzdG9yaW5nIGNvdXJzZXMgcGVyIHVzZXI6IHVzZXIgLT4gVXNlckNvdXJzZXMAAAAAAAALVXNlckNvdXJzZXMAAAAAAQAAABMAAAABAAAAOktleSBmb3Igc3RvcmluZyB1c2VycyBwZXIgY291cnNlOiBjb3Vyc2VfaWQgLT4gQ291cnNlVXNlcnMAAAAAAAtDb3Vyc2VVc2VycwAAAAABAAAAEA==", + "AAAAAQAAAKRSZXByZXNlbnRzIGEgdXNlcidzIHByb2ZpbGUgaW5mb3JtYXRpb24uCgpUaGlzIHN0cnVjdCBjb250YWlucyBhbGwgdGhlIHBlcnNvbmFsIGFuZCBwcm9mZXNzaW9uYWwgaW5mb3JtYXRpb24KdGhhdCB1c2VycyBjYW4gc3RvcmUgb24tY2hhaW4gYXMgcGFydCBvZiB0aGVpciBwcm9maWxlLgAAAAAAAAALVXNlclByb2ZpbGUAAAAABQAAAB9UaGUgdXNlcidzIGNvdW50cnkgb2YgcmVzaWRlbmNlAAAAAAdjb3VudHJ5AAAAABAAAAAYVGhlIHVzZXIncyBlbWFpbCBhZGRyZXNzAAAABWVtYWlsAAAAAAAAEAAAACVPcHRpb25hbCBsZWFybmluZyBnb2FscyBvciBvYmplY3RpdmVzAAAAAAAABWdvYWxzAAAAAAAD6AAAABAAAAAUVGhlIHVzZXIncyBmdWxsIG5hbWUAAAAEbmFtZQAAABAAAAAgT3B0aW9uYWwgcHJvZmVzc2lvbiBvciBqb2IgdGl0bGUAAAAKcHJvZmVzc2lvbgAAAAAD6AAAABA=", + "AAAAAQAAAKxDb250YWlucyBhbGwgdXNlcnMgd2hvIGhhdmUgYWNjZXNzIHRvIGEgc3BlY2lmaWMgY291cnNlLgoKVGhpcyBzdHJ1Y3QgaXMgdXNlZCB0byBlZmZpY2llbnRseSBxdWVyeSBhbmQgcmV0dXJuIGFsbCB1c2Vycwp3aG8gaGF2ZSBiZWVuIGdyYW50ZWQgYWNjZXNzIHRvIGEgcGFydGljdWxhciBjb3Vyc2UuAAAAAAAAAAtDb3Vyc2VVc2VycwAAAAACAAAAI1RoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgY291cnNlAAAAAAZjb3Vyc2UAAAAAABAAAAA0TGlzdCBvZiB1c2VyIGFkZHJlc3NlcyB3aG8gaGF2ZSBhY2Nlc3MgdG8gdGhlIGNvdXJzZQAAAAV1c2VycwAAAAAAA+oAAAAT", + "AAAAAAAAA6JPbmUtdGltZSBjb25zdHJ1Y3RvciB0byBzZXQgb3duZXIgYW5kIGNvbmZpZyBhZGRyZXNzZXMuCgpJbml0aWFsaXplcyB0aGUgY29udHJhY3Qgd2l0aCB0aGUgbmVjZXNzYXJ5IGV4dGVybmFsIGNvbnRyYWN0IGFkZHJlc3Nlcy4KVGhpcyBmdW5jdGlvbiBjYW4gb25seSBiZSBjYWxsZWQgb25jZSBkdXJpbmcgY29udHJhY3QgZGVwbG95bWVudC4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2FsbGVyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSBjb250cmFjdCBkZXBsb3llci9vd25lcgoqIGB1c2VyX21nbXRfYWRkcmAgLSBBZGRyZXNzIG9mIHRoZSB1c2VyIG1hbmFnZW1lbnQgY29udHJhY3QKKiBgY291cnNlX3JlZ2lzdHJ5X2FkZHJgIC0gQWRkcmVzcyBvZiB0aGUgY291cnNlIHJlZ2lzdHJ5IGNvbnRyYWN0CgojIFBhbmljcwoKKiBGYWlscyBpZiB0aGUgY29udHJhY3QgaGFzIGFscmVhZHkgYmVlbiBpbml0aWFsaXplZAoqIElmIGFueSBvZiB0aGUgcHJvdmlkZWQgYWRkcmVzc2VzIGFyZSBpbnZhbGlkCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIEluaXRpYWxpemUgY29udHJhY3QgZHVyaW5nIGRlcGxveW1lbnQKY29udHJhY3QuaW5pdGlhbGl6ZSgKZW52LmNsb25lKCksCmRlcGxveWVyX2FkZHJlc3MsCnVzZXJfbWdtdF9jb250cmFjdF9hZGRyZXNzLApjb3Vyc2VfcmVnaXN0cnlfY29udHJhY3RfYWRkcmVzcwopOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKkRvdWJsZSBpbml0aWFsaXphdGlvbioqOiBXaWxsIHBhbmljIGlmIGNhbGxlZCBtb3JlIHRoYW4gb25jZQoqICoqSW52YWxpZCBhZGRyZXNzZXMqKjogQ29udHJhY3QgYWRkcmVzc2VzIG11c3QgYmUgdmFsaWQKKiAqKkRlcGxveW1lbnQgb25seSoqOiBTaG91bGQgb25seSBiZSBjYWxsZWQgZHVyaW5nIGNvbnRyYWN0IGRlcGxveW1lbnQAAAAAAAppbml0aWFsaXplAAAAAAADAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAADnVzZXJfbWdtdF9hZGRyAAAAAAATAAAAAAAAABRjb3Vyc2VfcmVnaXN0cnlfYWRkcgAAABMAAAAA", + "AAAAAAAAA8tHcmFudCBhY2Nlc3MgdG8gYSBzcGVjaWZpYyB1c2VyIGZvciBhIGdpdmVuIGNvdXJzZS4KCkFsbG93cyBhIHVzZXIgdG8gYWNjZXNzIGEgc3BlY2lmaWMgY291cnNlLiBPbmx5IGF1dGhvcml6ZWQgdXNlcnMKKGNvdXJzZSBjcmVhdG9ycyBvciBhZG1pbnMpIGNhbiBncmFudCBhY2Nlc3MuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQoqIGB1c2VyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIHRvIGdyYW50IGFjY2VzcyB0bwoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBjYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgKG5vdCBjb3Vyc2UgY3JlYXRvciBvciBhZG1pbikKKiBJZiB1c2VyIGFscmVhZHkgaGFzIGFjY2VzcwoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBDb3Vyc2UgY3JlYXRvciBncmFudGluZyBhY2Nlc3MKY29udHJhY3QuZ3JhbnRfYWNjZXNzKAplbnYuY2xvbmUoKSwKImNvdXJzZV8xMjMiLnRyeV9pbnRvKCkudW53cmFwKCksCnN0dWRlbnRfYWRkcmVzcwopOwoKLy8gQWRtaW4gZ3JhbnRpbmcgYWNjZXNzCmNvbnRyYWN0LmdyYW50X2FjY2VzcygKZW52LmNsb25lKCksCiJjb3Vyc2VfNDU2Ii50cnlfaW50bygpLnVud3JhcCgpLApzdHVkZW50X2FkZHJlc3MKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipBbHJlYWR5IGhhcyBhY2Nlc3MqKjogV2lsbCBwYW5pYyBpZiB1c2VyIGFscmVhZHkgaGFzIGFjY2VzcwoqICoqTm9uLWV4aXN0ZW50IGNvdXJzZSoqOiBXaWxsIHBhbmljIGlmIGNvdXJzZSBkb2Vzbid0IGV4aXN0CiogKipQZXJtaXNzaW9uIGRlbmllZCoqOiBPbmx5IGNvdXJzZSBjcmVhdG9ycyBhbmQgYWRtaW5zIGNhbiBncmFudCBhY2Nlc3MKKiAqKlVzZXIgdmFsaWRhdGlvbioqOiBVc2VyIGFkZHJlc3MgbXVzdCBiZSB2YWxpZAAAAAAMZ3JhbnRfYWNjZXNzAAAAAgAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAEdXNlcgAAABMAAAAA", + "AAAAAAAABABSZXZva2UgYWNjZXNzIGZvciBhIHNwZWNpZmljIHVzZXIgZnJvbSBhIGNvdXJzZS4KClJlbW92ZXMgYSB1c2VyJ3MgYWNjZXNzIHRvIGEgc3BlY2lmaWMgY291cnNlLiBPbmx5IGF1dGhvcml6ZWQgdXNlcnMKKGNvdXJzZSBjcmVhdG9ycyBvciBhZG1pbnMpIGNhbiByZXZva2UgYWNjZXNzLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjb3Vyc2VfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBjb3Vyc2UKKiBgdXNlcmAgLSBUaGUgYWRkcmVzcyBvZiB0aGUgdXNlciB0byByZXZva2UgYWNjZXNzIGZyb20KCiMgUmV0dXJucwoKUmV0dXJucyBgdHJ1ZWAgaWYgYWNjZXNzIHdhcyBzdWNjZXNzZnVsbHkgcmV2b2tlZCwgYGZhbHNlYCBvdGhlcndpc2UuCgojIFBhbmljcwoKKiBJZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqIElmIGNhbGxlciBpcyBub3QgYXV0aG9yaXplZCAobm90IGNvdXJzZSBjcmVhdG9yIG9yIGFkbWluKQoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBSZXZva2UgYWNjZXNzIGZyb20gYSB1c2VyCmxldCBzdWNjZXNzID0gY29udHJhY3QucmV2b2tlX2FjY2VzcygKZW52LmNsb25lKCksCiJjb3Vyc2VfMTIzIi50cnlfaW50bygpLnVud3JhcCgpLApzdHVkZW50X2FkZHJlc3MKKTsKCmlmIHN1Y2Nlc3MgewpwcmludGxuISgiQWNjZXNzIHJldm9rZWQgc3VjY2Vzc2Z1bGx5Iik7Cn0gZWxzZSB7CnByaW50bG4hKCJVc2VyIGRpZG4ndCBoYXZlIGFjY2VzcyIpOwp9CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqTm8gYWNjZXNzIHRvIHJldm9rZSoqOiBSZXR1cm5zIGBmYWxzZWAgaWYgdXNlciBkaWRuJ3QgaGF2ZSBhY2Nlc3MKKiAqKk5vbi1leGlzdGVudCBjb3Vyc2UqKjogV2lsbCBwYW5pYyBpZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqICoqUGVybWlzc2lvbiBkZW5pZWQqKjogT25seSBjb3Vyc2UgY3JlYXRvcnMgYW5kIGFkbWlucyBjYW4gcmV2b2tlIGFjY2VzcwoqICoqSWRlbXBvdGVudCoqOiBTYWZlIHRvIGNhbGwgbXVsdGlwbGUgAAAADXJldm9rZV9hY2Nlc3MAAAAAAAACAAAAAAAAAAljb3Vyc2VfaWQAAAAAAAAQAAAAAAAAAAR1c2VyAAAAEwAAAAEAAAAB", + "AAAAAAAABABTYXZlIG9yIHVwZGF0ZSBhIHVzZXIncyBwcm9maWxlIG9uLWNoYWluLgoKU3RvcmVzIHVzZXIgcHJvZmlsZSBpbmZvcm1hdGlvbiBpbiB0aGUgY29udHJhY3Qgc3RvcmFnZS4KVGhpcyBpbmNsdWRlcyBwZXJzb25hbCBhbmQgcHJvZmVzc2lvbmFsIGluZm9ybWF0aW9uLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBuYW1lYCAtIFRoZSB1c2VyJ3MgZnVsbCBuYW1lCiogYGVtYWlsYCAtIFRoZSB1c2VyJ3MgZW1haWwgYWRkcmVzcwoqIGBwcm9mZXNzaW9uYCAtIE9wdGlvbmFsIHByb2Zlc3Npb24vam9iIHRpdGxlCiogYGdvYWxzYCAtIE9wdGlvbmFsIGxlYXJuaW5nIGdvYWxzIG9yIG9iamVjdGl2ZXMKKiBgY291bnRyeWAgLSBUaGUgdXNlcidzIGNvdW50cnkgb2YgcmVzaWRlbmNlCgojIFBhbmljcwoKKiBJZiBuYW1lLCBlbWFpbCwgb3IgY291bnRyeSBhcmUgZW1wdHkKKiBJZiBlbWFpbCBmb3JtYXQgaXMgaW52YWxpZAoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBTYXZlIHVzZXIgcHJvZmlsZQpjb250cmFjdC5zYXZlX3VzZXJfcHJvZmlsZSgKZW52LmNsb25lKCksCiJKb2huIERvZSIudHJ5X2ludG8oKS51bndyYXAoKSwKImpvaG5AZXhhbXBsZS5jb20iLnRyeV9pbnRvKCkudW53cmFwKCksClNvbWUoIlNvZnR3YXJlIERldmVsb3BlciIudHJ5X2ludG8oKS51bndyYXAoKSksClNvbWUoIkxlYXJuIFJ1c3QgcHJvZ3JhbW1pbmciLnRyeV9pbnRvKCkudW53cmFwKCkpLAoiVVMiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKCi8vIFNhdmUgbWluaW1hbCBwcm9maWxlCmNvbnRyYWN0LnNhdmVfdXNlcl9wcm9maWxlKAplbnYuY2xvbmUoKSwKIkphbmUgU21pdGgiLnRyeV9pbnRvKCkudW53cmFwKCksCiJqYW5lQGV4YW1wbGUuY29tIi50cnlfaW50bygpLnVud3JhcCgpLApOb25lLApOb25lLAoiQ0EiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipFbXB0eSByZXF1aXJlZCBmaWVsZHMqKjogTmFtZSwgZW1haWwsIGFuZCBjb3VuAAAAEXNhdmVfdXNlcl9wcm9maWxlAAAAAAAABQAAAAAAAAAEbmFtZQAAABAAAAAAAAAABWVtYWlsAAAAAAAAEAAAAAAAAAAKcHJvZmVzc2lvbgAAAAAD6AAAABAAAAAAAAAABWdvYWxzAAAAAAAD6AAAABAAAAAAAAAAB2NvdW50cnkAAAAAEAAAAAA=", + "AAAAAAAAAyVMaXN0IGFsbCBjb3Vyc2VzIGEgdXNlciBoYXMgYWNjZXNzIHRvLgoKUmV0cmlldmVzIGFsbCBjb3Vyc2VzIHRoYXQgdGhlIHNwZWNpZmllZCB1c2VyIGlzIGVucm9sbGVkIGluCm9yIGhhcyBiZWVuIGdyYW50ZWQgYWNjZXNzIHRvLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGB1c2VyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIHRvIHF1ZXJ5CgojIFJldHVybnMKClJldHVybnMgYSBgVXNlckNvdXJzZXNgIHN0cnVjdCBjb250YWluaW5nIHRoZSBsaXN0IG9mIGFjY2Vzc2libGUgY291cnNlcy4KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gR2V0IHVzZXIncyBhY2Nlc3NpYmxlIGNvdXJzZXMKbGV0IHVzZXJfY291cnNlcyA9IGNvbnRyYWN0Lmxpc3RfdXNlcl9jb3Vyc2VzKGVudi5jbG9uZSgpLCB1c2VyX2FkZHJlc3MpOwoKZm9yIGNvdXJzZV9pZCBpbiB1c2VyX2NvdXJzZXMuY291cnNlX2lkcyB7CnByaW50bG4hKCJVc2VyIGhhcyBhY2Nlc3MgdG8gY291cnNlOiB7fSIsIGNvdXJzZV9pZCk7Cn0KYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipObyBhY2Nlc3MqKjogUmV0dXJucyBlbXB0eSBsaXN0IGlmIHVzZXIgaGFzIG5vIGNvdXJzZSBhY2Nlc3MKKiAqKk5vbi1leGlzdGVudCB1c2VyKio6IFJldHVybnMgZW1wdHkgbGlzdCBmb3Igbm9uLWV4aXN0ZW50IHVzZXJzCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gcXVlcnkgdXNlciBjb3Vyc2VzCiogKipSZXZva2VkIGNvdXJzZXMqKjogT25seSBpbmNsdWRlcyBjdXJyZW50bHkgYWNjZXNzaWJsZSBjb3Vyc2VzAAAAAAAAEWxpc3RfdXNlcl9jb3Vyc2VzAAAAAAAAAQAAAAAAAAAEdXNlcgAAABMAAAABAAAH0AAAAAtVc2VyQ291cnNlcwA=", + "AAAAAAAAAxxMaXN0IGFsbCB1c2VycyB3aG8gaGF2ZSBhY2Nlc3MgdG8gYSBjb3Vyc2UuCgpSZXRyaWV2ZXMgYWxsIHVzZXJzIHdobyBoYXZlIGJlZW4gZ3JhbnRlZCBhY2Nlc3MgdG8gdGhlIHNwZWNpZmllZCBjb3Vyc2UuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQoKIyBSZXR1cm5zCgpSZXR1cm5zIGEgYENvdXJzZVVzZXJzYCBzdHJ1Y3QgY29udGFpbmluZyB0aGUgbGlzdCBvZiB1c2VycyB3aXRoIGFjY2Vzcy4KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gR2V0IGFsbCB1c2VycyB3aXRoIGFjY2VzcyB0byBhIGNvdXJzZQpsZXQgY291cnNlX3VzZXJzID0gY29udHJhY3QubGlzdF9jb3Vyc2VfYWNjZXNzKGVudi5jbG9uZSgpLCAiY291cnNlXzEyMyIudHJ5X2ludG8oKS51bndyYXAoKSk7Cgpmb3IgdXNlciBpbiBjb3Vyc2VfdXNlcnMudXNlcnMgewpwcmludGxuISgiVXNlciB3aXRoIGFjY2Vzczoge30iLCB1c2VyKTsKfQpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKk5vIHVzZXJzKio6IFJldHVybnMgZW1wdHkgbGlzdCBpZiBubyB1c2VycyBoYXZlIGFjY2VzcwoqICoqTm9uLWV4aXN0ZW50IGNvdXJzZSoqOiBSZXR1cm5zIGVtcHR5IGxpc3QgZm9yIG5vbi1leGlzdGVudCBjb3Vyc2VzCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gcXVlcnkgY291cnNlIGFjY2VzcwoqICoqUmVhbC10aW1lIGRhdGEqKjogQWx3YXlzIHJldHVybnMgY3VycmVudCBhY2Nlc3Mgc3RhdHVzAAAAEmxpc3RfY291cnNlX2FjY2VzcwAAAAAAAQAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAEAAAfQAAAAC0NvdXJzZVVzZXJzAA==", + "AAAAAAAAA+5SZXZva2UgYWxsIHVzZXIgYWNjZXNzIGZvciBhIGNvdXJzZS4KClJlbW92ZXMgYWNjZXNzIGZvciBhbGwgdXNlcnMgZnJvbSB0aGUgc3BlY2lmaWVkIGNvdXJzZS4KT25seSBhZG1pbiBvciBjb3Vyc2UgY3JlYXRvciBpcyBhbGxvd2VkIHRvIHBlcmZvcm0gdGhpcyBvcGVyYXRpb24uCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYHVzZXJgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIHVzZXIgcmVxdWVzdGluZyB0aGUgb3BlcmF0aW9uCiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQoKIyBSZXR1cm5zCgpSZXR1cm5zIHRoZSBudW1iZXIgb2YgdXNlcnMgYWZmZWN0ZWQgYnkgdGhlIHJldm9jYXRpb24gYW5kIGVtaXRzIGFuIGV2ZW50LgoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBjYWxsZXIgaXMgbm90IGF1dGhvcml6ZWQgKG5vdCBjb3Vyc2UgY3JlYXRvciBvciBhZG1pbikKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gUmV2b2tlIGFsbCBhY2Nlc3MgZm9yIGEgY291cnNlCmxldCBhZmZlY3RlZF91c2VycyA9IGNvbnRyYWN0LnJldm9rZV9hbGxfYWNjZXNzKAplbnYuY2xvbmUoKSwKYWRtaW5fYWRkcmVzcywKImNvdXJzZV8xMjMiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKCnByaW50bG4hKCJSZXZva2VkIGFjY2VzcyBmb3Ige30gdXNlcnMiLCBhZmZlY3RlZF91c2Vycyk7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqTm8gdXNlcnMqKjogUmV0dXJucyAwIGlmIG5vIHVzZXJzIGhhZCBhY2Nlc3MKKiAqKk5vbi1leGlzdGVudCBjb3Vyc2UqKjogV2lsbCBwYW5pYyBpZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqICoqUGVybWlzc2lvbiBkZW5pZWQqKjogT25seSBjb3Vyc2UgY3JlYXRvcnMgYW5kIGFkbWlucyBjYW4gcGVyZm9ybSB0aGlzCiogKipCdWxrIG9wZXJhdGlvbioqOiBFZmZpY2llbnRseSByZW1vdmVzIGFsbCBhY2Nlc3MgaW4gb25lIHRyYW5zYWN0aW9uAAAAAAARcmV2b2tlX2FsbF9hY2Nlc3MAAAAAAAACAAAAAAAAAAR1c2VyAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAEAAAAE", + "AAAAAAAABABDb25maWd1cmUgZXh0ZXJuYWwgY29udHJhY3QgYWRkcmVzc2VzIHVzZWQgZm9yIGF1dGggY2hlY2tzLgoKVXBkYXRlcyB0aGUgYWRkcmVzc2VzIG9mIGV4dGVybmFsIGNvbnRyYWN0cyB0aGF0IHRoaXMgY29udHJhY3QKZGVwZW5kcyBvbiBmb3IgYXV0aGVudGljYXRpb24gYW5kIGF1dGhvcml6YXRpb24gY2hlY2tzLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIHVzZXIgbWFraW5nIHRoZSBjb25maWd1cmF0aW9uIGNoYW5nZQoqIGB1c2VyX21nbXRfYWRkcmAgLSBBZGRyZXNzIG9mIHRoZSB1c2VyIG1hbmFnZW1lbnQgY29udHJhY3QKKiBgY291cnNlX3JlZ2lzdHJ5X2FkZHJgIC0gQWRkcmVzcyBvZiB0aGUgY291cnNlIHJlZ2lzdHJ5IGNvbnRyYWN0CgojIFBhbmljcwoKKiBJZiBjYWxsZXIgaXMgbm90IHRoZSBjb250cmFjdCBvd25lcgoqIElmIGFueSBvZiB0aGUgcHJvdmlkZWQgYWRkcmVzc2VzIGFyZSBpbnZhbGlkCgojIFN0b3JhZ2UKClN0b3JlcyB0aGUgYWRkcmVzc2VzIGluIGluc3RhbmNlIHN0b3JhZ2Uga2V5czogKCJ1c2VyX21nbXRfYWRkciIsKSBhbmQgKCJjb3Vyc2VfcmVnaXN0cnlfYWRkciIsKQoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBVcGRhdGUgY29udHJhY3QgYWRkcmVzc2VzCmNvbnRyYWN0LnNldF9jb25maWcoCmVudi5jbG9uZSgpLApjb250cmFjdF9vd25lcl9hZGRyZXNzLApuZXdfdXNlcl9tZ210X2FkZHJlc3MsCm5ld19jb3Vyc2VfcmVnaXN0cnlfYWRkcmVzcwopOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKk93bmVyIG9ubHkqKjogT25seSBjb250cmFjdCBvd25lciBjYW4gdXBkYXRlIGFkZHJlc3NlcwoqICoqSW52YWxpZCBhZGRyZXNzZXMqKjogV2lsbCBwYW5pYyBpZiBhZGRyZXNzZXMgYXJlIGludmFsaWQKKiAqKlJ1bnRpbWUgdXBkYXRlcyoqOiBDYW4gYmUgY2FsbGVkIGFmdGVyIGNvbnRyYWN0IGRlcGxveW1lbnQKKiAqKkltbWVkaWF0ZSBlZmZlY3QqKjogQ2hhbmdlAAAACnNldF9jb25maWcAAAAAAAMAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAAAAAAOdXNlcl9tZ210X2FkZHIAAAAAABMAAAAAAAAAFGNvdXJzZV9yZWdpc3RyeV9hZGRyAAAAEwAAAAA=", + "AAAAAAAAARFHZXQgdGhlIGN1cnJlbnQgY29udHJhY3QgdmVyc2lvbgoKUmV0dXJucyB0aGUgc2VtYW50aWMgdmVyc2lvbiBvZiB0aGUgY3VycmVudCBjb250cmFjdCBkZXBsb3ltZW50LgpUaGlzIGlzIHVzZWZ1bCBmb3IgdHJhY2tpbmcgY29udHJhY3QgdXBncmFkZXMgYW5kIGNvbXBhdGliaWxpdHkuCgojIEFyZ3VtZW50cwoqIGBfZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50ICh1bnVzZWQpCgojIFJldHVybnMKKiBgU3RyaW5nYCAtIFRoZSBjdXJyZW50IGNvbnRyYWN0IHZlcnNpb24AAAAAAAAUZ2V0X2NvbnRyYWN0X3ZlcnNpb24AAAAAAAAAAQAAABA=", + "AAAAAAAAAR5HZXQgY29udHJhY3QgdmVyc2lvbiBoaXN0b3J5CgpSZXR1cm5zIGEgbGlzdCBvZiBhbGwgdmVyc2lvbnMgdGhhdCBoYXZlIGJlZW4gZGVwbG95ZWQgZm9yIHRoaXMgY29udHJhY3QuClRoaXMgaGVscHMgdHJhY2sgdGhlIGV2b2x1dGlvbiBvZiB0aGUgY29udHJhY3Qgb3ZlciB0aW1lLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CgojIFJldHVybnMKKiBgVmVjPFN0cmluZz5gIC0gVmVjdG9yIG9mIHZlcnNpb24gc3RyaW5ncyBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyAAAAAAATZ2V0X3ZlcnNpb25faGlzdG9yeQAAAAAAAAAAAQAAA+oAAAAQ", + "AAAAAAAAAblDaGVjayBjb21wYXRpYmlsaXR5IGJldHdlZW4gY29udHJhY3QgdmVyc2lvbnMKCkRldGVybWluZXMgaWYgZGF0YSBmcm9tIG9uZSB2ZXJzaW9uIGNhbiBiZSBzYWZlbHkgdXNlZCB3aXRoIGFub3RoZXIgdmVyc2lvbi4KVGhpcyBpcyBjcnVjaWFsIGZvciBtaWdyYXRpb24gcHJvY2Vzc2VzIGFuZCBiYWNrd2FyZCBjb21wYXRpYmlsaXR5LgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGZyb21fdmVyc2lvbmAgLSBUaGUgc291cmNlIHZlcnNpb24gdG8gY2hlY2sgY29tcGF0aWJpbGl0eSBmcm9tCiogYHRvX3ZlcnNpb25gIC0gVGhlIHRhcmdldCB2ZXJzaW9uIHRvIGNoZWNrIGNvbXBhdGliaWxpdHkgdG8KCiMgUmV0dXJucwoqIGBib29sYCAtIFRydWUgaWYgdGhlIHZlcnNpb25zIGFyZSBjb21wYXRpYmxlLCBmYWxzZSBvdGhlcndpc2UAAAAAAAAVaXNfdmVyc2lvbl9jb21wYXRpYmxlAAAAAAAAAgAAAAAAAAAMZnJvbV92ZXJzaW9uAAAAEAAAAAAAAAAKdG9fdmVyc2lvbgAAAAAAEAAAAAEAAAAB", + "AAAAAAAAAjZNaWdyYXRlIGFjY2VzcyBkYXRhIGJldHdlZW4gY29udHJhY3QgdmVyc2lvbnMKClBlcmZvcm1zIGRhdGEgbWlncmF0aW9uIGZyb20gb25lIGNvbnRyYWN0IHZlcnNpb24gdG8gYW5vdGhlci4KVGhpcyBmdW5jdGlvbiBoYW5kbGVzIHRoZSB0cmFuc2Zvcm1hdGlvbiBvZiBjb3Vyc2UgYWNjZXNzIGRhdGEgc3RydWN0dXJlcwp3aGVuIHVwZ3JhZGluZyBjb250cmFjdCB2ZXJzaW9ucy4KCiMgQXJndW1lbnRzCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gVGhlIGFkZHJlc3MgcGVyZm9ybWluZyB0aGUgbWlncmF0aW9uIChtdXN0IGJlIGFkbWluKQoqIGBmcm9tX3ZlcnNpb25gIC0gVGhlIHNvdXJjZSB2ZXJzaW9uIHRvIG1pZ3JhdGUgZnJvbQoqIGB0b192ZXJzaW9uYCAtIFRoZSB0YXJnZXQgdmVyc2lvbiB0byBtaWdyYXRlIHRvCgojIFJldHVybnMKKiBgYm9vbGAgLSBUcnVlIGlmIG1pZ3JhdGlvbiB3YXMgc3VjY2Vzc2Z1bCwgZmFsc2Ugb3RoZXJ3aXNlCgojIEV2ZW50cwpFbWl0cyBhIG1pZ3JhdGlvbiBldmVudCB1cG9uIHN1Y2Nlc3NmdWwgY29tcGxldGlvbgAAAAAAE21pZ3JhdGVfYWNjZXNzX2RhdGEAAAAAAwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAxmcm9tX3ZlcnNpb24AAAAQAAAAAAAAAAp0b192ZXJzaW9uAAAAAAAQAAAAAQAAAAE=", + "AAAAAAAAAP9HZXQgbWlncmF0aW9uIHN0YXR1cyBmb3IgdGhlIGN1cnJlbnQgY29udHJhY3QKClJldHVybnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgbWlncmF0aW9uIHN0YXR1cyBhbmQgYW55CnBlbmRpbmcgbWlncmF0aW9ucyB0aGF0IG5lZWQgdG8gYmUgY29tcGxldGVkLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CgojIFJldHVybnMKKiBgU3RyaW5nYCAtIE1pZ3JhdGlvbiBzdGF0dXMgaW5mb3JtYXRpb24AAAAAFGdldF9taWdyYXRpb25fc3RhdHVzAAAAAAAAAAEAAAAQ", + "AAAAAAAAAAAAAAAPdHJhbnNmZXJfY291cnNlAAAAAAMAAAAAAAAACWNvdXJzZV9pZAAAAAAAABAAAAAAAAAABGZyb20AAAATAAAAAAAAAAJ0bwAAAAAAEwAAAAA=" ]), + options + ) + } + public readonly fromJSON = { + initialize: this.txFromJSON, + grant_access: this.txFromJSON, + revoke_access: this.txFromJSON, + save_user_profile: this.txFromJSON, + list_user_courses: this.txFromJSON, + list_course_access: this.txFromJSON, + revoke_all_access: this.txFromJSON, + set_config: this.txFromJSON, + get_contract_version: this.txFromJSON, + get_version_history: this.txFromJSON>, + is_version_compatible: this.txFromJSON, + migrate_access_data: this.txFromJSON, + get_migration_status: this.txFromJSON, + transfer_course: this.txFromJSON + } +} \ No newline at end of file diff --git a/schemas/course_access-ts/tsconfig.json b/schemas/course_access-ts/tsconfig.json new file mode 100644 index 0000000..acac142 --- /dev/null +++ b/schemas/course_access-ts/tsconfig.json @@ -0,0 +1,98 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "NodeNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + // "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "src/*" + ] +} \ No newline at end of file diff --git a/schemas/course_access_docs.md b/schemas/course_access_docs.md new file mode 100644 index 0000000..b4a0592 --- /dev/null +++ b/schemas/course_access_docs.md @@ -0,0 +1,882 @@ +Env Meta: AAAAAAAAABYAAAAA + • Protocol Version: 22 + +Contract Meta: + • rsver: 1.90.0-nightly + • rssdkver: 22.0.8#f46e9e0610213bbb72285566f9dd960ff96d03d8 + +Contract Spec: + • Error: Error + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UserAlreadyHasAccess), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UserNoAccessCourse), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(Unauthorized), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(NameRequired), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmailRequired), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CountryRequired), + value: 6, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidCourseId), + value: 7, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidUser), + value: 8, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyCourseId), + value: 9, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidTransferData), + value: 10, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(SameUserTransfer), + value: 11, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(Initialized), + value: 12, + } + + • Error: VersioningError + Docs: Errors that can occur during contract versioning operations + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Invalid version format), + name: StringM(InvalidVersion), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Version not found in history), + name: StringM(VersionNotFound), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration not compatible), + name: StringM(MigrationNotCompatible), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration already completed), + name: StringM(MigrationAlreadyCompleted), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Unauthorized migration attempt), + name: StringM(UnauthorizedMigration), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration failed), + name: StringM(MigrationFailed), + value: 6, + } + + • Struct: CourseAccess + Docs: Represents access permission for a user to a specific course. + + This struct defines the relationship between a user and a course + they have been granted access to. + Fields: + • course_id: String + StringM(The unique identifier of the course) + • user: Address + StringM(The address of the user who has access) + + • Struct: UserCourses + Docs: Contains all courses that a specific user has access to. + + This struct is used to efficiently query and return all courses + accessible by a particular user. + Fields: + • courses: Vec( + ScSpecTypeVec { + element_type: String, + }, + ) + StringM(List of course IDs the user has access to) + • user: Address + StringM(The address of the user) + + • Union: DataKey + Docs: Storage keys for different data types in the contract. + + This enum defines the various keys used to store and retrieve + data from the contract's persistent storage. + Cases: + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing course access: (course_id, user) -> CourseAccess), + name: StringM(CourseAccess), + type_: VecM( + [ + String, + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing user profile: user -> UserProfile), + name: StringM(UserProfile), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing courses per user: user -> UserCourses), + name: StringM(UserCourses), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing users per course: course_id -> CourseUsers), + name: StringM(CourseUsers), + type_: VecM( + [ + String, + ], + ), + }, + ) + + • Struct: UserProfile + Docs: Represents a user's profile information. + + This struct contains all the personal and professional information + that users can store on-chain as part of their profile. + Fields: + • country: String + StringM(The user's country of residence) + • email: String + StringM(The user's email address) + • goals: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(Optional learning goals or objectives) + • name: String + StringM(The user's full name) + • profession: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(Optional profession or job title) + + • Struct: CourseUsers + Docs: Contains all users who have access to a specific course. + + This struct is used to efficiently query and return all users + who have been granted access to a particular course. + Fields: + • course: String + StringM(The unique identifier of the course) + • users: Vec( + ScSpecTypeVec { + element_type: Address, + }, + ) + StringM(List of user addresses who have access to the course) + + • Function: initialize + Docs: One-time constructor to set owner and config addresses. + + Initializes the contract with the necessary external contract addresses. + This function can only be called once during contract deployment. + + # Arguments + + * `env` - The Soroban environment + * `caller` - The address of the contract deployer/owner + * `user_mgmt_addr` - Address of the user management contract + * `course_registry_addr` - Address of the course registry contract + + # Panics + + * Fails if the contract has already been initialized + * If any of the provided addresses are invalid + + # Examples + + ```rust + // Initialize contract during deployment + contract.initialize( + env.clone(), + deployer_address, + user_mgmt_contract_address, + course_registry_contract_address + ); + ``` + + # Edge Cases + + * **Double initialization**: Will panic if called more than once + * **Invalid addresses**: Contract addresses must be valid + * **Deployment only**: Should only be called during contract deployment + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user_mgmt_addr), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_registry_addr), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: grant_access + Docs: Grant access to a specific user for a given course. + + Allows a user to access a specific course. Only authorized users + (course creators or admins) can grant access. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course + * `user` - The address of the user to grant access to + + # Panics + + * If course doesn't exist + * If caller is not authorized (not course creator or admin) + * If user already has access + + # Examples + + ```rust + // Course creator granting access + contract.grant_access( + env.clone(), + "course_123".try_into().unwrap(), + student_address + ); + + // Admin granting access + contract.grant_access( + env.clone(), + "course_456".try_into().unwrap(), + student_address + ); + ``` + + # Edge Cases + + * **Already has access**: Will panic if user already has access + * **Non-existent course**: Will panic if course doesn't exist + * **Permission denied**: Only course creators and admins can grant access + * **User validation**: User address must be valid + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: revoke_access + Docs: Revoke access for a specific user from a course. + + Removes a user's access to a specific course. Only authorized users + (course creators or admins) can revoke access. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course + * `user` - The address of the user to revoke access from + + # Returns + + Returns `true` if access was successfully revoked, `false` otherwise. + + # Panics + + * If course doesn't exist + * If caller is not authorized (not course creator or admin) + + # Examples + + ```rust + // Revoke access from a user + let success = contract.revoke_access( + env.clone(), + "course_123".try_into().unwrap(), + student_address + ); + + if success { + println!("Access revoked successfully"); + } else { + println!("User didn't have access"); + } + ``` + + # Edge Cases + + * **No access to revoke**: Returns `false` if user didn't have access + * **Non-existent course**: Will panic if course doesn't exist + * **Permission denied**: Only course creators and admins can revoke access + * **Idempotent**: Safe to call multiple + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: save_user_profile + Docs: Save or update a user's profile on-chain. + + Stores user profile information in the contract storage. + This includes personal and professional information. + + # Arguments + + * `env` - The Soroban environment + * `name` - The user's full name + * `email` - The user's email address + * `profession` - Optional profession/job title + * `goals` - Optional learning goals or objectives + * `country` - The user's country of residence + + # Panics + + * If name, email, or country are empty + * If email format is invalid + + # Examples + + ```rust + // Save user profile + contract.save_user_profile( + env.clone(), + "John Doe".try_into().unwrap(), + "john@example.com".try_into().unwrap(), + Some("Software Developer".try_into().unwrap()), + Some("Learn Rust programming".try_into().unwrap()), + "US".try_into().unwrap() + ); + + // Save minimal profile + contract.save_user_profile( + env.clone(), + "Jane Smith".try_into().unwrap(), + "jane@example.com".try_into().unwrap(), + None, + None, + "CA".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Empty required fields**: Name, email, and coun + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(name), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(email), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(profession), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(goals), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(country), + type_: String, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: list_user_courses + Docs: List all courses a user has access to. + + Retrieves all courses that the specified user is enrolled in + or has been granted access to. + + # Arguments + + * `env` - The Soroban environment + * `user` - The address of the user to query + + # Returns + + Returns a `UserCourses` struct containing the list of accessible courses. + + # Examples + + ```rust + // Get user's accessible courses + let user_courses = contract.list_user_courses(env.clone(), user_address); + + for course_id in user_courses.course_ids { + println!("User has access to course: {}", course_id); + } + ``` + + # Edge Cases + + * **No access**: Returns empty list if user has no course access + * **Non-existent user**: Returns empty list for non-existent users + * **Public access**: Anyone can query user courses + * **Revoked courses**: Only includes currently accessible courses + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserCourses), + }, + ), + ], + ) + + • Function: list_course_access + Docs: List all users who have access to a course. + + Retrieves all users who have been granted access to the specified course. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course + + # Returns + + Returns a `CourseUsers` struct containing the list of users with access. + + # Examples + + ```rust + // Get all users with access to a course + let course_users = contract.list_course_access(env.clone(), "course_123".try_into().unwrap()); + + for user in course_users.users { + println!("User with access: {}", user); + } + ``` + + # Edge Cases + + * **No users**: Returns empty list if no users have access + * **Non-existent course**: Returns empty list for non-existent courses + * **Public access**: Anyone can query course access + * **Real-time data**: Always returns current access status + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(CourseUsers), + }, + ), + ], + ) + + • Function: revoke_all_access + Docs: Revoke all user access for a course. + + Removes access for all users from the specified course. + Only admin or course creator is allowed to perform this operation. + + # Arguments + + * `env` - The Soroban environment + * `user` - The address of the user requesting the operation + * `course_id` - The unique identifier of the course + + # Returns + + Returns the number of users affected by the revocation and emits an event. + + # Panics + + * If course doesn't exist + * If caller is not authorized (not course creator or admin) + + # Examples + + ```rust + // Revoke all access for a course + let affected_users = contract.revoke_all_access( + env.clone(), + admin_address, + "course_123".try_into().unwrap() + ); + + println!("Revoked access for {} users", affected_users); + ``` + + # Edge Cases + + * **No users**: Returns 0 if no users had access + * **Non-existent course**: Will panic if course doesn't exist + * **Permission denied**: Only course creators and admins can perform this + * **Bulk operation**: Efficiently removes all access in one transaction + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ], + ) + Output: VecM( + [ + U32, + ], + ) + + • Function: set_config + Docs: Configure external contract addresses used for auth checks. + + Updates the addresses of external contracts that this contract + depends on for authentication and authorization checks. + + # Arguments + + * `env` - The Soroban environment + * `caller` - The address of the user making the configuration change + * `user_mgmt_addr` - Address of the user management contract + * `course_registry_addr` - Address of the course registry contract + + # Panics + + * If caller is not the contract owner + * If any of the provided addresses are invalid + + # Storage + + Stores the addresses in instance storage keys: ("user_mgmt_addr",) and ("course_registry_addr",) + + # Examples + + ```rust + // Update contract addresses + contract.set_config( + env.clone(), + contract_owner_address, + new_user_mgmt_address, + new_course_registry_address + ); + ``` + + # Edge Cases + + * **Owner only**: Only contract owner can update addresses + * **Invalid addresses**: Will panic if addresses are invalid + * **Runtime updates**: Can be called after contract deployment + * **Immediate effect**: Change + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user_mgmt_addr), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_registry_addr), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: get_contract_version + Docs: Get the current contract version + + Returns the semantic version of the current contract deployment. + This is useful for tracking contract upgrades and compatibility. + + # Arguments + * `_env` - The Soroban environment (unused) + + # Returns + * `String` - The current contract version + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + • Function: get_version_history + Docs: Get contract version history + + Returns a list of all versions that have been deployed for this contract. + This helps track the evolution of the contract over time. + + # Arguments + * `env` - The Soroban environment + + # Returns + * `Vec` - Vector of version strings in chronological order + Inputs: VecM( + [], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: String, + }, + ), + ], + ) + + • Function: is_version_compatible + Docs: Check compatibility between contract versions + + Determines if data from one version can be safely used with another version. + This is crucial for migration processes and backward compatibility. + + # Arguments + * `env` - The Soroban environment + * `from_version` - The source version to check compatibility from + * `to_version` - The target version to check compatibility to + + # Returns + * `bool` - True if the versions are compatible, false otherwise + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(from_version), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(to_version), + type_: String, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: migrate_access_data + Docs: Migrate access data between contract versions + + Performs data migration from one contract version to another. + This function handles the transformation of course access data structures + when upgrading contract versions. + + # Arguments + * `env` - The Soroban environment + * `caller` - The address performing the migration (must be admin) + * `from_version` - The source version to migrate from + * `to_version` - The target version to migrate to + + # Returns + * `bool` - True if migration was successful, false otherwise + + # Events + Emits a migration event upon successful completion + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(from_version), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(to_version), + type_: String, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: get_migration_status + Docs: Get migration status for the current contract + + Returns information about the current migration status and any + pending migrations that need to be completed. + + # Arguments + * `env` - The Soroban environment + + # Returns + * `String` - Migration status information + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + • Function: transfer_course + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(from), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(to), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + diff --git a/schemas/course_access_schema.json b/schemas/course_access_schema.json new file mode 100644 index 0000000..54b645f --- /dev/null +++ b/schemas/course_access_schema.json @@ -0,0 +1,614 @@ +[ + { + "type": "enum", + "doc": "", + "name": "Error", + "cases": [ + { + "doc": "", + "name": "UserAlreadyHasAccess", + "value": 1 + }, + { + "doc": "", + "name": "UserNoAccessCourse", + "value": 2 + }, + { + "doc": "", + "name": "Unauthorized", + "value": 3 + }, + { + "doc": "", + "name": "NameRequired", + "value": 4 + }, + { + "doc": "", + "name": "EmailRequired", + "value": 5 + }, + { + "doc": "", + "name": "CountryRequired", + "value": 6 + }, + { + "doc": "", + "name": "InvalidCourseId", + "value": 7 + }, + { + "doc": "", + "name": "InvalidUser", + "value": 8 + }, + { + "doc": "", + "name": "EmptyCourseId", + "value": 9 + }, + { + "doc": "", + "name": "InvalidTransferData", + "value": 10 + }, + { + "doc": "", + "name": "SameUserTransfer", + "value": 11 + }, + { + "doc": "", + "name": "Initialized", + "value": 12 + } + ] + }, + { + "type": "enum", + "doc": "Errors that can occur during contract versioning operations", + "name": "VersioningError", + "cases": [ + { + "doc": "Invalid version format", + "name": "InvalidVersion", + "value": 1 + }, + { + "doc": "Version not found in history", + "name": "VersionNotFound", + "value": 2 + }, + { + "doc": "Migration not compatible", + "name": "MigrationNotCompatible", + "value": 3 + }, + { + "doc": "Migration already completed", + "name": "MigrationAlreadyCompleted", + "value": 4 + }, + { + "doc": "Unauthorized migration attempt", + "name": "UnauthorizedMigration", + "value": 5 + }, + { + "doc": "Migration failed", + "name": "MigrationFailed", + "value": 6 + } + ] + }, + { + "type": "struct", + "doc": "Represents access permission for a user to a specific course.\n\nThis struct defines the relationship between a user and a course\nthey have been granted access to.", + "name": "CourseAccess", + "fields": [ + { + "doc": "The unique identifier of the course", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "The address of the user who has access", + "name": "user", + "value": { + "type": "address" + } + } + ] + }, + { + "type": "struct", + "doc": "Contains all courses that a specific user has access to.\n\nThis struct is used to efficiently query and return all courses\naccessible by a particular user.", + "name": "UserCourses", + "fields": [ + { + "doc": "List of course IDs the user has access to", + "name": "courses", + "value": { + "type": "vec", + "element": { + "type": "string" + } + } + }, + { + "doc": "The address of the user", + "name": "user", + "value": { + "type": "address" + } + } + ] + }, + { + "type": "union", + "doc": "Storage keys for different data types in the contract.\n\nThis enum defines the various keys used to store and retrieve\ndata from the contract's persistent storage.", + "name": "DataKey", + "cases": [ + { + "doc": "Key for storing course access: (course_id, user) -> CourseAccess", + "name": "CourseAccess", + "values": [ + { + "type": "string" + }, + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing user profile: user -> UserProfile", + "name": "UserProfile", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing courses per user: user -> UserCourses", + "name": "UserCourses", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing users per course: course_id -> CourseUsers", + "name": "CourseUsers", + "values": [ + { + "type": "string" + } + ] + } + ] + }, + { + "type": "struct", + "doc": "Represents a user's profile information.\n\nThis struct contains all the personal and professional information\nthat users can store on-chain as part of their profile.", + "name": "UserProfile", + "fields": [ + { + "doc": "The user's country of residence", + "name": "country", + "value": { + "type": "string" + } + }, + { + "doc": "The user's email address", + "name": "email", + "value": { + "type": "string" + } + }, + { + "doc": "Optional learning goals or objectives", + "name": "goals", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "The user's full name", + "name": "name", + "value": { + "type": "string" + } + }, + { + "doc": "Optional profession or job title", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "struct", + "doc": "Contains all users who have access to a specific course.\n\nThis struct is used to efficiently query and return all users\nwho have been granted access to a particular course.", + "name": "CourseUsers", + "fields": [ + { + "doc": "The unique identifier of the course", + "name": "course", + "value": { + "type": "string" + } + }, + { + "doc": "List of user addresses who have access to the course", + "name": "users", + "value": { + "type": "vec", + "element": { + "type": "address" + } + } + } + ] + }, + { + "type": "function", + "doc": "One-time constructor to set owner and config addresses.\n\nInitializes the contract with the necessary external contract addresses.\nThis function can only be called once during contract deployment.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `caller` - The address of the contract deployer/owner\n* `user_mgmt_addr` - Address of the user management contract\n* `course_registry_addr` - Address of the course registry contract\n\n# Panics\n\n* Fails if the contract has already been initialized\n* If any of the provided addresses are invalid\n\n# Examples\n\n```rust\n// Initialize contract during deployment\ncontract.initialize(\nenv.clone(),\ndeployer_address,\nuser_mgmt_contract_address,\ncourse_registry_contract_address\n);\n```\n\n# Edge Cases\n\n* **Double initialization**: Will panic if called more than once\n* **Invalid addresses**: Contract addresses must be valid\n* **Deployment only**: Should only be called during contract deployment", + "name": "initialize", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "user_mgmt_addr", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_registry_addr", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Grant access to a specific user for a given course.\n\nAllows a user to access a specific course. Only authorized users\n(course creators or admins) can grant access.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course\n* `user` - The address of the user to grant access to\n\n# Panics\n\n* If course doesn't exist\n* If caller is not authorized (not course creator or admin)\n* If user already has access\n\n# Examples\n\n```rust\n// Course creator granting access\ncontract.grant_access(\nenv.clone(),\n\"course_123\".try_into().unwrap(),\nstudent_address\n);\n\n// Admin granting access\ncontract.grant_access(\nenv.clone(),\n\"course_456\".try_into().unwrap(),\nstudent_address\n);\n```\n\n# Edge Cases\n\n* **Already has access**: Will panic if user already has access\n* **Non-existent course**: Will panic if course doesn't exist\n* **Permission denied**: Only course creators and admins can grant access\n* **User validation**: User address must be valid", + "name": "grant_access", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Revoke access for a specific user from a course.\n\nRemoves a user's access to a specific course. Only authorized users\n(course creators or admins) can revoke access.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course\n* `user` - The address of the user to revoke access from\n\n# Returns\n\nReturns `true` if access was successfully revoked, `false` otherwise.\n\n# Panics\n\n* If course doesn't exist\n* If caller is not authorized (not course creator or admin)\n\n# Examples\n\n```rust\n// Revoke access from a user\nlet success = contract.revoke_access(\nenv.clone(),\n\"course_123\".try_into().unwrap(),\nstudent_address\n);\n\nif success {\nprintln!(\"Access revoked successfully\");\n} else {\nprintln!(\"User didn't have access\");\n}\n```\n\n# Edge Cases\n\n* **No access to revoke**: Returns `false` if user didn't have access\n* **Non-existent course**: Will panic if course doesn't exist\n* **Permission denied**: Only course creators and admins can revoke access\n* **Idempotent**: Safe to call multiple ", + "name": "revoke_access", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Save or update a user's profile on-chain.\n\nStores user profile information in the contract storage.\nThis includes personal and professional information.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `name` - The user's full name\n* `email` - The user's email address\n* `profession` - Optional profession/job title\n* `goals` - Optional learning goals or objectives\n* `country` - The user's country of residence\n\n# Panics\n\n* If name, email, or country are empty\n* If email format is invalid\n\n# Examples\n\n```rust\n// Save user profile\ncontract.save_user_profile(\nenv.clone(),\n\"John Doe\".try_into().unwrap(),\n\"john@example.com\".try_into().unwrap(),\nSome(\"Software Developer\".try_into().unwrap()),\nSome(\"Learn Rust programming\".try_into().unwrap()),\n\"US\".try_into().unwrap()\n);\n\n// Save minimal profile\ncontract.save_user_profile(\nenv.clone(),\n\"Jane Smith\".try_into().unwrap(),\n\"jane@example.com\".try_into().unwrap(),\nNone,\nNone,\n\"CA\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Empty required fields**: Name, email, and coun", + "name": "save_user_profile", + "inputs": [ + { + "doc": "", + "name": "name", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "email", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "goals", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "country", + "value": { + "type": "string" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "List all courses a user has access to.\n\nRetrieves all courses that the specified user is enrolled in\nor has been granted access to.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `user` - The address of the user to query\n\n# Returns\n\nReturns a `UserCourses` struct containing the list of accessible courses.\n\n# Examples\n\n```rust\n// Get user's accessible courses\nlet user_courses = contract.list_user_courses(env.clone(), user_address);\n\nfor course_id in user_courses.course_ids {\nprintln!(\"User has access to course: {}\", course_id);\n}\n```\n\n# Edge Cases\n\n* **No access**: Returns empty list if user has no course access\n* **Non-existent user**: Returns empty list for non-existent users\n* **Public access**: Anyone can query user courses\n* **Revoked courses**: Only includes currently accessible courses", + "name": "list_user_courses", + "inputs": [ + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "UserCourses" + } + ] + }, + { + "type": "function", + "doc": "List all users who have access to a course.\n\nRetrieves all users who have been granted access to the specified course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course\n\n# Returns\n\nReturns a `CourseUsers` struct containing the list of users with access.\n\n# Examples\n\n```rust\n// Get all users with access to a course\nlet course_users = contract.list_course_access(env.clone(), \"course_123\".try_into().unwrap());\n\nfor user in course_users.users {\nprintln!(\"User with access: {}\", user);\n}\n```\n\n# Edge Cases\n\n* **No users**: Returns empty list if no users have access\n* **Non-existent course**: Returns empty list for non-existent courses\n* **Public access**: Anyone can query course access\n* **Real-time data**: Always returns current access status", + "name": "list_course_access", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "CourseUsers" + } + ] + }, + { + "type": "function", + "doc": "Revoke all user access for a course.\n\nRemoves access for all users from the specified course.\nOnly admin or course creator is allowed to perform this operation.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `user` - The address of the user requesting the operation\n* `course_id` - The unique identifier of the course\n\n# Returns\n\nReturns the number of users affected by the revocation and emits an event.\n\n# Panics\n\n* If course doesn't exist\n* If caller is not authorized (not course creator or admin)\n\n# Examples\n\n```rust\n// Revoke all access for a course\nlet affected_users = contract.revoke_all_access(\nenv.clone(),\nadmin_address,\n\"course_123\".try_into().unwrap()\n);\n\nprintln!(\"Revoked access for {} users\", affected_users);\n```\n\n# Edge Cases\n\n* **No users**: Returns 0 if no users had access\n* **Non-existent course**: Will panic if course doesn't exist\n* **Permission denied**: Only course creators and admins can perform this\n* **Bulk operation**: Efficiently removes all access in one transaction", + "name": "revoke_all_access", + "inputs": [ + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "type": "function", + "doc": "Configure external contract addresses used for auth checks.\n\nUpdates the addresses of external contracts that this contract\ndepends on for authentication and authorization checks.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `caller` - The address of the user making the configuration change\n* `user_mgmt_addr` - Address of the user management contract\n* `course_registry_addr` - Address of the course registry contract\n\n# Panics\n\n* If caller is not the contract owner\n* If any of the provided addresses are invalid\n\n# Storage\n\nStores the addresses in instance storage keys: (\"user_mgmt_addr\",) and (\"course_registry_addr\",)\n\n# Examples\n\n```rust\n// Update contract addresses\ncontract.set_config(\nenv.clone(),\ncontract_owner_address,\nnew_user_mgmt_address,\nnew_course_registry_address\n);\n```\n\n# Edge Cases\n\n* **Owner only**: Only contract owner can update addresses\n* **Invalid addresses**: Will panic if addresses are invalid\n* **Runtime updates**: Can be called after contract deployment\n* **Immediate effect**: Change", + "name": "set_config", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "user_mgmt_addr", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_registry_addr", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Get the current contract version\n\nReturns the semantic version of the current contract deployment.\nThis is useful for tracking contract upgrades and compatibility.\n\n# Arguments\n* `_env` - The Soroban environment (unused)\n\n# Returns\n* `String` - The current contract version", + "name": "get_contract_version", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + }, + { + "type": "function", + "doc": "Get contract version history\n\nReturns a list of all versions that have been deployed for this contract.\nThis helps track the evolution of the contract over time.\n\n# Arguments\n* `env` - The Soroban environment\n\n# Returns\n* `Vec` - Vector of version strings in chronological order", + "name": "get_version_history", + "inputs": [], + "outputs": [ + { + "type": "vec", + "element": { + "type": "string" + } + } + ] + }, + { + "type": "function", + "doc": "Check compatibility between contract versions\n\nDetermines if data from one version can be safely used with another version.\nThis is crucial for migration processes and backward compatibility.\n\n# Arguments\n* `env` - The Soroban environment\n* `from_version` - The source version to check compatibility from\n* `to_version` - The target version to check compatibility to\n\n# Returns\n* `bool` - True if the versions are compatible, false otherwise", + "name": "is_version_compatible", + "inputs": [ + { + "doc": "", + "name": "from_version", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "to_version", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Migrate access data between contract versions\n\nPerforms data migration from one contract version to another.\nThis function handles the transformation of course access data structures\nwhen upgrading contract versions.\n\n# Arguments\n* `env` - The Soroban environment\n* `caller` - The address performing the migration (must be admin)\n* `from_version` - The source version to migrate from\n* `to_version` - The target version to migrate to\n\n# Returns\n* `bool` - True if migration was successful, false otherwise\n\n# Events\nEmits a migration event upon successful completion", + "name": "migrate_access_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "from_version", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "to_version", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Get migration status for the current contract\n\nReturns information about the current migration status and any\npending migrations that need to be completed.\n\n# Arguments\n* `env` - The Soroban environment\n\n# Returns\n* `String` - Migration status information", + "name": "get_migration_status", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + }, + { + "type": "function", + "doc": "", + "name": "transfer_course", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "from", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "to", + "value": { + "type": "address" + } + } + ], + "outputs": [] + } +] diff --git a/schemas/course_access_spec.json b/schemas/course_access_spec.json new file mode 100644 index 0000000..e69de29 diff --git a/schemas/course_registry-ts/.gitignore b/schemas/course_registry-ts/.gitignore new file mode 100644 index 0000000..72aae85 --- /dev/null +++ b/schemas/course_registry-ts/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +out/ diff --git a/schemas/course_registry-ts/README.md b/schemas/course_registry-ts/README.md new file mode 100644 index 0000000..7357d28 --- /dev/null +++ b/schemas/course_registry-ts/README.md @@ -0,0 +1,54 @@ +# course_registry-ts JS + +JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `course_registry-ts` via Soroban RPC. + +This library was automatically generated by Soroban CLI using a command similar to: + +```bash +soroban contract bindings ts \ + --rpc-url INSERT_RPC_URL_HERE \ + --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \ + --contract-id INSERT_CONTRACT_ID_HERE \ + --output-dir ./path/to/course_registry-ts +``` + +The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily. + +# To publish or not to publish + +This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command. + +But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path: + +```json +"dependencies": { + "course_registry-ts": "./path/to/this/folder" +} +``` + +However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract. + +```json +"scripts": { + "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name course_registry-ts" +} +``` + +Obviously you need to adjust the above command based on the actual command you used to generate the library. + +# Use it + +Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods: + +```js +import { Contract, networks } from "course_registry-ts" + +const contract = new Contract({ + ...networks.futurenet, // for example; check which networks this library exports + rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers +}) + +contract.| +``` + +As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code. diff --git a/schemas/course_registry-ts/package.json b/schemas/course_registry-ts/package.json new file mode 100644 index 0000000..78fee06 --- /dev/null +++ b/schemas/course_registry-ts/package.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.0", + "name": "course_registry-ts", + "type": "module", + "exports": "./dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@stellar/stellar-sdk": "^14.1.1", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } +} diff --git a/schemas/course_registry-ts/src/index.ts b/schemas/course_registry-ts/src/index.ts new file mode 100644 index 0000000..6a42b7d --- /dev/null +++ b/schemas/course_registry-ts/src/index.ts @@ -0,0 +1,1732 @@ +import { Buffer } from "buffer"; +import { Address } from '@stellar/stellar-sdk'; +import { + AssembledTransaction, + Client as ContractClient, + ClientOptions as ContractClientOptions, + MethodOptions, + Result, + Spec as ContractSpec, +} from '@stellar/stellar-sdk/contract'; +import type { + u32, + i32, + u64, + i64, + u128, + i128, + u256, + i256, + Option, + Typepoint, + Duration, +} from '@stellar/stellar-sdk/contract'; +export * from '@stellar/stellar-sdk' +export * as contract from '@stellar/stellar-sdk/contract' +export * as rpc from '@stellar/stellar-sdk/rpc' + +if (typeof window !== 'undefined') { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || Buffer; +} + + + + +export const Errors = { + 1: {message:"OnlyCreatorCanAddGoals"}, + 2: {message:"EmptyGoalContent"}, + 3: {message:"CourseIdNotExist"}, + 4: {message:"OnlyCreatorCanArchive"}, + 5: {message:"CourseAlreadyArchived"}, + 6: {message:"Unauthorized"}, + 7: {message:"NameRequired"}, + 8: {message:"EmptyCourseTitle"}, + 9: {message:"InvalidPrice"}, + 10: {message:"DuplicateCourseTitle"}, + 11: {message:"DuplicateCourseId"}, + 12: {message:"OnlyCreatorCanEditPrereqs"}, + 13: {message:"PrereqCourseNotFound"}, + 14: {message:"SelfPrerequisite"}, + 15: {message:"CircularDependency"}, + 16: {message:"EmptyCourseId"}, + 17: {message:"CourseNotFound"}, + 18: {message:"EmptyNewGoalContent"}, + 19: {message:"EmptyGoalId"}, + 20: {message:"GoalCourseMismatch"}, + 21: {message:"ModuleNotFound"}, + 401: {message:"UnauthorizedCaller"}, + 402: {message:"UnauthorizedCourseAccess"}, + 403: {message:"InvalidAdminOperation"}, + 404: {message:"EmptyModuleTitle"}, + 405: {message:"DuplicateModulePosition"}, + 22: {message:"EmptyModuleId"}, + 23: {message:"PrereqNotInList"}, + 24: {message:"InvalidModulePosition"}, + 25: {message:"InvalidModuleTitle"}, + 26: {message:"InvalidCourseDescription"}, + 27: {message:"InvalidCategoryName"}, + 28: {message:"EmptyCategory"}, + 29: {message:"InvalidTitleLength"}, + 43: {message:"InvalidLanguageLength"}, + 44: {message:"InvalidThumbnailUrlLength"}, + 45: {message:"InvalidDurationValue"}, + 46: {message:"InvalidLimitValue"}, + 47: {message:"InvalidOffsetValue"}, + 48: {message:"InvalidGoalContent"}, + 49: {message:"InvalidPrerequisiteId"}, + 50: {message:"EmptyPrerequisiteList"}, + 51: {message:"TooManyPrerequisites"}, + 52: {message:"EmptyPrerequisiteId"}, + 53: {message:"InvalidCourseId"}, + 54: {message:"InvalidPrice100"}, + 55: {message:"AlreadyInitialized"}, + 56: {message:"DuplicatePrerequisite"}, + 57: {message:"CourseRateLimitExceeded"}, + 58: {message:"CourseRateLimitNotConfigured"} +} + +/** + * Errors that can occur during contract versioning operations + */ +export const VersioningError = { + /** + * Invalid version format + */ + 1: {message:"InvalidVersion"}, + /** + * Version not found in history + */ + 2: {message:"VersionNotFound"}, + /** + * Migration not compatible + */ + 3: {message:"MigrationNotCompatible"}, + /** + * Migration already completed + */ + 4: {message:"MigrationAlreadyCompleted"}, + /** + * Unauthorized migration attempt + */ + 5: {message:"UnauthorizedMigration"}, + /** + * Migration failed + */ + 6: {message:"MigrationFailed"} +} + + +export interface CourseModule { + course_id: string; + created_at: u64; + id: string; + position: u32; + title: string; +} + + +export interface CourseGoal { + content: string; + course_id: string; + created_at: u64; + created_by: string; + goal_id: string; +} + + +/** + * Rate limiting configuration for course operations. + * + * Tracks rate limiting settings for spam protection in course creation. + */ +export interface CourseRateLimitConfig { + /** + * Maximum course creations allowed per window + */ +max_courses_per_window: u32; + /** + * Time window for rate limiting in seconds + */ +window_seconds: u64; +} + + +/** + * Rate limiting tracking data for course operations per address. + * + * Stores the current usage count and window start time for course rate limiting. + */ +export interface CourseRateLimitData { + /** + * Current count of course creations in this window + */ +count: u32; + /** + * Timestamp when the current window started + */ +window_start: u64; +} + + +export interface CourseCategory { + description: Option; + id: u128; + name: string; +} + +export type DataKey = {tag: "Module", values: readonly [string]} | {tag: "Courses", values: void} | {tag: "CourseGoalList", values: readonly [string]} | {tag: "CourseGoal", values: readonly [string, string]} | {tag: "CoursePrerequisites", values: readonly [string]} | {tag: "CategorySeq", values: void} | {tag: "CourseCategory", values: readonly [u128]} | {tag: "Admins", values: void} | {tag: "CourseRateLimitConfig", values: void} | {tag: "CourseRateLimit", values: readonly [string]}; + + +export interface Course { + category: Option; + creator: string; + description: string; + duration_hours: Option; + id: string; + is_archived: boolean; + language: Option; + level: Option; + prerequisites: Array; + price: u128; + published: boolean; + thumbnail_url: Option; + title: string; +} + + +export interface CourseId { + count: u128; + id: string; +} + + +export interface Category { + count: u128; + name: string; +} + + +export interface CourseFilters { + category: Option; + level: Option; + max_duration: Option; + max_price: Option; + min_duration: Option; + min_price: Option; + /** + * Text search in course title and description + */ +search_text: Option; +} + + +export interface EditCourseParams { + new_category: Option>; + new_description: Option; + new_duration_hours: Option>; + new_language: Option>; + new_level: Option>; + new_price: Option; + new_published: Option; + new_thumbnail_url: Option>; + new_title: Option; +} + + +/** + * Backup data structure for course registry system. + * + * Contains all course data, categories, modules, goals, and prerequisites + * for backup and recovery operations. + */ +export interface CourseBackupData { + /** + * List of admin addresses + */ +admins: Array; + /** + * Backup timestamp + */ +backup_timestamp: u64; + /** + * Backup version for compatibility + */ +backup_version: string; + /** + * All course categories + */ +categories: Map; + /** + * Category sequence counter + */ +category_seq: u128; + /** + * All courses in the system + */ +courses: Map; + /** + * All course goals mapped by (course_id, goal_id) + */ +goals: Map>; + /** + * All course modules + */ +modules: Map; + /** + * Course prerequisites mapping + */ +prerequisites: Map>; +} + +export interface Client { + /** + * Construct and simulate a create_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Create a new course in the registry. + * + * This function creates a new course with the specified metadata and + * returns the created course object with a unique identifier. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `title` - The course title + * * `description` - The course description + * * `price` - The course price in the platform's currency + * * `category` - Optional course category + * * `language` - Optional course language + * * `thumbnail_url` - Optional URL for the course thumbnail image + * * `level` - Optional course difficulty level + * * `duration_hours` - Optional estimated duration in hours + * + * # Returns + * + * Returns the created `Course` object with all metadata and a unique ID. + * + * # Panics + * + * * If title or description are empty + * * If creator address is invalid + * * If price exceeds maximum allowed value + * + * # Examples + * + * ```rust + * let course = contract.create_course( + * env.clone(), + * instructor_address, + * "Rust Programming Basics".try_into().unwrap(), + * "Learn Rust from scratch".try_into().unwrap(), + * 50 + */ + create_course: ({creator, title, description, price, category, language, thumbnail_url, level, duration_hours}: {creator: string, title: string, description: string, price: u128, category: Option, language: Option, thumbnail_url: Option, level: Option, duration_hours: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a create_course_category transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Create a new course category. + * + * This function creates a new category that can be used to classify courses. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `caller` - The address of the user creating the category + * * `name` - The name of the category + * * `description` - Optional description of the category + * + * # Returns + * + * Returns the unique ID of the created category. + * + * # Panics + * + * * If category name is empty + * * If category with same name already exists + * + * # Examples + * + * ```rust + * // Create a programming category + * let category_id = contract.create_course_category( + * env.clone(), + * admin_address, + * "Programming".try_into().unwrap(), + * Some("Computer programming courses".try_into().unwrap()) + * ); + * ``` + * + * # Edge Cases + * + * * **Duplicate names**: Cannot create categories with existing names + * * **Empty names**: Category name cannot be empty + * * **Unique IDs**: Each category gets a unique auto-generated ID + */ + create_course_category: ({caller, name, description}: {caller: string, name: string, description: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Retrieve a course by its ID. + * + * This function fetches a course's complete information using its unique identifier. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course to retrieve + * + * # Returns + * + * Returns the `Course` object containing all course metadata. + * + * # Panics + * + * * If course with given ID doesn't exist + * * If course_id is invalid or empty + * + * # Examples + * + * ```rust + * // Get course by ID + * let course = contract.get_course(env.clone(), "course_123".try_into().unwrap()); + * println!("Course title: {}", course.title); + * ``` + * + * # Edge Cases + * + * * **Non-existent course**: Will panic if course ID doesn't exist + * * **Archived courses**: Still retrievable but marked as archived + * * **Public access**: Anyone can retrieve course information + */ + get_course: ({course_id}: {course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_course_category transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Retrieve a course category by its ID. + * + * This function fetches a category's information using its unique identifier. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `category_id` - The unique identifier of the category to retrieve + * + * # Returns + * + * Returns `Some(CourseCategory)` if found, `None` if the category doesn't exist. + * + * # Examples + * + * ```rust + * // Get category by ID + * if let Some(category) = contract.get_course_category(env.clone(), 1) { + * println!("Category: {}", category.name); + * } else { + * println!("Category not found"); + * } + * ``` + * + * # Edge Cases + * + * * **Non-existent category**: Returns `None` instead of panicking + * * **Invalid ID**: Returns `None` for invalid category IDs + * * **Public access**: Anyone can retrieve category information + */ + get_course_category: ({category_id}: {category_id: u128}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a get_courses_by_instructor transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get all courses created by a specific instructor. + * + * This function retrieves all courses that were created by the specified instructor. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `instructor` - The address of the instructor to query courses for + * + * # Returns + * + * Returns a vector of `Course` objects created by the instructor. + * + * # Examples + * + * ```rust + * // Get all courses by an instructor + * let instructor_courses = contract.get_courses_by_instructor(env.clone(), instructor_address); + * for course in instructor_courses { + * println!("Course: {}", course.title); + * } + * ``` + * + * # Edge Cases + * + * * **No courses**: Returns empty vector if instructor has no courses + * * **Archived courses**: Includes archived courses in results + * * **Public access**: Anyone can query instructor courses + * * **Invalid instructor**: Returns empty vector for non-existent instructors + */ + get_courses_by_instructor: ({instructor}: {instructor: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a remove_module transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Remove a module from a course. + * + * This function removes a specific module from its associated course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `module_id` - The unique identifier of the module to remove + * + * # Panics + * + * Remove a module from a course. + * + * This function removes a specific module from its associated course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `module_id` - The unique identifier of the module to remove + * + * # Panics + * + * * If the module doesn't exist + * * If the module_id is invalid or empty + * * If module removal operation fails + * + * # Examples + * + * ```rust + * // Remove a module from a course + * contract.remove_module(env.clone(), "module_123".try_into().unwrap()); + * ``` + * + * # Edge Cases + * + * * **Non-existent module**: Will panic if module ID doesn't exist + * * **Invalid ID**: Will panic for invalid or empty module IDs + * * **Course updates**: Automatically updates course module count + * + * Panics if the module removal fails or if the module doesn't exist. + */ + remove_module: ({module_id}: {module_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a add_module transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Add a new module to a course. + * + * This function creates and adds a new module to the specified course + * at the given position. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course to add the module to + * * `position` - The position where the module should be inserted + * * `title` - The title of the new module + * + * # Returns + * + * Returns the created `CourseModule` object. + * + * # Panics + * + * * If course doesn't exist + * * If caller is not the course creator + * * If module title is empty + * * If position is invalid + * + * # Examples + * + * ```rust + * // Add a module at position 1 + * let module = contract.add_module( + * env.clone(), + * course_creator_address, + * "course_123".try_into().unwrap(), + * 1, + * "Introduction to Variables".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Invalid position**: Position must be valid for the course + * * **Empty title**: Module title cannot be empty + * * **Creator only**: Only course creator can add modules + * * **Auto-generated ID**: Module gets unique auto-generated ID + */ + add_module: ({caller, course_id, position, title}: {caller: string, course_id: string, position: u32, title: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a delete_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Delete a course from the registry. + * + * This function permanently removes a course from the registry. + * Only the course creator can delete their own courses. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course to delete + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the actual course creator + * * If course_id is invalid or empty + * + * # Examples + * + * ```rust + * // Course creator deleting their course + * contract.delete_course(env.clone(), course_creator_address, "course_123".try_into().unwrap()); + * ``` + * + * # Edge Cases + * + * * **Permission denied**: Only course creator can delete their courses + * * **Non-existent course**: Will panic if course doesn't exist + * * **Permanent deletion**: Course and all associated data are permanently removed + * * **Enrolled students**: Consider impact on enrolled students before deletion + * + * Panics if the deletion fails or if the creator is not authorized. + */ + delete_course: ({creator, course_id}: {creator: string, course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a hello_world transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Simple hello world function for testing. + * + * This is a basic function that returns a greeting message, + * primarily used for testing contract deployment and basic functionality. + * + * # Arguments + * + * * `_env` - The Soroban environment (unused) + * + * # Returns + * + * Returns a greeting string. + * + * # Examples + * + * ```rust + * // Test contract deployment + * let greeting = contract.hello_world(env.clone()); + * assert_eq!(greeting, "Hello from Web3 👋"); + * ``` + * + * # Edge Cases + * + * * **Always succeeds**: This function never fails + * * **No dependencies**: Requires no external data or state + * * **Testing only**: Primarily intended for contract testing + */ + hello_world: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a edit_goal transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Edit an existing course goal. + * + * This function allows the course creator to modify the content of an existing goal. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course + * * `goal_id` - The unique identifier of the goal to edit + * * `new_content` - The new content for the goal + * + * # Returns + * + * Returns the updated `CourseGoal` object. + * + * # Panics + * + * * If course doesn't exist + * * If goal doesn't exist + * * If creator is not the course creator + * * If new_content is empty + * + * # Examples + * + * ```rust + * // Edit a course goal + * let updated_goal = contract.edit_goal( + * env.clone(), + * course_creator_address, + * "course_123".try_into().unwrap(), + * "goal_456".try_into().unwrap(), + * "Updated learning objective".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Empty content**: New content cannot be empty + * * **Creator only**: Only course creator can edit goals + * * **Non-existent goal**: Will panic if goal ID doesn't exist + * * **Content validation**: New content must meet validation + */ + edit_goal: ({creator, course_id, goal_id, new_content}: {creator: string, course_id: string, goal_id: string, new_content: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a add_goal transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Add a new goal to a course. + * + * This function creates and adds a new learning goal to the specified course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course + * * `content` - The content/description of the goal + * + * # Returns + * + * Returns the created `CourseGoal` object. + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If content is empty + * + * # Examples + * + * ```rust + * // Add a learning goal to a course + * let goal = contract.add_goal( + * env.clone(), + * course_creator_address, + * "course_123".try_into().unwrap(), + * "Students will learn basic programming concepts".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Empty content**: Goal content cannot be empty + * * **Creator only**: Only course creator can add goals + * * **Auto-generated ID**: Goal gets unique auto-generated ID + * * **Content validation**: Goal content must meet validation requirements + */ + add_goal: ({creator, course_id, content}: {creator: string, course_id: string, content: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a remove_goal transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Remove a goal from a course. + * + * This function removes a specific learning goal from the course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `caller` - The address of the user requesting the removal + * * `course_id` - The unique identifier of the course + * * `goal_id` - The unique identifier of the goal to remove + * + * # Panics + * + * * If course doesn't exist + * * If goal doesn't exist + * * If caller is not the course creator + * + * # Examples + * + * ```rust + * // Remove a goal from a course + * contract.remove_goal( + * env.clone(), + * course_creator_address, + * "course_123".try_into().unwrap(), + * "goal_456".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Creator only**: Only course creator can remove goals + * * **Non-existent goal**: Will panic if goal ID doesn't exist + * * **Permanent removal**: Goal is permanently deleted from course + * * **Goal count**: Automatically updates course goal count + */ + remove_goal: ({caller, course_id, goal_id}: {caller: string, course_id: string, goal_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a add_prerequisite transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Add prerequisites to a course. + * + * This function adds prerequisite courses that must be completed + * before a student can enroll in the target course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course + * * `prerequisite_course_ids` - Vector of course IDs that are prerequisites + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If any prerequisite course doesn't exist + * * If trying to add self as prerequisite + * + * # Examples + * + * ```rust + * let mut prerequisites = Vec::new(&env); + * prerequisites.push_back("basic_rust".try_into().unwrap()); + * prerequisites.push_back("programming_fundamentals".try_into().unwrap()); + * + * contract.add_prerequisite( + * env.clone(), + * course_creator_address, + * "advanced_rust".try_into().unwrap(), + * prerequisites + * ); + * ``` + * + * # Edge Cases + * + * * **Circular dependencies**: Cannot add self as prerequisite + * * **Non-existent courses**: All prerequisite courses must exist + * * **Creator only**: Only course creator ca + */ + add_prerequisite: ({creator, course_id, prerequisite_course_ids}: {creator: string, course_id: string, prerequisite_course_ids: Array}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a remove_prerequisite transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Remove a prerequisite from a course. + * + * This function removes a specific prerequisite course requirement + * from the target course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course + * * `prerequisite_course_id` - The ID of the prerequisite course to remove + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If prerequisite doesn't exist for the course + * + * # Examples + * + * ```rust + * // Remove a prerequisite from a course + * contract.remove_prerequisite( + * env.clone(), + * course_creator_address, + * "advanced_rust".try_into().unwrap(), + * "basic_rust".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Non-existent prerequisite**: Will panic if prerequisite doesn't exist + * * **Creator only**: Only course creator can remove prerequisites + * * **No effect**: Removing non-existent prerequisite has no effect + * * **Student impact**: Consider impact on enrolled students + */ + remove_prerequisite: ({creator, course_id, prerequisite_course_id}: {creator: string, course_id: string, prerequisite_course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a edit_prerequisite transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Edit the prerequisites for a course. + * + * This function replaces all existing prerequisites with a new set + * of prerequisite courses. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course + * * `new_prerequisites` - Vector of new prerequisite course IDs + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If any prerequisite course doesn't exist + * * If trying to add self as prerequisite + * + * # Examples + * + * ```rust + * let mut new_prerequisites = Vec::new(&env); + * new_prerequisites.push_back("updated_course_1".try_into().unwrap()); + * new_prerequisites.push_back("updated_course_2".try_into().unwrap()); + * + * contract.edit_prerequisite( + * env.clone(), + * course_creator_address, + * "target_course".try_into().unwrap(), + * new_prerequisites + * ); + * ``` + * + * # Edge Cases + * + * * **Complete replacement**: All old prerequisites are removed + * * **Empty vector**: Can clear all prerequisites with empty vector + * * **Circular dependencies**: Cannot add self as prere + */ + edit_prerequisite: ({creator, course_id, new_prerequisites}: {creator: string, course_id: string, new_prerequisites: Array}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a edit_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Edit course information. + * + * This function allows the course creator to update various aspects + * of the course using the provided parameters. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course to edit + * * `params` - Parameters containing the fields to update + * + * # Returns + * + * Returns the updated `Course` object. + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If any field validation fails + * + * # Examples + * + * ```rust + * let params = EditCourseParams { + * title: Some("Updated Course Title".try_into().unwrap()), + * description: Some("Updated description".try_into().unwrap()), + * price: Some(7500), + * level: Some(CourseLevel::Intermediate), + * ..Default::default() + * }; + * + * let updated_course = contract.edit_course( + * env.clone(), + * course_creator_address, + * "course_123".try_into().unwrap(), + * params + * ); + * ``` + * + * # Edge Cases + * + * * **Partial updates**: Only provided fields are updated + * * **Validation**: All fields must pass validation rules + * * **Cre + */ + edit_course: ({creator, course_id, params}: {creator: string, course_id: string, params: EditCourseParams}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a archive_course transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Archive a course. + * + * This function marks a course as archived, making it unavailable for new enrollments + * while preserving existing data and access for current students. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `creator` - The address of the course creator + * * `course_id` - The unique identifier of the course to archive + * + * # Returns + * + * Returns the updated `Course` object with archived status. + * + * # Panics + * + * * If course doesn't exist + * * If creator is not the course creator + * * If course is already archived + * + * # Examples + * + * ```rust + * // Archive a course + * let archived_course = contract.archive_course( + * &env, + * course_creator_address, + * "course_123".try_into().unwrap() + * ); + * ``` + * + * # Edge Cases + * + * * **Already archived**: Will panic if course is already archived + * * **Creator only**: Only course creator can archive course + * * **Student access**: Current students retain access + * * **Reversible**: Course can be unarchived if needed + */ + archive_course: ({creator, course_id}: {creator: string, course_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a is_course_creator transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Check if a user is the creator of a specific course. + * + * This function verifies whether the specified user is the original creator + * of the given course. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `course_id` - The unique identifier of the course + * * `user` - The address of the user to check + * + * # Returns + * + * Returns `true` if the user is the course creator, `false` otherwise. + * + * # Panics + * + * * If course doesn't exist + * + * # Examples + * + * ```rust + * // Check if user is course creator + * let is_creator = contract.is_course_creator( + * &env, + * "course_123".try_into().unwrap(), + * user_address + * ); + * + * if is_creator { + * // User can edit this course + * } + * ``` + * + * # Edge Cases + * + * * **Non-existent course**: Will panic if course doesn't exist + * * **Public access**: Anyone can check creator status + * * **Creator verification**: Useful for permission checks + */ + is_course_creator: ({course_id, user}: {course_id: string, user: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a list_categories transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * List all available course categories. + * + * This function retrieves all course categories that have been created + * in the system. + * + * # Arguments + * + * * `env` - The Soroban environment + * + * # Returns + * + * Returns a vector of all available `Category` objects. + * + * # Examples + * + * ```rust + * // Get all categories + * let categories = contract.list_categories(env.clone()); + * for category in categories { + * println!("Category: {}", category.name); + * } + * ``` + * + * # Edge Cases + * + * * **Empty system**: Returns empty vector if no categories exist + * * **Public access**: Anyone can list categories + * * **Order**: Categories are returned in creation order + */ + list_categories: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a list_courses_with_filters transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * List courses with filtering and pagination. + * + * This function retrieves courses based on the provided filters + * with optional pagination support. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `filters` - Filtering criteria for courses + * * `limit` - Optional maximum number of courses to return + * * `offset` - Optional number of courses to skip for pagination + * + * # Returns + * + * Returns a vector of `Course` objects matching the filter criteria. + * + * # Examples + * + * ```rust + * // List first 10 courses + * let courses = contract.list_courses_with_filters( + * env.clone(), + * CourseFilters::default(), + * Some(10), + * Some(0) + * ); + * + * // Filter by category + * let mut filters = CourseFilters::default(); + * filters.category = Some("Programming".try_into().unwrap()); + * let programming_courses = contract.list_courses_with_filters( + * env.clone(), + * filters, + * Some(20), + * None + * ); + * ``` + * + * # Edge Cases + * + * * **No matches**: Returns empty vector if no courses match filters + * * **Large limits**: Limit should be reasonable to avoid gas issues + * * **Public access**: Anyone can list courses + * * **Arch + */ + list_courses_with_filters: ({filters, limit, offset}: {filters: CourseFilters, limit: Option, offset: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a export_course_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Export all course data for backup purposes (admin only) + * + * This function exports all course data including courses, categories, + * modules, goals, and prerequisites for backup and recovery purposes. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the export (must be admin) + * + * # Returns + * * `CourseBackupData` - Complete backup data structure + * + * # Panics + * * If caller is not an admin + */ + export_course_data: ({caller}: {caller: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a import_course_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Import course data from backup (admin only) + * + * This function imports course data from a backup structure. + * Only admins can perform this operation. This will overwrite existing data. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the import (must be admin) + * * `backup_data` - Backup data structure to import + * + * # Returns + * * `u32` - Number of courses imported + * + * # Panics + * * If caller is not an admin + * * If backup data is invalid + * * If import operation fails + */ + import_course_data: ({caller, backup_data}: {caller: string, backup_data: CourseBackupData}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_contract_version transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get the current contract version + * + * Returns the semantic version of the current contract deployment. + * This is useful for tracking contract upgrades and compatibility. + * + * # Arguments + * * `_env` - The Soroban environment (unused) + * + * # Returns + * * `String` - The current contract version + */ + get_contract_version: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_version_history transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get contract version history + * + * Returns a list of all versions that have been deployed for this contract. + * This helps track the evolution of the contract over time. + * + * # Arguments + * * `env` - The Soroban environment + * + * # Returns + * * `Vec` - Vector of version strings in chronological order + */ + get_version_history: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a is_version_compatible transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Check compatibility between contract versions + * + * Determines if data from one version can be safely used with another version. + * This is crucial for migration processes and backward compatibility. + * + * # Arguments + * * `env` - The Soroban environment + * * `from_version` - The source version to check compatibility from + * * `to_version` - The target version to check compatibility to + * + * # Returns + * * `bool` - True if the versions are compatible, false otherwise + */ + is_version_compatible: ({from_version, to_version}: {from_version: string, to_version: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a migrate_course_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Migrate course data between contract versions + * + * Performs data migration from one contract version to another. + * This function handles the transformation of course data structures + * when upgrading contract versions. + * + * # Arguments + * * `env` - The Soroban environment + * * `caller` - The address performing the migration (must be course creator or admin) + * * `from_version` - The source version to migrate from + * * `to_version` - The target version to migrate to + * + * # Returns + * * `bool` - True if migration was successful, false otherwise + * + * # Events + * Emits a migration event upon successful completion + */ + migrate_course_data: ({caller, from_version, to_version}: {caller: string, from_version: string, to_version: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_migration_status transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get migration status for the current contract + * + * Returns information about the current migration status and any + * pending migrations that need to be completed. + * + * # Arguments + * * `env` - The Soroban environment + * + * # Returns + * * `String` - Migration status information + */ + get_migration_status: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + +} +export class Client extends ContractClient { + static async deploy( + /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ + options: MethodOptions & + Omit & { + /** The hash of the Wasm blob, which must already be installed on-chain. */ + wasmHash: Buffer | string; + /** Salt used to generate the contract's ID. Passed through to {@link Operation.createCustomContract}. Default: random. */ + salt?: Buffer | Uint8Array; + /** The format used to decode `wasmHash`, if it's provided as a string. */ + format?: "hex" | "base64"; + } + ): Promise> { + return ContractClient.deploy(null, options) + } + constructor(public readonly options: ContractClientOptions) { + super( + new ContractSpec([ "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAMgAAAAAAAAAWT25seUNyZWF0b3JDYW5BZGRHb2FscwAAAAAAAQAAAAAAAAAQRW1wdHlHb2FsQ29udGVudAAAAAIAAAAAAAAAEENvdXJzZUlkTm90RXhpc3QAAAADAAAAAAAAABVPbmx5Q3JlYXRvckNhbkFyY2hpdmUAAAAAAAAEAAAAAAAAABVDb3Vyc2VBbHJlYWR5QXJjaGl2ZWQAAAAAAAAFAAAAAAAAAAxVbmF1dGhvcml6ZWQAAAAGAAAAAAAAAAxOYW1lUmVxdWlyZWQAAAAHAAAAAAAAABBFbXB0eUNvdXJzZVRpdGxlAAAACAAAAAAAAAAMSW52YWxpZFByaWNlAAAACQAAAAAAAAAURHVwbGljYXRlQ291cnNlVGl0bGUAAAAKAAAAAAAAABFEdXBsaWNhdGVDb3Vyc2VJZAAAAAAAAAsAAAAAAAAAGU9ubHlDcmVhdG9yQ2FuRWRpdFByZXJlcXMAAAAAAAAMAAAAAAAAABRQcmVyZXFDb3Vyc2VOb3RGb3VuZAAAAA0AAAAAAAAAEFNlbGZQcmVyZXF1aXNpdGUAAAAOAAAAAAAAABJDaXJjdWxhckRlcGVuZGVuY3kAAAAAAA8AAAAAAAAADUVtcHR5Q291cnNlSWQAAAAAAAAQAAAAAAAAAA5Db3Vyc2VOb3RGb3VuZAAAAAAAEQAAAAAAAAATRW1wdHlOZXdHb2FsQ29udGVudAAAAAASAAAAAAAAAAtFbXB0eUdvYWxJZAAAAAATAAAAAAAAABJHb2FsQ291cnNlTWlzbWF0Y2gAAAAAABQAAAAAAAAADk1vZHVsZU5vdEZvdW5kAAAAAAAVAAAAAAAAABJVbmF1dGhvcml6ZWRDYWxsZXIAAAAAAZEAAAAAAAAAGFVuYXV0aG9yaXplZENvdXJzZUFjY2VzcwAAAZIAAAAAAAAAFUludmFsaWRBZG1pbk9wZXJhdGlvbgAAAAAAAZMAAAAAAAAAEEVtcHR5TW9kdWxlVGl0bGUAAAGUAAAAAAAAABdEdXBsaWNhdGVNb2R1bGVQb3NpdGlvbgAAAAGVAAAAAAAAAA1FbXB0eU1vZHVsZUlkAAAAAAAAFgAAAAAAAAAPUHJlcmVxTm90SW5MaXN0AAAAABcAAAAAAAAAFUludmFsaWRNb2R1bGVQb3NpdGlvbgAAAAAAABgAAAAAAAAAEkludmFsaWRNb2R1bGVUaXRsZQAAAAAAGQAAAAAAAAAYSW52YWxpZENvdXJzZURlc2NyaXB0aW9uAAAAGgAAAAAAAAATSW52YWxpZENhdGVnb3J5TmFtZQAAAAAbAAAAAAAAAA1FbXB0eUNhdGVnb3J5AAAAAAAAHAAAAAAAAAASSW52YWxpZFRpdGxlTGVuZ3RoAAAAAAAdAAAAAAAAABVJbnZhbGlkTGFuZ3VhZ2VMZW5ndGgAAAAAAAArAAAAAAAAABlJbnZhbGlkVGh1bWJuYWlsVXJsTGVuZ3RoAAAAAAAALAAAAAAAAAAUSW52YWxpZER1cmF0aW9uVmFsdWUAAAAtAAAAAAAAABFJbnZhbGlkTGltaXRWYWx1ZQAAAAAAAC4AAAAAAAAAEkludmFsaWRPZmZzZXRWYWx1ZQAAAAAALwAAAAAAAAASSW52YWxpZEdvYWxDb250ZW50AAAAAAAwAAAAAAAAABVJbnZhbGlkUHJlcmVxdWlzaXRlSWQAAAAAAAAxAAAAAAAAABVFbXB0eVByZXJlcXVpc2l0ZUxpc3QAAAAAAAAyAAAAAAAAABRUb29NYW55UHJlcmVxdWlzaXRlcwAAADMAAAAAAAAAE0VtcHR5UHJlcmVxdWlzaXRlSWQAAAAANAAAAAAAAAAPSW52YWxpZENvdXJzZUlkAAAAADUAAAAAAAAAD0ludmFsaWRQcmljZTEwMAAAAAA2AAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAADcAAAAAAAAAFUR1cGxpY2F0ZVByZXJlcXVpc2l0ZQAAAAAAADgAAAAAAAAAF0NvdXJzZVJhdGVMaW1pdEV4Y2VlZGVkAAAAADkAAAAAAAAAHENvdXJzZVJhdGVMaW1pdE5vdENvbmZpZ3VyZWQAAAA6", + "AAAABAAAADtFcnJvcnMgdGhhdCBjYW4gb2NjdXIgZHVyaW5nIGNvbnRyYWN0IHZlcnNpb25pbmcgb3BlcmF0aW9ucwAAAAAAAAAAD1ZlcnNpb25pbmdFcnJvcgAAAAAGAAAAFkludmFsaWQgdmVyc2lvbiBmb3JtYXQAAAAAAA5JbnZhbGlkVmVyc2lvbgAAAAAAAQAAABxWZXJzaW9uIG5vdCBmb3VuZCBpbiBoaXN0b3J5AAAAD1ZlcnNpb25Ob3RGb3VuZAAAAAACAAAAGE1pZ3JhdGlvbiBub3QgY29tcGF0aWJsZQAAABZNaWdyYXRpb25Ob3RDb21wYXRpYmxlAAAAAAADAAAAG01pZ3JhdGlvbiBhbHJlYWR5IGNvbXBsZXRlZAAAAAAZTWlncmF0aW9uQWxyZWFkeUNvbXBsZXRlZAAAAAAAAAQAAAAeVW5hdXRob3JpemVkIG1pZ3JhdGlvbiBhdHRlbXB0AAAAAAAVVW5hdXRob3JpemVkTWlncmF0aW9uAAAAAAAABQAAABBNaWdyYXRpb24gZmFpbGVkAAAAD01pZ3JhdGlvbkZhaWxlZAAAAAAG", + "AAAAAQAAAAAAAAAAAAAADENvdXJzZU1vZHVsZQAAAAUAAAAAAAAACWNvdXJzZV9pZAAAAAAAABAAAAAAAAAACmNyZWF0ZWRfYXQAAAAAAAYAAAAAAAAAAmlkAAAAAAAQAAAAAAAAAAhwb3NpdGlvbgAAAAQAAAAAAAAABXRpdGxlAAAAAAAAEA==", + "AAAAAQAAAAAAAAAAAAAACkNvdXJzZUdvYWwAAAAAAAUAAAAAAAAAB2NvbnRlbnQAAAAAEAAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAKY3JlYXRlZF9hdAAAAAAABgAAAAAAAAAKY3JlYXRlZF9ieQAAAAAAEwAAAAAAAAAHZ29hbF9pZAAAAAAQ", + "AAAAAQAAAHlSYXRlIGxpbWl0aW5nIGNvbmZpZ3VyYXRpb24gZm9yIGNvdXJzZSBvcGVyYXRpb25zLgoKVHJhY2tzIHJhdGUgbGltaXRpbmcgc2V0dGluZ3MgZm9yIHNwYW0gcHJvdGVjdGlvbiBpbiBjb3Vyc2UgY3JlYXRpb24uAAAAAAAAAAAAABVDb3Vyc2VSYXRlTGltaXRDb25maWcAAAAAAAACAAAAK01heGltdW0gY291cnNlIGNyZWF0aW9ucyBhbGxvd2VkIHBlciB3aW5kb3cAAAAAFm1heF9jb3Vyc2VzX3Blcl93aW5kb3cAAAAAAAQAAAAoVGltZSB3aW5kb3cgZm9yIHJhdGUgbGltaXRpbmcgaW4gc2Vjb25kcwAAAA53aW5kb3dfc2Vjb25kcwAAAAAABg==", + "AAAAAQAAAI5SYXRlIGxpbWl0aW5nIHRyYWNraW5nIGRhdGEgZm9yIGNvdXJzZSBvcGVyYXRpb25zIHBlciBhZGRyZXNzLgoKU3RvcmVzIHRoZSBjdXJyZW50IHVzYWdlIGNvdW50IGFuZCB3aW5kb3cgc3RhcnQgdGltZSBmb3IgY291cnNlIHJhdGUgbGltaXRpbmcuAAAAAAAAAAAAE0NvdXJzZVJhdGVMaW1pdERhdGEAAAAAAgAAADBDdXJyZW50IGNvdW50IG9mIGNvdXJzZSBjcmVhdGlvbnMgaW4gdGhpcyB3aW5kb3cAAAAFY291bnQAAAAAAAAEAAAAKVRpbWVzdGFtcCB3aGVuIHRoZSBjdXJyZW50IHdpbmRvdyBzdGFydGVkAAAAAAAADHdpbmRvd19zdGFydAAAAAY=", + "AAAAAQAAAAAAAAAAAAAADkNvdXJzZUNhdGVnb3J5AAAAAAADAAAAAAAAAAtkZXNjcmlwdGlvbgAAAAPoAAAAEAAAAAAAAAACaWQAAAAAAAoAAAAAAAAABG5hbWUAAAAQ", + "AAAAAgAAAAAAAAAAAAAAB0RhdGFLZXkAAAAACgAAAAEAAAAAAAAABk1vZHVsZQAAAAAAAQAAABAAAAAAAAAAAAAAAAdDb3Vyc2VzAAAAAAEAAAAAAAAADkNvdXJzZUdvYWxMaXN0AAAAAAABAAAAEAAAAAEAAAAAAAAACkNvdXJzZUdvYWwAAAAAAAIAAAAQAAAAEAAAAAEAAAAAAAAAE0NvdXJzZVByZXJlcXVpc2l0ZXMAAAAAAQAAABAAAAAAAAAAAAAAAAtDYXRlZ29yeVNlcQAAAAABAAAAAAAAAA5Db3Vyc2VDYXRlZ29yeQAAAAAAAQAAAAoAAAAAAAAAAAAAAAZBZG1pbnMAAAAAAAAAAAAyS2V5IGZvciBzdG9yaW5nIGNvdXJzZSByYXRlIGxpbWl0aW5nIGNvbmZpZ3VyYXRpb24AAAAAABVDb3Vyc2VSYXRlTGltaXRDb25maWcAAAAAAAABAAAAVUtleSBmb3Igc3RvcmluZyBjb3Vyc2UgcmF0ZSBsaW1pdGluZyBkYXRhIHBlciBhZGRyZXNzOiBhZGRyZXNzIC0+IENvdXJzZVJhdGVMaW1pdERhdGEAAAAAAAAPQ291cnNlUmF0ZUxpbWl0AAAAAAEAAAAT", + "AAAAAQAAAAAAAAAAAAAABkNvdXJzZQAAAAAADQAAAAAAAAAIY2F0ZWdvcnkAAAPoAAAAEAAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAtkZXNjcmlwdGlvbgAAAAAQAAAAAAAAAA5kdXJhdGlvbl9ob3VycwAAAAAD6AAAAAQAAAAAAAAAAmlkAAAAAAAQAAAAAAAAAAtpc19hcmNoaXZlZAAAAAABAAAAAAAAAAhsYW5ndWFnZQAAA+gAAAAQAAAAAAAAAAVsZXZlbAAAAAAAA+gAAAfQAAAAC0NvdXJzZUxldmVsAAAAAAAAAAANcHJlcmVxdWlzaXRlcwAAAAAAA+oAAAfQAAAACENvdXJzZUlkAAAAAAAAAAVwcmljZQAAAAAAAAoAAAAAAAAACXB1Ymxpc2hlZAAAAAAAAAEAAAAAAAAADXRodW1ibmFpbF91cmwAAAAAAAPoAAAAEAAAAAAAAAAFdGl0bGUAAAAAAAAQ", + "AAAAAQAAAAAAAAAAAAAACENvdXJzZUlkAAAAAgAAAAAAAAAFY291bnQAAAAAAAAKAAAAAAAAAAJpZAAAAAAAEA==", + "AAAAAQAAAAAAAAAAAAAACENhdGVnb3J5AAAAAgAAAAAAAAAFY291bnQAAAAAAAAKAAAAAAAAAARuYW1lAAAAEA==", + "AAAAAQAAAAAAAAAAAAAADUNvdXJzZUZpbHRlcnMAAAAAAAAHAAAAAAAAAAhjYXRlZ29yeQAAA+gAAAAQAAAAAAAAAAVsZXZlbAAAAAAAA+gAAAfQAAAAC0NvdXJzZUxldmVsAAAAAAAAAAAMbWF4X2R1cmF0aW9uAAAD6AAAAAQAAAAAAAAACW1heF9wcmljZQAAAAAAA+gAAAAKAAAAAAAAAAxtaW5fZHVyYXRpb24AAAPoAAAABAAAAAAAAAAJbWluX3ByaWNlAAAAAAAD6AAAAAoAAAArVGV4dCBzZWFyY2ggaW4gY291cnNlIHRpdGxlIGFuZCBkZXNjcmlwdGlvbgAAAAALc2VhcmNoX3RleHQAAAAD6AAAABA=", + "AAAAAQAAAAAAAAAAAAAAEEVkaXRDb3Vyc2VQYXJhbXMAAAAJAAAAAAAAAAxuZXdfY2F0ZWdvcnkAAAPoAAAD6AAAABAAAAAAAAAAD25ld19kZXNjcmlwdGlvbgAAAAPoAAAAEAAAAAAAAAASbmV3X2R1cmF0aW9uX2hvdXJzAAAAAAPoAAAD6AAAAAQAAAAAAAAADG5ld19sYW5ndWFnZQAAA+gAAAPoAAAAEAAAAAAAAAAJbmV3X2xldmVsAAAAAAAD6AAAA+gAAAfQAAAAC0NvdXJzZUxldmVsAAAAAAAAAAAJbmV3X3ByaWNlAAAAAAAD6AAAAAoAAAAAAAAADW5ld19wdWJsaXNoZWQAAAAAAAPoAAAAAQAAAAAAAAARbmV3X3RodW1ibmFpbF91cmwAAAAAAAPoAAAD6AAAABAAAAAAAAAACW5ld190aXRsZQAAAAAAA+gAAAAQ", + "AAAAAQAAAJ5CYWNrdXAgZGF0YSBzdHJ1Y3R1cmUgZm9yIGNvdXJzZSByZWdpc3RyeSBzeXN0ZW0uCgpDb250YWlucyBhbGwgY291cnNlIGRhdGEsIGNhdGVnb3JpZXMsIG1vZHVsZXMsIGdvYWxzLCBhbmQgcHJlcmVxdWlzaXRlcwpmb3IgYmFja3VwIGFuZCByZWNvdmVyeSBvcGVyYXRpb25zLgAAAAAAAAAAABBDb3Vyc2VCYWNrdXBEYXRhAAAACQAAABdMaXN0IG9mIGFkbWluIGFkZHJlc3NlcwAAAAAGYWRtaW5zAAAAAAPqAAAAEwAAABBCYWNrdXAgdGltZXN0YW1wAAAAEGJhY2t1cF90aW1lc3RhbXAAAAAGAAAAIEJhY2t1cCB2ZXJzaW9uIGZvciBjb21wYXRpYmlsaXR5AAAADmJhY2t1cF92ZXJzaW9uAAAAAAAQAAAAFUFsbCBjb3Vyc2UgY2F0ZWdvcmllcwAAAAAAAApjYXRlZ29yaWVzAAAAAAPsAAAACgAAB9AAAAAOQ291cnNlQ2F0ZWdvcnkAAAAAABlDYXRlZ29yeSBzZXF1ZW5jZSBjb3VudGVyAAAAAAAADGNhdGVnb3J5X3NlcQAAAAoAAAAZQWxsIGNvdXJzZXMgaW4gdGhlIHN5c3RlbQAAAAAAAAdjb3Vyc2VzAAAAA+wAAAAQAAAH0AAAAAZDb3Vyc2UAAAAAAC9BbGwgY291cnNlIGdvYWxzIG1hcHBlZCBieSAoY291cnNlX2lkLCBnb2FsX2lkKQAAAAAFZ29hbHMAAAAAAAPsAAAAEAAAA+oAAAfQAAAACkNvdXJzZUdvYWwAAAAAABJBbGwgY291cnNlIG1vZHVsZXMAAAAAAAdtb2R1bGVzAAAAA+wAAAAQAAAH0AAAAAxDb3Vyc2VNb2R1bGUAAAAcQ291cnNlIHByZXJlcXVpc2l0ZXMgbWFwcGluZwAAAA1wcmVyZXF1aXNpdGVzAAAAAAAD7AAAABAAAAPqAAAH0AAAAAhDb3Vyc2VJZA==", + "AAAAAAAABABDcmVhdGUgYSBuZXcgY291cnNlIGluIHRoZSByZWdpc3RyeS4KClRoaXMgZnVuY3Rpb24gY3JlYXRlcyBhIG5ldyBjb3Vyc2Ugd2l0aCB0aGUgc3BlY2lmaWVkIG1ldGFkYXRhIGFuZApyZXR1cm5zIHRoZSBjcmVhdGVkIGNvdXJzZSBvYmplY3Qgd2l0aCBhIHVuaXF1ZSBpZGVudGlmaWVyLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjcmVhdG9yYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSBjb3Vyc2UgY3JlYXRvcgoqIGB0aXRsZWAgLSBUaGUgY291cnNlIHRpdGxlCiogYGRlc2NyaXB0aW9uYCAtIFRoZSBjb3Vyc2UgZGVzY3JpcHRpb24KKiBgcHJpY2VgIC0gVGhlIGNvdXJzZSBwcmljZSBpbiB0aGUgcGxhdGZvcm0ncyBjdXJyZW5jeQoqIGBjYXRlZ29yeWAgLSBPcHRpb25hbCBjb3Vyc2UgY2F0ZWdvcnkKKiBgbGFuZ3VhZ2VgIC0gT3B0aW9uYWwgY291cnNlIGxhbmd1YWdlCiogYHRodW1ibmFpbF91cmxgIC0gT3B0aW9uYWwgVVJMIGZvciB0aGUgY291cnNlIHRodW1ibmFpbCBpbWFnZQoqIGBsZXZlbGAgLSBPcHRpb25hbCBjb3Vyc2UgZGlmZmljdWx0eSBsZXZlbAoqIGBkdXJhdGlvbl9ob3Vyc2AgLSBPcHRpb25hbCBlc3RpbWF0ZWQgZHVyYXRpb24gaW4gaG91cnMKCiMgUmV0dXJucwoKUmV0dXJucyB0aGUgY3JlYXRlZCBgQ291cnNlYCBvYmplY3Qgd2l0aCBhbGwgbWV0YWRhdGEgYW5kIGEgdW5pcXVlIElELgoKIyBQYW5pY3MKCiogSWYgdGl0bGUgb3IgZGVzY3JpcHRpb24gYXJlIGVtcHR5CiogSWYgY3JlYXRvciBhZGRyZXNzIGlzIGludmFsaWQKKiBJZiBwcmljZSBleGNlZWRzIG1heGltdW0gYWxsb3dlZCB2YWx1ZQoKIyBFeGFtcGxlcwoKYGBgcnVzdApsZXQgY291cnNlID0gY29udHJhY3QuY3JlYXRlX2NvdXJzZSgKZW52LmNsb25lKCksCmluc3RydWN0b3JfYWRkcmVzcywKIlJ1c3QgUHJvZ3JhbW1pbmcgQmFzaWNzIi50cnlfaW50bygpLnVud3JhcCgpLAoiTGVhcm4gUnVzdCBmcm9tIHNjcmF0Y2giLnRyeV9pbnRvKCkudW53cmFwKCksCjUwAAAADWNyZWF0ZV9jb3Vyc2UAAAAAAAAJAAAAAAAAAAdjcmVhdG9yAAAAABMAAAAAAAAABXRpdGxlAAAAAAAAEAAAAAAAAAALZGVzY3JpcHRpb24AAAAAEAAAAAAAAAAFcHJpY2UAAAAAAAAKAAAAAAAAAAhjYXRlZ29yeQAAA+gAAAAQAAAAAAAAAAhsYW5ndWFnZQAAA+gAAAAQAAAAAAAAAA10aHVtYm5haWxfdXJsAAAAAAAD6AAAABAAAAAAAAAABWxldmVsAAAAAAAD6AAAB9AAAAALQ291cnNlTGV2ZWwAAAAAAAAAAA5kdXJhdGlvbl9ob3VycwAAAAAD6AAAAAQAAAABAAAH0AAAAAZDb3Vyc2UAAA==", + "AAAAAAAAA2lDcmVhdGUgYSBuZXcgY291cnNlIGNhdGVnb3J5LgoKVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IGNhdGVnb3J5IHRoYXQgY2FuIGJlIHVzZWQgdG8gY2xhc3NpZnkgY291cnNlcy4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2FsbGVyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIGNyZWF0aW5nIHRoZSBjYXRlZ29yeQoqIGBuYW1lYCAtIFRoZSBuYW1lIG9mIHRoZSBjYXRlZ29yeQoqIGBkZXNjcmlwdGlvbmAgLSBPcHRpb25hbCBkZXNjcmlwdGlvbiBvZiB0aGUgY2F0ZWdvcnkKCiMgUmV0dXJucwoKUmV0dXJucyB0aGUgdW5pcXVlIElEIG9mIHRoZSBjcmVhdGVkIGNhdGVnb3J5LgoKIyBQYW5pY3MKCiogSWYgY2F0ZWdvcnkgbmFtZSBpcyBlbXB0eQoqIElmIGNhdGVnb3J5IHdpdGggc2FtZSBuYW1lIGFscmVhZHkgZXhpc3RzCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIENyZWF0ZSBhIHByb2dyYW1taW5nIGNhdGVnb3J5CmxldCBjYXRlZ29yeV9pZCA9IGNvbnRyYWN0LmNyZWF0ZV9jb3Vyc2VfY2F0ZWdvcnkoCmVudi5jbG9uZSgpLAphZG1pbl9hZGRyZXNzLAoiUHJvZ3JhbW1pbmciLnRyeV9pbnRvKCkudW53cmFwKCksClNvbWUoIkNvbXB1dGVyIHByb2dyYW1taW5nIGNvdXJzZXMiLnRyeV9pbnRvKCkudW53cmFwKCkpCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqRHVwbGljYXRlIG5hbWVzKio6IENhbm5vdCBjcmVhdGUgY2F0ZWdvcmllcyB3aXRoIGV4aXN0aW5nIG5hbWVzCiogKipFbXB0eSBuYW1lcyoqOiBDYXRlZ29yeSBuYW1lIGNhbm5vdCBiZSBlbXB0eQoqICoqVW5pcXVlIElEcyoqOiBFYWNoIGNhdGVnb3J5IGdldHMgYSB1bmlxdWUgYXV0by1nZW5lcmF0ZWQgSUQAAAAAAAAWY3JlYXRlX2NvdXJzZV9jYXRlZ29yeQAAAAAAAwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAARuYW1lAAAAEAAAAAAAAAALZGVzY3JpcHRpb24AAAAD6AAAABAAAAABAAAACg==", + "AAAAAAAAAvVSZXRyaWV2ZSBhIGNvdXJzZSBieSBpdHMgSUQuCgpUaGlzIGZ1bmN0aW9uIGZldGNoZXMgYSBjb3Vyc2UncyBjb21wbGV0ZSBpbmZvcm1hdGlvbiB1c2luZyBpdHMgdW5pcXVlIGlkZW50aWZpZXIuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZSB0byByZXRyaWV2ZQoKIyBSZXR1cm5zCgpSZXR1cm5zIHRoZSBgQ291cnNlYCBvYmplY3QgY29udGFpbmluZyBhbGwgY291cnNlIG1ldGFkYXRhLgoKIyBQYW5pY3MKCiogSWYgY291cnNlIHdpdGggZ2l2ZW4gSUQgZG9lc24ndCBleGlzdAoqIElmIGNvdXJzZV9pZCBpcyBpbnZhbGlkIG9yIGVtcHR5CgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIEdldCBjb3Vyc2UgYnkgSUQKbGV0IGNvdXJzZSA9IGNvbnRyYWN0LmdldF9jb3Vyc2UoZW52LmNsb25lKCksICJjb3Vyc2VfMTIzIi50cnlfaW50bygpLnVud3JhcCgpKTsKcHJpbnRsbiEoIkNvdXJzZSB0aXRsZToge30iLCBjb3Vyc2UudGl0bGUpOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKk5vbi1leGlzdGVudCBjb3Vyc2UqKjogV2lsbCBwYW5pYyBpZiBjb3Vyc2UgSUQgZG9lc24ndCBleGlzdAoqICoqQXJjaGl2ZWQgY291cnNlcyoqOiBTdGlsbCByZXRyaWV2YWJsZSBidXQgbWFya2VkIGFzIGFyY2hpdmVkCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gcmV0cmlldmUgY291cnNlIGluZm9ybWF0aW9uAAAAAAAACmdldF9jb3Vyc2UAAAAAAAEAAAAAAAAACWNvdXJzZV9pZAAAAAAAABAAAAABAAAH0AAAAAZDb3Vyc2UAAA==", + "AAAAAAAAAtNSZXRyaWV2ZSBhIGNvdXJzZSBjYXRlZ29yeSBieSBpdHMgSUQuCgpUaGlzIGZ1bmN0aW9uIGZldGNoZXMgYSBjYXRlZ29yeSdzIGluZm9ybWF0aW9uIHVzaW5nIGl0cyB1bmlxdWUgaWRlbnRpZmllci4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2F0ZWdvcnlfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBjYXRlZ29yeSB0byByZXRyaWV2ZQoKIyBSZXR1cm5zCgpSZXR1cm5zIGBTb21lKENvdXJzZUNhdGVnb3J5KWAgaWYgZm91bmQsIGBOb25lYCBpZiB0aGUgY2F0ZWdvcnkgZG9lc24ndCBleGlzdC4KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gR2V0IGNhdGVnb3J5IGJ5IElECmlmIGxldCBTb21lKGNhdGVnb3J5KSA9IGNvbnRyYWN0LmdldF9jb3Vyc2VfY2F0ZWdvcnkoZW52LmNsb25lKCksIDEpIHsKcHJpbnRsbiEoIkNhdGVnb3J5OiB7fSIsIGNhdGVnb3J5Lm5hbWUpOwp9IGVsc2UgewpwcmludGxuISgiQ2F0ZWdvcnkgbm90IGZvdW5kIik7Cn0KYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipOb24tZXhpc3RlbnQgY2F0ZWdvcnkqKjogUmV0dXJucyBgTm9uZWAgaW5zdGVhZCBvZiBwYW5pY2tpbmcKKiAqKkludmFsaWQgSUQqKjogUmV0dXJucyBgTm9uZWAgZm9yIGludmFsaWQgY2F0ZWdvcnkgSURzCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gcmV0cmlldmUgY2F0ZWdvcnkgaW5mb3JtYXRpb24AAAAAE2dldF9jb3Vyc2VfY2F0ZWdvcnkAAAAAAQAAAAAAAAALY2F0ZWdvcnlfaWQAAAAACgAAAAEAAAPoAAAH0AAAAA5Db3Vyc2VDYXRlZ29yeQAA", + "AAAAAAAAA0BHZXQgYWxsIGNvdXJzZXMgY3JlYXRlZCBieSBhIHNwZWNpZmljIGluc3RydWN0b3IuCgpUaGlzIGZ1bmN0aW9uIHJldHJpZXZlcyBhbGwgY291cnNlcyB0aGF0IHdlcmUgY3JlYXRlZCBieSB0aGUgc3BlY2lmaWVkIGluc3RydWN0b3IuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGluc3RydWN0b3JgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIGluc3RydWN0b3IgdG8gcXVlcnkgY291cnNlcyBmb3IKCiMgUmV0dXJucwoKUmV0dXJucyBhIHZlY3RvciBvZiBgQ291cnNlYCBvYmplY3RzIGNyZWF0ZWQgYnkgdGhlIGluc3RydWN0b3IuCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIEdldCBhbGwgY291cnNlcyBieSBhbiBpbnN0cnVjdG9yCmxldCBpbnN0cnVjdG9yX2NvdXJzZXMgPSBjb250cmFjdC5nZXRfY291cnNlc19ieV9pbnN0cnVjdG9yKGVudi5jbG9uZSgpLCBpbnN0cnVjdG9yX2FkZHJlc3MpOwpmb3IgY291cnNlIGluIGluc3RydWN0b3JfY291cnNlcyB7CnByaW50bG4hKCJDb3Vyc2U6IHt9IiwgY291cnNlLnRpdGxlKTsKfQpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKk5vIGNvdXJzZXMqKjogUmV0dXJucyBlbXB0eSB2ZWN0b3IgaWYgaW5zdHJ1Y3RvciBoYXMgbm8gY291cnNlcwoqICoqQXJjaGl2ZWQgY291cnNlcyoqOiBJbmNsdWRlcyBhcmNoaXZlZCBjb3Vyc2VzIGluIHJlc3VsdHMKKiAqKlB1YmxpYyBhY2Nlc3MqKjogQW55b25lIGNhbiBxdWVyeSBpbnN0cnVjdG9yIGNvdXJzZXMKKiAqKkludmFsaWQgaW5zdHJ1Y3RvcioqOiBSZXR1cm5zIGVtcHR5IHZlY3RvciBmb3Igbm9uLWV4aXN0ZW50IGluc3RydWN0b3JzAAAAGWdldF9jb3Vyc2VzX2J5X2luc3RydWN0b3IAAAAAAAABAAAAAAAAAAppbnN0cnVjdG9yAAAAAAATAAAAAQAAA+oAAAfQAAAABkNvdXJzZQAA", + "AAAAAAAAA7RSZW1vdmUgYSBtb2R1bGUgZnJvbSBhIGNvdXJzZS4KClRoaXMgZnVuY3Rpb24gcmVtb3ZlcyBhIHNwZWNpZmljIG1vZHVsZSBmcm9tIGl0cyBhc3NvY2lhdGVkIGNvdXJzZS4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgbW9kdWxlX2lkYCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgbW9kdWxlIHRvIHJlbW92ZQoKIyBQYW5pY3MKClJlbW92ZSBhIG1vZHVsZSBmcm9tIGEgY291cnNlLgoKVGhpcyBmdW5jdGlvbiByZW1vdmVzIGEgc3BlY2lmaWMgbW9kdWxlIGZyb20gaXRzIGFzc29jaWF0ZWQgY291cnNlLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBtb2R1bGVfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBtb2R1bGUgdG8gcmVtb3ZlCgojIFBhbmljcwoKKiBJZiB0aGUgbW9kdWxlIGRvZXNuJ3QgZXhpc3QKKiBJZiB0aGUgbW9kdWxlX2lkIGlzIGludmFsaWQgb3IgZW1wdHkKKiBJZiBtb2R1bGUgcmVtb3ZhbCBvcGVyYXRpb24gZmFpbHMKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gUmVtb3ZlIGEgbW9kdWxlIGZyb20gYSBjb3Vyc2UKY29udHJhY3QucmVtb3ZlX21vZHVsZShlbnYuY2xvbmUoKSwgIm1vZHVsZV8xMjMiLnRyeV9pbnRvKCkudW53cmFwKCkpOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKk5vbi1leGlzdGVudCBtb2R1bGUqKjogV2lsbCBwYW5pYyBpZiBtb2R1bGUgSUQgZG9lc24ndCBleGlzdAoqICoqSW52YWxpZCBJRCoqOiBXaWxsIHBhbmljIGZvciBpbnZhbGlkIG9yIGVtcHR5IG1vZHVsZSBJRHMKKiAqKkNvdXJzZSB1cGRhdGVzKio6IEF1dG9tYXRpY2FsbHkgdXBkYXRlcyBjb3Vyc2UgbW9kdWxlIGNvdW50CgpQYW5pY3MgaWYgdGhlIG1vZHVsZSByZW1vdmFsIGZhaWxzIG9yIGlmIHRoZSBtb2R1bGUgZG9lc24ndCBleGlzdC4AAAANcmVtb3ZlX21vZHVsZQAAAAAAAAEAAAAAAAAACW1vZHVsZV9pZAAAAAAAABAAAAAA", + "AAAAAAAAA9lBZGQgYSBuZXcgbW9kdWxlIHRvIGEgY291cnNlLgoKVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGFuZCBhZGRzIGEgbmV3IG1vZHVsZSB0byB0aGUgc3BlY2lmaWVkIGNvdXJzZQphdCB0aGUgZ2l2ZW4gcG9zaXRpb24uCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZSB0byBhZGQgdGhlIG1vZHVsZSB0bwoqIGBwb3NpdGlvbmAgLSBUaGUgcG9zaXRpb24gd2hlcmUgdGhlIG1vZHVsZSBzaG91bGQgYmUgaW5zZXJ0ZWQKKiBgdGl0bGVgIC0gVGhlIHRpdGxlIG9mIHRoZSBuZXcgbW9kdWxlCgojIFJldHVybnMKClJldHVybnMgdGhlIGNyZWF0ZWQgYENvdXJzZU1vZHVsZWAgb2JqZWN0LgoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBjYWxsZXIgaXMgbm90IHRoZSBjb3Vyc2UgY3JlYXRvcgoqIElmIG1vZHVsZSB0aXRsZSBpcyBlbXB0eQoqIElmIHBvc2l0aW9uIGlzIGludmFsaWQKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gQWRkIGEgbW9kdWxlIGF0IHBvc2l0aW9uIDEKbGV0IG1vZHVsZSA9IGNvbnRyYWN0LmFkZF9tb2R1bGUoCmVudi5jbG9uZSgpLApjb3Vyc2VfY3JlYXRvcl9hZGRyZXNzLAoiY291cnNlXzEyMyIudHJ5X2ludG8oKS51bndyYXAoKSwKMSwKIkludHJvZHVjdGlvbiB0byBWYXJpYWJsZXMiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipJbnZhbGlkIHBvc2l0aW9uKio6IFBvc2l0aW9uIG11c3QgYmUgdmFsaWQgZm9yIHRoZSBjb3Vyc2UKKiAqKkVtcHR5IHRpdGxlKio6IE1vZHVsZSB0aXRsZSBjYW5ub3QgYmUgZW1wdHkKKiAqKkNyZWF0b3Igb25seSoqOiBPbmx5IGNvdXJzZSBjcmVhdG9yIGNhbiBhZGQgbW9kdWxlcwoqICoqQXV0by1nZW5lcmF0ZWQgSUQqKjogTW9kdWxlIGdldHMgdW5pcXVlIGF1dG8tZ2VuZXJhdGVkIElEAAAAAAAACmFkZF9tb2R1bGUAAAAAAAQAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAIcG9zaXRpb24AAAAEAAAAAAAAAAV0aXRsZQAAAAAAABAAAAABAAAH0AAAAAxDb3Vyc2VNb2R1bGU=", + "AAAAAAAAA8BEZWxldGUgYSBjb3Vyc2UgZnJvbSB0aGUgcmVnaXN0cnkuCgpUaGlzIGZ1bmN0aW9uIHBlcm1hbmVudGx5IHJlbW92ZXMgYSBjb3Vyc2UgZnJvbSB0aGUgcmVnaXN0cnkuCk9ubHkgdGhlIGNvdXJzZSBjcmVhdG9yIGNhbiBkZWxldGUgdGhlaXIgb3duIGNvdXJzZXMuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNyZWF0b3JgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIGNvdXJzZSBjcmVhdG9yCiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZSB0byBkZWxldGUKCiMgUGFuaWNzCgoqIElmIGNvdXJzZSBkb2Vzbid0IGV4aXN0CiogSWYgY3JlYXRvciBpcyBub3QgdGhlIGFjdHVhbCBjb3Vyc2UgY3JlYXRvcgoqIElmIGNvdXJzZV9pZCBpcyBpbnZhbGlkIG9yIGVtcHR5CgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIENvdXJzZSBjcmVhdG9yIGRlbGV0aW5nIHRoZWlyIGNvdXJzZQpjb250cmFjdC5kZWxldGVfY291cnNlKGVudi5jbG9uZSgpLCBjb3Vyc2VfY3JlYXRvcl9hZGRyZXNzLCAiY291cnNlXzEyMyIudHJ5X2ludG8oKS51bndyYXAoKSk7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqUGVybWlzc2lvbiBkZW5pZWQqKjogT25seSBjb3Vyc2UgY3JlYXRvciBjYW4gZGVsZXRlIHRoZWlyIGNvdXJzZXMKKiAqKk5vbi1leGlzdGVudCBjb3Vyc2UqKjogV2lsbCBwYW5pYyBpZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqICoqUGVybWFuZW50IGRlbGV0aW9uKio6IENvdXJzZSBhbmQgYWxsIGFzc29jaWF0ZWQgZGF0YSBhcmUgcGVybWFuZW50bHkgcmVtb3ZlZAoqICoqRW5yb2xsZWQgc3R1ZGVudHMqKjogQ29uc2lkZXIgaW1wYWN0IG9uIGVucm9sbGVkIHN0dWRlbnRzIGJlZm9yZSBkZWxldGlvbgoKUGFuaWNzIGlmIHRoZSBkZWxldGlvbiBmYWlscyBvciBpZiB0aGUgY3JlYXRvciBpcyBub3QgYXV0aG9yaXplZC4AAAANZGVsZXRlX2NvdXJzZQAAAAAAAAIAAAAAAAAAB2NyZWF0b3IAAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAA=", + "AAAAAAAAAldTaW1wbGUgaGVsbG8gd29ybGQgZnVuY3Rpb24gZm9yIHRlc3RpbmcuCgpUaGlzIGlzIGEgYmFzaWMgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgZ3JlZXRpbmcgbWVzc2FnZSwKcHJpbWFyaWx5IHVzZWQgZm9yIHRlc3RpbmcgY29udHJhY3QgZGVwbG95bWVudCBhbmQgYmFzaWMgZnVuY3Rpb25hbGl0eS4KCiMgQXJndW1lbnRzCgoqIGBfZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50ICh1bnVzZWQpCgojIFJldHVybnMKClJldHVybnMgYSBncmVldGluZyBzdHJpbmcuCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIFRlc3QgY29udHJhY3QgZGVwbG95bWVudApsZXQgZ3JlZXRpbmcgPSBjb250cmFjdC5oZWxsb193b3JsZChlbnYuY2xvbmUoKSk7CmFzc2VydF9lcSEoZ3JlZXRpbmcsICJIZWxsbyBmcm9tIFdlYjMg8J+RiyIpOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKkFsd2F5cyBzdWNjZWVkcyoqOiBUaGlzIGZ1bmN0aW9uIG5ldmVyIGZhaWxzCiogKipObyBkZXBlbmRlbmNpZXMqKjogUmVxdWlyZXMgbm8gZXh0ZXJuYWwgZGF0YSBvciBzdGF0ZQoqICoqVGVzdGluZyBvbmx5Kio6IFByaW1hcmlseSBpbnRlbmRlZCBmb3IgY29udHJhY3QgdGVzdGluZwAAAAALaGVsbG9fd29ybGQAAAAAAAAAAAEAAAAQ", + "AAAAAAAABABFZGl0IGFuIGV4aXN0aW5nIGNvdXJzZSBnb2FsLgoKVGhpcyBmdW5jdGlvbiBhbGxvd3MgdGhlIGNvdXJzZSBjcmVhdG9yIHRvIG1vZGlmeSB0aGUgY29udGVudCBvZiBhbiBleGlzdGluZyBnb2FsLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjcmVhdG9yYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSBjb3Vyc2UgY3JlYXRvcgoqIGBjb3Vyc2VfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBjb3Vyc2UKKiBgZ29hbF9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGdvYWwgdG8gZWRpdAoqIGBuZXdfY29udGVudGAgLSBUaGUgbmV3IGNvbnRlbnQgZm9yIHRoZSBnb2FsCgojIFJldHVybnMKClJldHVybnMgdGhlIHVwZGF0ZWQgYENvdXJzZUdvYWxgIG9iamVjdC4KCiMgUGFuaWNzCgoqIElmIGNvdXJzZSBkb2Vzbid0IGV4aXN0CiogSWYgZ29hbCBkb2Vzbid0IGV4aXN0CiogSWYgY3JlYXRvciBpcyBub3QgdGhlIGNvdXJzZSBjcmVhdG9yCiogSWYgbmV3X2NvbnRlbnQgaXMgZW1wdHkKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gRWRpdCBhIGNvdXJzZSBnb2FsCmxldCB1cGRhdGVkX2dvYWwgPSBjb250cmFjdC5lZGl0X2dvYWwoCmVudi5jbG9uZSgpLApjb3Vyc2VfY3JlYXRvcl9hZGRyZXNzLAoiY291cnNlXzEyMyIudHJ5X2ludG8oKS51bndyYXAoKSwKImdvYWxfNDU2Ii50cnlfaW50bygpLnVud3JhcCgpLAoiVXBkYXRlZCBsZWFybmluZyBvYmplY3RpdmUiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipFbXB0eSBjb250ZW50Kio6IE5ldyBjb250ZW50IGNhbm5vdCBiZSBlbXB0eQoqICoqQ3JlYXRvciBvbmx5Kio6IE9ubHkgY291cnNlIGNyZWF0b3IgY2FuIGVkaXQgZ29hbHMKKiAqKk5vbi1leGlzdGVudCBnb2FsKio6IFdpbGwgcGFuaWMgaWYgZ29hbCBJRCBkb2Vzbid0IGV4aXN0CiogKipDb250ZW50IHZhbGlkYXRpb24qKjogTmV3IGNvbnRlbnQgbXVzdCBtZWV0IHZhbGlkYXRpb24gAAAACWVkaXRfZ29hbAAAAAAAAAQAAAAAAAAAB2NyZWF0b3IAAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAHZ29hbF9pZAAAAAAQAAAAAAAAAAtuZXdfY29udGVudAAAAAAQAAAAAQAAB9AAAAAKQ291cnNlR29hbAAA", + "AAAAAAAAA6pBZGQgYSBuZXcgZ29hbCB0byBhIGNvdXJzZS4KClRoaXMgZnVuY3Rpb24gY3JlYXRlcyBhbmQgYWRkcyBhIG5ldyBsZWFybmluZyBnb2FsIHRvIHRoZSBzcGVjaWZpZWQgY291cnNlLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjcmVhdG9yYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSBjb3Vyc2UgY3JlYXRvcgoqIGBjb3Vyc2VfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBjb3Vyc2UKKiBgY29udGVudGAgLSBUaGUgY29udGVudC9kZXNjcmlwdGlvbiBvZiB0aGUgZ29hbAoKIyBSZXR1cm5zCgpSZXR1cm5zIHRoZSBjcmVhdGVkIGBDb3Vyc2VHb2FsYCBvYmplY3QuCgojIFBhbmljcwoKKiBJZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqIElmIGNyZWF0b3IgaXMgbm90IHRoZSBjb3Vyc2UgY3JlYXRvcgoqIElmIGNvbnRlbnQgaXMgZW1wdHkKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gQWRkIGEgbGVhcm5pbmcgZ29hbCB0byBhIGNvdXJzZQpsZXQgZ29hbCA9IGNvbnRyYWN0LmFkZF9nb2FsKAplbnYuY2xvbmUoKSwKY291cnNlX2NyZWF0b3JfYWRkcmVzcywKImNvdXJzZV8xMjMiLnRyeV9pbnRvKCkudW53cmFwKCksCiJTdHVkZW50cyB3aWxsIGxlYXJuIGJhc2ljIHByb2dyYW1taW5nIGNvbmNlcHRzIi50cnlfaW50bygpLnVud3JhcCgpCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqRW1wdHkgY29udGVudCoqOiBHb2FsIGNvbnRlbnQgY2Fubm90IGJlIGVtcHR5CiogKipDcmVhdG9yIG9ubHkqKjogT25seSBjb3Vyc2UgY3JlYXRvciBjYW4gYWRkIGdvYWxzCiogKipBdXRvLWdlbmVyYXRlZCBJRCoqOiBHb2FsIGdldHMgdW5pcXVlIGF1dG8tZ2VuZXJhdGVkIElECiogKipDb250ZW50IHZhbGlkYXRpb24qKjogR29hbCBjb250ZW50IG11c3QgbWVldCB2YWxpZGF0aW9uIHJlcXVpcmVtZW50cwAAAAAACGFkZF9nb2FsAAAAAwAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAljb3Vyc2VfaWQAAAAAAAAQAAAAAAAAAAdjb250ZW50AAAAABAAAAABAAAH0AAAAApDb3Vyc2VHb2FsAAA=", + "AAAAAAAAA1FSZW1vdmUgYSBnb2FsIGZyb20gYSBjb3Vyc2UuCgpUaGlzIGZ1bmN0aW9uIHJlbW92ZXMgYSBzcGVjaWZpYyBsZWFybmluZyBnb2FsIGZyb20gdGhlIGNvdXJzZS4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2FsbGVyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIHJlcXVlc3RpbmcgdGhlIHJlbW92YWwKKiBgY291cnNlX2lkYCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgY291cnNlCiogYGdvYWxfaWRgIC0gVGhlIHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBnb2FsIHRvIHJlbW92ZQoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBnb2FsIGRvZXNuJ3QgZXhpc3QKKiBJZiBjYWxsZXIgaXMgbm90IHRoZSBjb3Vyc2UgY3JlYXRvcgoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBSZW1vdmUgYSBnb2FsIGZyb20gYSBjb3Vyc2UKY29udHJhY3QucmVtb3ZlX2dvYWwoCmVudi5jbG9uZSgpLApjb3Vyc2VfY3JlYXRvcl9hZGRyZXNzLAoiY291cnNlXzEyMyIudHJ5X2ludG8oKS51bndyYXAoKSwKImdvYWxfNDU2Ii50cnlfaW50bygpLnVud3JhcCgpCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqQ3JlYXRvciBvbmx5Kio6IE9ubHkgY291cnNlIGNyZWF0b3IgY2FuIHJlbW92ZSBnb2FscwoqICoqTm9uLWV4aXN0ZW50IGdvYWwqKjogV2lsbCBwYW5pYyBpZiBnb2FsIElEIGRvZXNuJ3QgZXhpc3QKKiAqKlBlcm1hbmVudCByZW1vdmFsKio6IEdvYWwgaXMgcGVybWFuZW50bHkgZGVsZXRlZCBmcm9tIGNvdXJzZQoqICoqR29hbCBjb3VudCoqOiBBdXRvbWF0aWNhbGx5IHVwZGF0ZXMgY291cnNlIGdvYWwgY291bnQAAAAAAAALcmVtb3ZlX2dvYWwAAAAAAwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAljb3Vyc2VfaWQAAAAAAAAQAAAAAAAAAAdnb2FsX2lkAAAAABAAAAAA", + "AAAAAAAABABBZGQgcHJlcmVxdWlzaXRlcyB0byBhIGNvdXJzZS4KClRoaXMgZnVuY3Rpb24gYWRkcyBwcmVyZXF1aXNpdGUgY291cnNlcyB0aGF0IG11c3QgYmUgY29tcGxldGVkCmJlZm9yZSBhIHN0dWRlbnQgY2FuIGVucm9sbCBpbiB0aGUgdGFyZ2V0IGNvdXJzZS4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY3JlYXRvcmAgLSBUaGUgYWRkcmVzcyBvZiB0aGUgY291cnNlIGNyZWF0b3IKKiBgY291cnNlX2lkYCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgY291cnNlCiogYHByZXJlcXVpc2l0ZV9jb3Vyc2VfaWRzYCAtIFZlY3RvciBvZiBjb3Vyc2UgSURzIHRoYXQgYXJlIHByZXJlcXVpc2l0ZXMKCiMgUGFuaWNzCgoqIElmIGNvdXJzZSBkb2Vzbid0IGV4aXN0CiogSWYgY3JlYXRvciBpcyBub3QgdGhlIGNvdXJzZSBjcmVhdG9yCiogSWYgYW55IHByZXJlcXVpc2l0ZSBjb3Vyc2UgZG9lc24ndCBleGlzdAoqIElmIHRyeWluZyB0byBhZGQgc2VsZiBhcyBwcmVyZXF1aXNpdGUKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKbGV0IG11dCBwcmVyZXF1aXNpdGVzID0gVmVjOjpuZXcoJmVudik7CnByZXJlcXVpc2l0ZXMucHVzaF9iYWNrKCJiYXNpY19ydXN0Ii50cnlfaW50bygpLnVud3JhcCgpKTsKcHJlcmVxdWlzaXRlcy5wdXNoX2JhY2soInByb2dyYW1taW5nX2Z1bmRhbWVudGFscyIudHJ5X2ludG8oKS51bndyYXAoKSk7Cgpjb250cmFjdC5hZGRfcHJlcmVxdWlzaXRlKAplbnYuY2xvbmUoKSwKY291cnNlX2NyZWF0b3JfYWRkcmVzcywKImFkdmFuY2VkX3J1c3QiLnRyeV9pbnRvKCkudW53cmFwKCksCnByZXJlcXVpc2l0ZXMKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipDaXJjdWxhciBkZXBlbmRlbmNpZXMqKjogQ2Fubm90IGFkZCBzZWxmIGFzIHByZXJlcXVpc2l0ZQoqICoqTm9uLWV4aXN0ZW50IGNvdXJzZXMqKjogQWxsIHByZXJlcXVpc2l0ZSBjb3Vyc2VzIG11c3QgZXhpc3QKKiAqKkNyZWF0b3Igb25seSoqOiBPbmx5IGNvdXJzZSBjcmVhdG9yIGNhAAAAEGFkZF9wcmVyZXF1aXNpdGUAAAADAAAAAAAAAAdjcmVhdG9yAAAAABMAAAAAAAAACWNvdXJzZV9pZAAAAAAAABAAAAAAAAAAF3ByZXJlcXVpc2l0ZV9jb3Vyc2VfaWRzAAAAA+oAAAAQAAAAAA==", + "AAAAAAAAA7lSZW1vdmUgYSBwcmVyZXF1aXNpdGUgZnJvbSBhIGNvdXJzZS4KClRoaXMgZnVuY3Rpb24gcmVtb3ZlcyBhIHNwZWNpZmljIHByZXJlcXVpc2l0ZSBjb3Vyc2UgcmVxdWlyZW1lbnQKZnJvbSB0aGUgdGFyZ2V0IGNvdXJzZS4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY3JlYXRvcmAgLSBUaGUgYWRkcmVzcyBvZiB0aGUgY291cnNlIGNyZWF0b3IKKiBgY291cnNlX2lkYCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgY291cnNlCiogYHByZXJlcXVpc2l0ZV9jb3Vyc2VfaWRgIC0gVGhlIElEIG9mIHRoZSBwcmVyZXF1aXNpdGUgY291cnNlIHRvIHJlbW92ZQoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBjcmVhdG9yIGlzIG5vdCB0aGUgY291cnNlIGNyZWF0b3IKKiBJZiBwcmVyZXF1aXNpdGUgZG9lc24ndCBleGlzdCBmb3IgdGhlIGNvdXJzZQoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBSZW1vdmUgYSBwcmVyZXF1aXNpdGUgZnJvbSBhIGNvdXJzZQpjb250cmFjdC5yZW1vdmVfcHJlcmVxdWlzaXRlKAplbnYuY2xvbmUoKSwKY291cnNlX2NyZWF0b3JfYWRkcmVzcywKImFkdmFuY2VkX3J1c3QiLnRyeV9pbnRvKCkudW53cmFwKCksCiJiYXNpY19ydXN0Ii50cnlfaW50bygpLnVud3JhcCgpCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqTm9uLWV4aXN0ZW50IHByZXJlcXVpc2l0ZSoqOiBXaWxsIHBhbmljIGlmIHByZXJlcXVpc2l0ZSBkb2Vzbid0IGV4aXN0CiogKipDcmVhdG9yIG9ubHkqKjogT25seSBjb3Vyc2UgY3JlYXRvciBjYW4gcmVtb3ZlIHByZXJlcXVpc2l0ZXMKKiAqKk5vIGVmZmVjdCoqOiBSZW1vdmluZyBub24tZXhpc3RlbnQgcHJlcmVxdWlzaXRlIGhhcyBubyBlZmZlY3QKKiAqKlN0dWRlbnQgaW1wYWN0Kio6IENvbnNpZGVyIGltcGFjdCBvbiBlbnJvbGxlZCBzdHVkZW50cwAAAAAAABNyZW1vdmVfcHJlcmVxdWlzaXRlAAAAAAMAAAAAAAAAB2NyZWF0b3IAAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAWcHJlcmVxdWlzaXRlX2NvdXJzZV9pZAAAAAAAEAAAAAA=", + "AAAAAAAABABFZGl0IHRoZSBwcmVyZXF1aXNpdGVzIGZvciBhIGNvdXJzZS4KClRoaXMgZnVuY3Rpb24gcmVwbGFjZXMgYWxsIGV4aXN0aW5nIHByZXJlcXVpc2l0ZXMgd2l0aCBhIG5ldyBzZXQKb2YgcHJlcmVxdWlzaXRlIGNvdXJzZXMuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNyZWF0b3JgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIGNvdXJzZSBjcmVhdG9yCiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQoqIGBuZXdfcHJlcmVxdWlzaXRlc2AgLSBWZWN0b3Igb2YgbmV3IHByZXJlcXVpc2l0ZSBjb3Vyc2UgSURzCgojIFBhbmljcwoKKiBJZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqIElmIGNyZWF0b3IgaXMgbm90IHRoZSBjb3Vyc2UgY3JlYXRvcgoqIElmIGFueSBwcmVyZXF1aXNpdGUgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiB0cnlpbmcgdG8gYWRkIHNlbGYgYXMgcHJlcmVxdWlzaXRlCgojIEV4YW1wbGVzCgpgYGBydXN0CmxldCBtdXQgbmV3X3ByZXJlcXVpc2l0ZXMgPSBWZWM6Om5ldygmZW52KTsKbmV3X3ByZXJlcXVpc2l0ZXMucHVzaF9iYWNrKCJ1cGRhdGVkX2NvdXJzZV8xIi50cnlfaW50bygpLnVud3JhcCgpKTsKbmV3X3ByZXJlcXVpc2l0ZXMucHVzaF9iYWNrKCJ1cGRhdGVkX2NvdXJzZV8yIi50cnlfaW50bygpLnVud3JhcCgpKTsKCmNvbnRyYWN0LmVkaXRfcHJlcmVxdWlzaXRlKAplbnYuY2xvbmUoKSwKY291cnNlX2NyZWF0b3JfYWRkcmVzcywKInRhcmdldF9jb3Vyc2UiLnRyeV9pbnRvKCkudW53cmFwKCksCm5ld19wcmVyZXF1aXNpdGVzCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqQ29tcGxldGUgcmVwbGFjZW1lbnQqKjogQWxsIG9sZCBwcmVyZXF1aXNpdGVzIGFyZSByZW1vdmVkCiogKipFbXB0eSB2ZWN0b3IqKjogQ2FuIGNsZWFyIGFsbCBwcmVyZXF1aXNpdGVzIHdpdGggZW1wdHkgdmVjdG9yCiogKipDaXJjdWxhciBkZXBlbmRlbmNpZXMqKjogQ2Fubm90IGFkZCBzZWxmIGFzIHByZXJlAAAAEWVkaXRfcHJlcmVxdWlzaXRlAAAAAAAAAwAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAljb3Vyc2VfaWQAAAAAAAAQAAAAAAAAABFuZXdfcHJlcmVxdWlzaXRlcwAAAAAAA+oAAAAQAAAAAA==", + "AAAAAAAABABFZGl0IGNvdXJzZSBpbmZvcm1hdGlvbi4KClRoaXMgZnVuY3Rpb24gYWxsb3dzIHRoZSBjb3Vyc2UgY3JlYXRvciB0byB1cGRhdGUgdmFyaW91cyBhc3BlY3RzCm9mIHRoZSBjb3Vyc2UgdXNpbmcgdGhlIHByb3ZpZGVkIHBhcmFtZXRlcnMuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNyZWF0b3JgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIGNvdXJzZSBjcmVhdG9yCiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZSB0byBlZGl0CiogYHBhcmFtc2AgLSBQYXJhbWV0ZXJzIGNvbnRhaW5pbmcgdGhlIGZpZWxkcyB0byB1cGRhdGUKCiMgUmV0dXJucwoKUmV0dXJucyB0aGUgdXBkYXRlZCBgQ291cnNlYCBvYmplY3QuCgojIFBhbmljcwoKKiBJZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoqIElmIGNyZWF0b3IgaXMgbm90IHRoZSBjb3Vyc2UgY3JlYXRvcgoqIElmIGFueSBmaWVsZCB2YWxpZGF0aW9uIGZhaWxzCgojIEV4YW1wbGVzCgpgYGBydXN0CmxldCBwYXJhbXMgPSBFZGl0Q291cnNlUGFyYW1zIHsKdGl0bGU6IFNvbWUoIlVwZGF0ZWQgQ291cnNlIFRpdGxlIi50cnlfaW50bygpLnVud3JhcCgpKSwKZGVzY3JpcHRpb246IFNvbWUoIlVwZGF0ZWQgZGVzY3JpcHRpb24iLnRyeV9pbnRvKCkudW53cmFwKCkpLApwcmljZTogU29tZSg3NTAwKSwKbGV2ZWw6IFNvbWUoQ291cnNlTGV2ZWw6OkludGVybWVkaWF0ZSksCi4uRGVmYXVsdDo6ZGVmYXVsdCgpCn07CgpsZXQgdXBkYXRlZF9jb3Vyc2UgPSBjb250cmFjdC5lZGl0X2NvdXJzZSgKZW52LmNsb25lKCksCmNvdXJzZV9jcmVhdG9yX2FkZHJlc3MsCiJjb3Vyc2VfMTIzIi50cnlfaW50bygpLnVud3JhcCgpLApwYXJhbXMKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipQYXJ0aWFsIHVwZGF0ZXMqKjogT25seSBwcm92aWRlZCBmaWVsZHMgYXJlIHVwZGF0ZWQKKiAqKlZhbGlkYXRpb24qKjogQWxsIGZpZWxkcyBtdXN0IHBhc3MgdmFsaWRhdGlvbiBydWxlcwoqICoqQ3JlAAAAC2VkaXRfY291cnNlAAAAAAMAAAAAAAAAB2NyZWF0b3IAAAAAEwAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAGcGFyYW1zAAAAAAfQAAAAEEVkaXRDb3Vyc2VQYXJhbXMAAAABAAAH0AAAAAZDb3Vyc2UAAA==", + "AAAAAAAAA4pBcmNoaXZlIGEgY291cnNlLgoKVGhpcyBmdW5jdGlvbiBtYXJrcyBhIGNvdXJzZSBhcyBhcmNoaXZlZCwgbWFraW5nIGl0IHVuYXZhaWxhYmxlIGZvciBuZXcgZW5yb2xsbWVudHMKd2hpbGUgcHJlc2VydmluZyBleGlzdGluZyBkYXRhIGFuZCBhY2Nlc3MgZm9yIGN1cnJlbnQgc3R1ZGVudHMuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNyZWF0b3JgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIGNvdXJzZSBjcmVhdG9yCiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZSB0byBhcmNoaXZlCgojIFJldHVybnMKClJldHVybnMgdGhlIHVwZGF0ZWQgYENvdXJzZWAgb2JqZWN0IHdpdGggYXJjaGl2ZWQgc3RhdHVzLgoKIyBQYW5pY3MKCiogSWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiBJZiBjcmVhdG9yIGlzIG5vdCB0aGUgY291cnNlIGNyZWF0b3IKKiBJZiBjb3Vyc2UgaXMgYWxyZWFkeSBhcmNoaXZlZAoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBBcmNoaXZlIGEgY291cnNlCmxldCBhcmNoaXZlZF9jb3Vyc2UgPSBjb250cmFjdC5hcmNoaXZlX2NvdXJzZSgKJmVudiwKY291cnNlX2NyZWF0b3JfYWRkcmVzcywKImNvdXJzZV8xMjMiLnRyeV9pbnRvKCkudW53cmFwKCkKKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipBbHJlYWR5IGFyY2hpdmVkKio6IFdpbGwgcGFuaWMgaWYgY291cnNlIGlzIGFscmVhZHkgYXJjaGl2ZWQKKiAqKkNyZWF0b3Igb25seSoqOiBPbmx5IGNvdXJzZSBjcmVhdG9yIGNhbiBhcmNoaXZlIGNvdXJzZQoqICoqU3R1ZGVudCBhY2Nlc3MqKjogQ3VycmVudCBzdHVkZW50cyByZXRhaW4gYWNjZXNzCiogKipSZXZlcnNpYmxlKio6IENvdXJzZSBjYW4gYmUgdW5hcmNoaXZlZCBpZiBuZWVkZWQAAAAAAA5hcmNoaXZlX2NvdXJzZQAAAAAAAgAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAljb3Vyc2VfaWQAAAAAAAAQAAAAAQAAB9AAAAAGQ291cnNlAAA=", + "AAAAAAAAAyZDaGVjayBpZiBhIHVzZXIgaXMgdGhlIGNyZWF0b3Igb2YgYSBzcGVjaWZpYyBjb3Vyc2UuCgpUaGlzIGZ1bmN0aW9uIHZlcmlmaWVzIHdoZXRoZXIgdGhlIHNwZWNpZmllZCB1c2VyIGlzIHRoZSBvcmlnaW5hbCBjcmVhdG9yCm9mIHRoZSBnaXZlbiBjb3Vyc2UuCgojIEFyZ3VtZW50cwoKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNvdXJzZV9pZGAgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIGNvdXJzZQoqIGB1c2VyYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIHRvIGNoZWNrCgojIFJldHVybnMKClJldHVybnMgYHRydWVgIGlmIHRoZSB1c2VyIGlzIHRoZSBjb3Vyc2UgY3JlYXRvciwgYGZhbHNlYCBvdGhlcndpc2UuCgojIFBhbmljcwoKKiBJZiBjb3Vyc2UgZG9lc24ndCBleGlzdAoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBDaGVjayBpZiB1c2VyIGlzIGNvdXJzZSBjcmVhdG9yCmxldCBpc19jcmVhdG9yID0gY29udHJhY3QuaXNfY291cnNlX2NyZWF0b3IoCiZlbnYsCiJjb3Vyc2VfMTIzIi50cnlfaW50bygpLnVud3JhcCgpLAp1c2VyX2FkZHJlc3MKKTsKCmlmIGlzX2NyZWF0b3IgewovLyBVc2VyIGNhbiBlZGl0IHRoaXMgY291cnNlCn0KYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipOb24tZXhpc3RlbnQgY291cnNlKio6IFdpbGwgcGFuaWMgaWYgY291cnNlIGRvZXNuJ3QgZXhpc3QKKiAqKlB1YmxpYyBhY2Nlc3MqKjogQW55b25lIGNhbiBjaGVjayBjcmVhdG9yIHN0YXR1cwoqICoqQ3JlYXRvciB2ZXJpZmljYXRpb24qKjogVXNlZnVsIGZvciBwZXJtaXNzaW9uIGNoZWNrcwAAAAAAEWlzX2NvdXJzZV9jcmVhdG9yAAAAAAAAAgAAAAAAAAAJY291cnNlX2lkAAAAAAAAEAAAAAAAAAAEdXNlcgAAABMAAAABAAAAAQ==", + "AAAAAAAAAlFMaXN0IGFsbCBhdmFpbGFibGUgY291cnNlIGNhdGVnb3JpZXMuCgpUaGlzIGZ1bmN0aW9uIHJldHJpZXZlcyBhbGwgY291cnNlIGNhdGVnb3JpZXMgdGhhdCBoYXZlIGJlZW4gY3JlYXRlZAppbiB0aGUgc3lzdGVtLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoKIyBSZXR1cm5zCgpSZXR1cm5zIGEgdmVjdG9yIG9mIGFsbCBhdmFpbGFibGUgYENhdGVnb3J5YCBvYmplY3RzLgoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBHZXQgYWxsIGNhdGVnb3JpZXMKbGV0IGNhdGVnb3JpZXMgPSBjb250cmFjdC5saXN0X2NhdGVnb3JpZXMoZW52LmNsb25lKCkpOwpmb3IgY2F0ZWdvcnkgaW4gY2F0ZWdvcmllcyB7CnByaW50bG4hKCJDYXRlZ29yeToge30iLCBjYXRlZ29yeS5uYW1lKTsKfQpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKkVtcHR5IHN5c3RlbSoqOiBSZXR1cm5zIGVtcHR5IHZlY3RvciBpZiBubyBjYXRlZ29yaWVzIGV4aXN0CiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gbGlzdCBjYXRlZ29yaWVzCiogKipPcmRlcioqOiBDYXRlZ29yaWVzIGFyZSByZXR1cm5lZCBpbiBjcmVhdGlvbiBvcmRlcgAAAAAAAA9saXN0X2NhdGVnb3JpZXMAAAAAAAAAAAEAAAPqAAAH0AAAAAhDYXRlZ29yeQ==", + "AAAAAAAABABMaXN0IGNvdXJzZXMgd2l0aCBmaWx0ZXJpbmcgYW5kIHBhZ2luYXRpb24uCgpUaGlzIGZ1bmN0aW9uIHJldHJpZXZlcyBjb3Vyc2VzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBmaWx0ZXJzCndpdGggb3B0aW9uYWwgcGFnaW5hdGlvbiBzdXBwb3J0LgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGBmaWx0ZXJzYCAtIEZpbHRlcmluZyBjcml0ZXJpYSBmb3IgY291cnNlcwoqIGBsaW1pdGAgLSBPcHRpb25hbCBtYXhpbXVtIG51bWJlciBvZiBjb3Vyc2VzIHRvIHJldHVybgoqIGBvZmZzZXRgIC0gT3B0aW9uYWwgbnVtYmVyIG9mIGNvdXJzZXMgdG8gc2tpcCBmb3IgcGFnaW5hdGlvbgoKIyBSZXR1cm5zCgpSZXR1cm5zIGEgdmVjdG9yIG9mIGBDb3Vyc2VgIG9iamVjdHMgbWF0Y2hpbmcgdGhlIGZpbHRlciBjcml0ZXJpYS4KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gTGlzdCBmaXJzdCAxMCBjb3Vyc2VzCmxldCBjb3Vyc2VzID0gY29udHJhY3QubGlzdF9jb3Vyc2VzX3dpdGhfZmlsdGVycygKZW52LmNsb25lKCksCkNvdXJzZUZpbHRlcnM6OmRlZmF1bHQoKSwKU29tZSgxMCksClNvbWUoMCkKKTsKCi8vIEZpbHRlciBieSBjYXRlZ29yeQpsZXQgbXV0IGZpbHRlcnMgPSBDb3Vyc2VGaWx0ZXJzOjpkZWZhdWx0KCk7CmZpbHRlcnMuY2F0ZWdvcnkgPSBTb21lKCJQcm9ncmFtbWluZyIudHJ5X2ludG8oKS51bndyYXAoKSk7CmxldCBwcm9ncmFtbWluZ19jb3Vyc2VzID0gY29udHJhY3QubGlzdF9jb3Vyc2VzX3dpdGhfZmlsdGVycygKZW52LmNsb25lKCksCmZpbHRlcnMsClNvbWUoMjApLApOb25lCik7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqTm8gbWF0Y2hlcyoqOiBSZXR1cm5zIGVtcHR5IHZlY3RvciBpZiBubyBjb3Vyc2VzIG1hdGNoIGZpbHRlcnMKKiAqKkxhcmdlIGxpbWl0cyoqOiBMaW1pdCBzaG91bGQgYmUgcmVhc29uYWJsZSB0byBhdm9pZCBnYXMgaXNzdWVzCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gbGlzdCBjb3Vyc2VzCiogKipBcmNoAAAAGWxpc3RfY291cnNlc193aXRoX2ZpbHRlcnMAAAAAAAADAAAAAAAAAAdmaWx0ZXJzAAAAB9AAAAANQ291cnNlRmlsdGVycwAAAAAAAAAAAAAFbGltaXQAAAAAAAPoAAAABAAAAAAAAAAGb2Zmc2V0AAAAAAPoAAAABAAAAAEAAAPqAAAH0AAAAAZDb3Vyc2UAAA==", + "AAAAAAAAAY5FeHBvcnQgYWxsIGNvdXJzZSBkYXRhIGZvciBiYWNrdXAgcHVycG9zZXMgKGFkbWluIG9ubHkpCgpUaGlzIGZ1bmN0aW9uIGV4cG9ydHMgYWxsIGNvdXJzZSBkYXRhIGluY2x1ZGluZyBjb3Vyc2VzLCBjYXRlZ29yaWVzLAptb2R1bGVzLCBnb2FscywgYW5kIHByZXJlcXVpc2l0ZXMgZm9yIGJhY2t1cCBhbmQgcmVjb3ZlcnkgcHVycG9zZXMuCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBleHBvcnQgKG11c3QgYmUgYWRtaW4pCgojIFJldHVybnMKKiBgQ291cnNlQmFja3VwRGF0YWAgLSBDb21wbGV0ZSBiYWNrdXAgZGF0YSBzdHJ1Y3R1cmUKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGlzIG5vdCBhbiBhZG1pbgAAAAAAEmV4cG9ydF9jb3Vyc2VfZGF0YQAAAAAAAQAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAQAAB9AAAAAQQ291cnNlQmFja3VwRGF0YQ==", + "AAAAAAAAAdhJbXBvcnQgY291cnNlIGRhdGEgZnJvbSBiYWNrdXAgKGFkbWluIG9ubHkpCgpUaGlzIGZ1bmN0aW9uIGltcG9ydHMgY291cnNlIGRhdGEgZnJvbSBhIGJhY2t1cCBzdHJ1Y3R1cmUuCk9ubHkgYWRtaW5zIGNhbiBwZXJmb3JtIHRoaXMgb3BlcmF0aW9uLiBUaGlzIHdpbGwgb3ZlcndyaXRlIGV4aXN0aW5nIGRhdGEuCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBpbXBvcnQgKG11c3QgYmUgYWRtaW4pCiogYGJhY2t1cF9kYXRhYCAtIEJhY2t1cCBkYXRhIHN0cnVjdHVyZSB0byBpbXBvcnQKCiMgUmV0dXJucwoqIGB1MzJgIC0gTnVtYmVyIG9mIGNvdXJzZXMgaW1wb3J0ZWQKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGlzIG5vdCBhbiBhZG1pbgoqIElmIGJhY2t1cCBkYXRhIGlzIGludmFsaWQKKiBJZiBpbXBvcnQgb3BlcmF0aW9uIGZhaWxzAAAAEmltcG9ydF9jb3Vyc2VfZGF0YQAAAAAAAgAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAtiYWNrdXBfZGF0YQAAAAfQAAAAEENvdXJzZUJhY2t1cERhdGEAAAABAAAABA==", + "AAAAAAAAARFHZXQgdGhlIGN1cnJlbnQgY29udHJhY3QgdmVyc2lvbgoKUmV0dXJucyB0aGUgc2VtYW50aWMgdmVyc2lvbiBvZiB0aGUgY3VycmVudCBjb250cmFjdCBkZXBsb3ltZW50LgpUaGlzIGlzIHVzZWZ1bCBmb3IgdHJhY2tpbmcgY29udHJhY3QgdXBncmFkZXMgYW5kIGNvbXBhdGliaWxpdHkuCgojIEFyZ3VtZW50cwoqIGBfZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50ICh1bnVzZWQpCgojIFJldHVybnMKKiBgU3RyaW5nYCAtIFRoZSBjdXJyZW50IGNvbnRyYWN0IHZlcnNpb24AAAAAAAAUZ2V0X2NvbnRyYWN0X3ZlcnNpb24AAAAAAAAAAQAAABA=", + "AAAAAAAAAR5HZXQgY29udHJhY3QgdmVyc2lvbiBoaXN0b3J5CgpSZXR1cm5zIGEgbGlzdCBvZiBhbGwgdmVyc2lvbnMgdGhhdCBoYXZlIGJlZW4gZGVwbG95ZWQgZm9yIHRoaXMgY29udHJhY3QuClRoaXMgaGVscHMgdHJhY2sgdGhlIGV2b2x1dGlvbiBvZiB0aGUgY29udHJhY3Qgb3ZlciB0aW1lLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CgojIFJldHVybnMKKiBgVmVjPFN0cmluZz5gIC0gVmVjdG9yIG9mIHZlcnNpb24gc3RyaW5ncyBpbiBjaHJvbm9sb2dpY2FsIG9yZGVyAAAAAAATZ2V0X3ZlcnNpb25faGlzdG9yeQAAAAAAAAAAAQAAA+oAAAAQ", + "AAAAAAAAAblDaGVjayBjb21wYXRpYmlsaXR5IGJldHdlZW4gY29udHJhY3QgdmVyc2lvbnMKCkRldGVybWluZXMgaWYgZGF0YSBmcm9tIG9uZSB2ZXJzaW9uIGNhbiBiZSBzYWZlbHkgdXNlZCB3aXRoIGFub3RoZXIgdmVyc2lvbi4KVGhpcyBpcyBjcnVjaWFsIGZvciBtaWdyYXRpb24gcHJvY2Vzc2VzIGFuZCBiYWNrd2FyZCBjb21wYXRpYmlsaXR5LgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGZyb21fdmVyc2lvbmAgLSBUaGUgc291cmNlIHZlcnNpb24gdG8gY2hlY2sgY29tcGF0aWJpbGl0eSBmcm9tCiogYHRvX3ZlcnNpb25gIC0gVGhlIHRhcmdldCB2ZXJzaW9uIHRvIGNoZWNrIGNvbXBhdGliaWxpdHkgdG8KCiMgUmV0dXJucwoqIGBib29sYCAtIFRydWUgaWYgdGhlIHZlcnNpb25zIGFyZSBjb21wYXRpYmxlLCBmYWxzZSBvdGhlcndpc2UAAAAAAAAVaXNfdmVyc2lvbl9jb21wYXRpYmxlAAAAAAAAAgAAAAAAAAAMZnJvbV92ZXJzaW9uAAAAEAAAAAAAAAAKdG9fdmVyc2lvbgAAAAAAEAAAAAEAAAAB", + "AAAAAAAAAkFNaWdyYXRlIGNvdXJzZSBkYXRhIGJldHdlZW4gY29udHJhY3QgdmVyc2lvbnMKClBlcmZvcm1zIGRhdGEgbWlncmF0aW9uIGZyb20gb25lIGNvbnRyYWN0IHZlcnNpb24gdG8gYW5vdGhlci4KVGhpcyBmdW5jdGlvbiBoYW5kbGVzIHRoZSB0cmFuc2Zvcm1hdGlvbiBvZiBjb3Vyc2UgZGF0YSBzdHJ1Y3R1cmVzCndoZW4gdXBncmFkaW5nIGNvbnRyYWN0IHZlcnNpb25zLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNhbGxlcmAgLSBUaGUgYWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBtaWdyYXRpb24gKG11c3QgYmUgY291cnNlIGNyZWF0b3Igb3IgYWRtaW4pCiogYGZyb21fdmVyc2lvbmAgLSBUaGUgc291cmNlIHZlcnNpb24gdG8gbWlncmF0ZSBmcm9tCiogYHRvX3ZlcnNpb25gIC0gVGhlIHRhcmdldCB2ZXJzaW9uIHRvIG1pZ3JhdGUgdG8KCiMgUmV0dXJucwoqIGBib29sYCAtIFRydWUgaWYgbWlncmF0aW9uIHdhcyBzdWNjZXNzZnVsLCBmYWxzZSBvdGhlcndpc2UKCiMgRXZlbnRzCkVtaXRzIGEgbWlncmF0aW9uIGV2ZW50IHVwb24gc3VjY2Vzc2Z1bCBjb21wbGV0aW9uAAAAAAAAE21pZ3JhdGVfY291cnNlX2RhdGEAAAAAAwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAxmcm9tX3ZlcnNpb24AAAAQAAAAAAAAAAp0b192ZXJzaW9uAAAAAAAQAAAAAQAAAAE=", + "AAAAAAAAAP9HZXQgbWlncmF0aW9uIHN0YXR1cyBmb3IgdGhlIGN1cnJlbnQgY29udHJhY3QKClJldHVybnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgbWlncmF0aW9uIHN0YXR1cyBhbmQgYW55CnBlbmRpbmcgbWlncmF0aW9ucyB0aGF0IG5lZWQgdG8gYmUgY29tcGxldGVkLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50CgojIFJldHVybnMKKiBgU3RyaW5nYCAtIE1pZ3JhdGlvbiBzdGF0dXMgaW5mb3JtYXRpb24AAAAAFGdldF9taWdyYXRpb25fc3RhdHVzAAAAAAAAAAEAAAAQ" ]), + options + ) + } + public readonly fromJSON = { + create_course: this.txFromJSON, + create_course_category: this.txFromJSON, + get_course: this.txFromJSON, + get_course_category: this.txFromJSON>, + get_courses_by_instructor: this.txFromJSON>, + remove_module: this.txFromJSON, + add_module: this.txFromJSON, + delete_course: this.txFromJSON, + hello_world: this.txFromJSON, + edit_goal: this.txFromJSON, + add_goal: this.txFromJSON, + remove_goal: this.txFromJSON, + add_prerequisite: this.txFromJSON, + remove_prerequisite: this.txFromJSON, + edit_prerequisite: this.txFromJSON, + edit_course: this.txFromJSON, + archive_course: this.txFromJSON, + is_course_creator: this.txFromJSON, + list_categories: this.txFromJSON>, + list_courses_with_filters: this.txFromJSON>, + export_course_data: this.txFromJSON, + import_course_data: this.txFromJSON, + get_contract_version: this.txFromJSON, + get_version_history: this.txFromJSON>, + is_version_compatible: this.txFromJSON, + migrate_course_data: this.txFromJSON, + get_migration_status: this.txFromJSON + } +} \ No newline at end of file diff --git a/schemas/course_registry-ts/tsconfig.json b/schemas/course_registry-ts/tsconfig.json new file mode 100644 index 0000000..acac142 --- /dev/null +++ b/schemas/course_registry-ts/tsconfig.json @@ -0,0 +1,98 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "NodeNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + // "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "src/*" + ] +} \ No newline at end of file diff --git a/schemas/course_registry_docs.md b/schemas/course_registry_docs.md new file mode 100644 index 0000000..aa37e83 --- /dev/null +++ b/schemas/course_registry_docs.md @@ -0,0 +1,2208 @@ +Env Meta: AAAAAAAAABYAAAAA + • Protocol Version: 22 + +Contract Meta: + • rsver: 1.90.0-nightly + • rssdkver: 22.0.8#f46e9e0610213bbb72285566f9dd960ff96d03d8 + +Contract Spec: + • Error: Error + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(OnlyCreatorCanAddGoals), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyGoalContent), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CourseIdNotExist), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(OnlyCreatorCanArchive), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CourseAlreadyArchived), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(Unauthorized), + value: 6, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(NameRequired), + value: 7, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyCourseTitle), + value: 8, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidPrice), + value: 9, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(DuplicateCourseTitle), + value: 10, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(DuplicateCourseId), + value: 11, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(OnlyCreatorCanEditPrereqs), + value: 12, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PrereqCourseNotFound), + value: 13, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(SelfPrerequisite), + value: 14, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CircularDependency), + value: 15, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyCourseId), + value: 16, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CourseNotFound), + value: 17, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyNewGoalContent), + value: 18, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyGoalId), + value: 19, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(GoalCourseMismatch), + value: 20, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(ModuleNotFound), + value: 21, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UnauthorizedCaller), + value: 401, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UnauthorizedCourseAccess), + value: 402, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidAdminOperation), + value: 403, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyModuleTitle), + value: 404, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(DuplicateModulePosition), + value: 405, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyModuleId), + value: 22, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PrereqNotInList), + value: 23, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidModulePosition), + value: 24, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidModuleTitle), + value: 25, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidCourseDescription), + value: 26, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidCategoryName), + value: 27, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyCategory), + value: 28, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidTitleLength), + value: 29, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidLanguageLength), + value: 43, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidThumbnailUrlLength), + value: 44, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidDurationValue), + value: 45, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidLimitValue), + value: 46, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidOffsetValue), + value: 47, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidGoalContent), + value: 48, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidPrerequisiteId), + value: 49, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyPrerequisiteList), + value: 50, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(TooManyPrerequisites), + value: 51, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmptyPrerequisiteId), + value: 52, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidCourseId), + value: 53, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidPrice100), + value: 54, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(AlreadyInitialized), + value: 55, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(DuplicatePrerequisite), + value: 56, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CourseRateLimitExceeded), + value: 57, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CourseRateLimitNotConfigured), + value: 58, + } + + • Error: VersioningError + Docs: Errors that can occur during contract versioning operations + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Invalid version format), + name: StringM(InvalidVersion), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Version not found in history), + name: StringM(VersionNotFound), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration not compatible), + name: StringM(MigrationNotCompatible), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration already completed), + name: StringM(MigrationAlreadyCompleted), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Unauthorized migration attempt), + name: StringM(UnauthorizedMigration), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration failed), + name: StringM(MigrationFailed), + value: 6, + } + + • Struct: CourseModule + Fields: + • course_id: String + • created_at: U64 + • id: String + • position: U32 + • title: String + + • Struct: CourseGoal + Fields: + • content: String + • course_id: String + • created_at: U64 + • created_by: Address + • goal_id: String + + • Struct: CourseRateLimitConfig + Docs: Rate limiting configuration for course operations. + + Tracks rate limiting settings for spam protection in course creation. + Fields: + • max_courses_per_window: U32 + StringM(Maximum course creations allowed per window) + • window_seconds: U64 + StringM(Time window for rate limiting in seconds) + + • Struct: CourseRateLimitData + Docs: Rate limiting tracking data for course operations per address. + + Stores the current usage count and window start time for course rate limiting. + Fields: + • count: U32 + StringM(Current count of course creations in this window) + • window_start: U64 + StringM(Timestamp when the current window started) + + • Struct: CourseCategory + Fields: + • description: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • id: U128 + • name: String + + • Union: DataKey + Cases: + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(Module), + type_: VecM( + [ + String, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(), + name: StringM(Courses), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(CourseGoalList), + type_: VecM( + [ + String, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(CourseGoal), + type_: VecM( + [ + String, + String, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(CoursePrerequisites), + type_: VecM( + [ + String, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(), + name: StringM(CategorySeq), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(CourseCategory), + type_: VecM( + [ + U128, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(), + name: StringM(Admins), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Key for storing course rate limiting configuration), + name: StringM(CourseRateLimitConfig), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing course rate limiting data per address: address -> CourseRateLimitData), + name: StringM(CourseRateLimit), + type_: VecM( + [ + Address, + ], + ), + }, + ) + + • Struct: Course + Fields: + • category: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • creator: Address + • description: String + • duration_hours: Option( + ScSpecTypeOption { + value_type: U32, + }, + ) + • id: String + • is_archived: Bool + • language: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • level: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseLevel), + }, + ), + }, + ) + • prerequisites: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseId), + }, + ), + }, + ) + • price: U128 + • published: Bool + • thumbnail_url: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • title: String + + • Struct: CourseId + Fields: + • count: U128 + • id: String + + • Struct: Category + Fields: + • count: U128 + • name: String + + • Struct: CourseFilters + Fields: + • category: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • level: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseLevel), + }, + ), + }, + ) + • max_duration: Option( + ScSpecTypeOption { + value_type: U32, + }, + ) + • max_price: Option( + ScSpecTypeOption { + value_type: U128, + }, + ) + • min_duration: Option( + ScSpecTypeOption { + value_type: U32, + }, + ) + • min_price: Option( + ScSpecTypeOption { + value_type: U128, + }, + ) + • search_text: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(Text search in course title and description) + + • Struct: EditCourseParams + Fields: + • new_category: Option( + ScSpecTypeOption { + value_type: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ) + • new_description: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + • new_duration_hours: Option( + ScSpecTypeOption { + value_type: Option( + ScSpecTypeOption { + value_type: U32, + }, + ), + }, + ) + • new_language: Option( + ScSpecTypeOption { + value_type: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ) + • new_level: Option( + ScSpecTypeOption { + value_type: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseLevel), + }, + ), + }, + ), + }, + ) + • new_price: Option( + ScSpecTypeOption { + value_type: U128, + }, + ) + • new_published: Option( + ScSpecTypeOption { + value_type: Bool, + }, + ) + • new_thumbnail_url: Option( + ScSpecTypeOption { + value_type: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ) + • new_title: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + + • Struct: CourseBackupData + Docs: Backup data structure for course registry system. + + Contains all course data, categories, modules, goals, and prerequisites + for backup and recovery operations. + Fields: + • admins: Vec( + ScSpecTypeVec { + element_type: Address, + }, + ) + StringM(List of admin addresses) + • backup_timestamp: U64 + StringM(Backup timestamp) + • backup_version: String + StringM(Backup version for compatibility) + • categories: Map( + ScSpecTypeMap { + key_type: U128, + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseCategory), + }, + ), + }, + ) + StringM(All course categories) + • category_seq: U128 + StringM(Category sequence counter) + • courses: Map( + ScSpecTypeMap { + key_type: String, + value_type: Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + }, + ) + StringM(All courses in the system) + • goals: Map( + ScSpecTypeMap { + key_type: String, + value_type: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseGoal), + }, + ), + }, + ), + }, + ) + StringM(All course goals mapped by (course_id, goal_id)) + • modules: Map( + ScSpecTypeMap { + key_type: String, + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseModule), + }, + ), + }, + ) + StringM(All course modules) + • prerequisites: Map( + ScSpecTypeMap { + key_type: String, + value_type: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseId), + }, + ), + }, + ), + }, + ) + StringM(Course prerequisites mapping) + + • Function: create_course + Docs: Create a new course in the registry. + + This function creates a new course with the specified metadata and + returns the created course object with a unique identifier. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `title` - The course title + * `description` - The course description + * `price` - The course price in the platform's currency + * `category` - Optional course category + * `language` - Optional course language + * `thumbnail_url` - Optional URL for the course thumbnail image + * `level` - Optional course difficulty level + * `duration_hours` - Optional estimated duration in hours + + # Returns + + Returns the created `Course` object with all metadata and a unique ID. + + # Panics + + * If title or description are empty + * If creator address is invalid + * If price exceeds maximum allowed value + + # Examples + + ```rust + let course = contract.create_course( + env.clone(), + instructor_address, + "Rust Programming Basics".try_into().unwrap(), + "Learn Rust from scratch".try_into().unwrap(), + 50 + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(title), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(description), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(price), + type_: U128, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(category), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(language), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(thumbnail_url), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(level), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseLevel), + }, + ), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(duration_hours), + type_: Option( + ScSpecTypeOption { + value_type: U32, + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + ], + ) + + • Function: create_course_category + Docs: Create a new course category. + + This function creates a new category that can be used to classify courses. + + # Arguments + + * `env` - The Soroban environment + * `caller` - The address of the user creating the category + * `name` - The name of the category + * `description` - Optional description of the category + + # Returns + + Returns the unique ID of the created category. + + # Panics + + * If category name is empty + * If category with same name already exists + + # Examples + + ```rust + // Create a programming category + let category_id = contract.create_course_category( + env.clone(), + admin_address, + "Programming".try_into().unwrap(), + Some("Computer programming courses".try_into().unwrap()) + ); + ``` + + # Edge Cases + + * **Duplicate names**: Cannot create categories with existing names + * **Empty names**: Category name cannot be empty + * **Unique IDs**: Each category gets a unique auto-generated ID + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(name), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(description), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ], + ) + Output: VecM( + [ + U128, + ], + ) + + • Function: get_course + Docs: Retrieve a course by its ID. + + This function fetches a course's complete information using its unique identifier. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course to retrieve + + # Returns + + Returns the `Course` object containing all course metadata. + + # Panics + + * If course with given ID doesn't exist + * If course_id is invalid or empty + + # Examples + + ```rust + // Get course by ID + let course = contract.get_course(env.clone(), "course_123".try_into().unwrap()); + println!("Course title: {}", course.title); + ``` + + # Edge Cases + + * **Non-existent course**: Will panic if course ID doesn't exist + * **Archived courses**: Still retrievable but marked as archived + * **Public access**: Anyone can retrieve course information + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + ], + ) + + • Function: get_course_category + Docs: Retrieve a course category by its ID. + + This function fetches a category's information using its unique identifier. + + # Arguments + + * `env` - The Soroban environment + * `category_id` - The unique identifier of the category to retrieve + + # Returns + + Returns `Some(CourseCategory)` if found, `None` if the category doesn't exist. + + # Examples + + ```rust + // Get category by ID + if let Some(category) = contract.get_course_category(env.clone(), 1) { + println!("Category: {}", category.name); + } else { + println!("Category not found"); + } + ``` + + # Edge Cases + + * **Non-existent category**: Returns `None` instead of panicking + * **Invalid ID**: Returns `None` for invalid category IDs + * **Public access**: Anyone can retrieve category information + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(category_id), + type_: U128, + }, + ], + ) + Output: VecM( + [ + Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(CourseCategory), + }, + ), + }, + ), + ], + ) + + • Function: get_courses_by_instructor + Docs: Get all courses created by a specific instructor. + + This function retrieves all courses that were created by the specified instructor. + + # Arguments + + * `env` - The Soroban environment + * `instructor` - The address of the instructor to query courses for + + # Returns + + Returns a vector of `Course` objects created by the instructor. + + # Examples + + ```rust + // Get all courses by an instructor + let instructor_courses = contract.get_courses_by_instructor(env.clone(), instructor_address); + for course in instructor_courses { + println!("Course: {}", course.title); + } + ``` + + # Edge Cases + + * **No courses**: Returns empty vector if instructor has no courses + * **Archived courses**: Includes archived courses in results + * **Public access**: Anyone can query instructor courses + * **Invalid instructor**: Returns empty vector for non-existent instructors + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(instructor), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + }, + ), + ], + ) + + • Function: remove_module + Docs: Remove a module from a course. + + This function removes a specific module from its associated course. + + # Arguments + + * `env` - The Soroban environment + * `module_id` - The unique identifier of the module to remove + + # Panics + + Remove a module from a course. + + This function removes a specific module from its associated course. + + # Arguments + + * `env` - The Soroban environment + * `module_id` - The unique identifier of the module to remove + + # Panics + + * If the module doesn't exist + * If the module_id is invalid or empty + * If module removal operation fails + + # Examples + + ```rust + // Remove a module from a course + contract.remove_module(env.clone(), "module_123".try_into().unwrap()); + ``` + + # Edge Cases + + * **Non-existent module**: Will panic if module ID doesn't exist + * **Invalid ID**: Will panic for invalid or empty module IDs + * **Course updates**: Automatically updates course module count + + Panics if the module removal fails or if the module doesn't exist. + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(module_id), + type_: String, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: add_module + Docs: Add a new module to a course. + + This function creates and adds a new module to the specified course + at the given position. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course to add the module to + * `position` - The position where the module should be inserted + * `title` - The title of the new module + + # Returns + + Returns the created `CourseModule` object. + + # Panics + + * If course doesn't exist + * If caller is not the course creator + * If module title is empty + * If position is invalid + + # Examples + + ```rust + // Add a module at position 1 + let module = contract.add_module( + env.clone(), + course_creator_address, + "course_123".try_into().unwrap(), + 1, + "Introduction to Variables".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Invalid position**: Position must be valid for the course + * **Empty title**: Module title cannot be empty + * **Creator only**: Only course creator can add modules + * **Auto-generated ID**: Module gets unique auto-generated ID + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(position), + type_: U32, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(title), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(CourseModule), + }, + ), + ], + ) + + • Function: delete_course + Docs: Delete a course from the registry. + + This function permanently removes a course from the registry. + Only the course creator can delete their own courses. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course to delete + + # Panics + + * If course doesn't exist + * If creator is not the actual course creator + * If course_id is invalid or empty + + # Examples + + ```rust + // Course creator deleting their course + contract.delete_course(env.clone(), course_creator_address, "course_123".try_into().unwrap()); + ``` + + # Edge Cases + + * **Permission denied**: Only course creator can delete their courses + * **Non-existent course**: Will panic if course doesn't exist + * **Permanent deletion**: Course and all associated data are permanently removed + * **Enrolled students**: Consider impact on enrolled students before deletion + + Panics if the deletion fails or if the creator is not authorized. + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: hello_world + Docs: Simple hello world function for testing. + + This is a basic function that returns a greeting message, + primarily used for testing contract deployment and basic functionality. + + # Arguments + + * `_env` - The Soroban environment (unused) + + # Returns + + Returns a greeting string. + + # Examples + + ```rust + // Test contract deployment + let greeting = contract.hello_world(env.clone()); + assert_eq!(greeting, "Hello from Web3 👋"); + ``` + + # Edge Cases + + * **Always succeeds**: This function never fails + * **No dependencies**: Requires no external data or state + * **Testing only**: Primarily intended for contract testing + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + • Function: edit_goal + Docs: Edit an existing course goal. + + This function allows the course creator to modify the content of an existing goal. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course + * `goal_id` - The unique identifier of the goal to edit + * `new_content` - The new content for the goal + + # Returns + + Returns the updated `CourseGoal` object. + + # Panics + + * If course doesn't exist + * If goal doesn't exist + * If creator is not the course creator + * If new_content is empty + + # Examples + + ```rust + // Edit a course goal + let updated_goal = contract.edit_goal( + env.clone(), + course_creator_address, + "course_123".try_into().unwrap(), + "goal_456".try_into().unwrap(), + "Updated learning objective".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Empty content**: New content cannot be empty + * **Creator only**: Only course creator can edit goals + * **Non-existent goal**: Will panic if goal ID doesn't exist + * **Content validation**: New content must meet validation + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(goal_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(new_content), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(CourseGoal), + }, + ), + ], + ) + + • Function: add_goal + Docs: Add a new goal to a course. + + This function creates and adds a new learning goal to the specified course. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course + * `content` - The content/description of the goal + + # Returns + + Returns the created `CourseGoal` object. + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If content is empty + + # Examples + + ```rust + // Add a learning goal to a course + let goal = contract.add_goal( + env.clone(), + course_creator_address, + "course_123".try_into().unwrap(), + "Students will learn basic programming concepts".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Empty content**: Goal content cannot be empty + * **Creator only**: Only course creator can add goals + * **Auto-generated ID**: Goal gets unique auto-generated ID + * **Content validation**: Goal content must meet validation requirements + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(content), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(CourseGoal), + }, + ), + ], + ) + + • Function: remove_goal + Docs: Remove a goal from a course. + + This function removes a specific learning goal from the course. + + # Arguments + + * `env` - The Soroban environment + * `caller` - The address of the user requesting the removal + * `course_id` - The unique identifier of the course + * `goal_id` - The unique identifier of the goal to remove + + # Panics + + * If course doesn't exist + * If goal doesn't exist + * If caller is not the course creator + + # Examples + + ```rust + // Remove a goal from a course + contract.remove_goal( + env.clone(), + course_creator_address, + "course_123".try_into().unwrap(), + "goal_456".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Creator only**: Only course creator can remove goals + * **Non-existent goal**: Will panic if goal ID doesn't exist + * **Permanent removal**: Goal is permanently deleted from course + * **Goal count**: Automatically updates course goal count + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(goal_id), + type_: String, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: add_prerequisite + Docs: Add prerequisites to a course. + + This function adds prerequisite courses that must be completed + before a student can enroll in the target course. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course + * `prerequisite_course_ids` - Vector of course IDs that are prerequisites + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If any prerequisite course doesn't exist + * If trying to add self as prerequisite + + # Examples + + ```rust + let mut prerequisites = Vec::new(&env); + prerequisites.push_back("basic_rust".try_into().unwrap()); + prerequisites.push_back("programming_fundamentals".try_into().unwrap()); + + contract.add_prerequisite( + env.clone(), + course_creator_address, + "advanced_rust".try_into().unwrap(), + prerequisites + ); + ``` + + # Edge Cases + + * **Circular dependencies**: Cannot add self as prerequisite + * **Non-existent courses**: All prerequisite courses must exist + * **Creator only**: Only course creator ca + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(prerequisite_course_ids), + type_: Vec( + ScSpecTypeVec { + element_type: String, + }, + ), + }, + ], + ) + Output: VecM( + [], + ) + + • Function: remove_prerequisite + Docs: Remove a prerequisite from a course. + + This function removes a specific prerequisite course requirement + from the target course. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course + * `prerequisite_course_id` - The ID of the prerequisite course to remove + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If prerequisite doesn't exist for the course + + # Examples + + ```rust + // Remove a prerequisite from a course + contract.remove_prerequisite( + env.clone(), + course_creator_address, + "advanced_rust".try_into().unwrap(), + "basic_rust".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Non-existent prerequisite**: Will panic if prerequisite doesn't exist + * **Creator only**: Only course creator can remove prerequisites + * **No effect**: Removing non-existent prerequisite has no effect + * **Student impact**: Consider impact on enrolled students + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(prerequisite_course_id), + type_: String, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: edit_prerequisite + Docs: Edit the prerequisites for a course. + + This function replaces all existing prerequisites with a new set + of prerequisite courses. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course + * `new_prerequisites` - Vector of new prerequisite course IDs + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If any prerequisite course doesn't exist + * If trying to add self as prerequisite + + # Examples + + ```rust + let mut new_prerequisites = Vec::new(&env); + new_prerequisites.push_back("updated_course_1".try_into().unwrap()); + new_prerequisites.push_back("updated_course_2".try_into().unwrap()); + + contract.edit_prerequisite( + env.clone(), + course_creator_address, + "target_course".try_into().unwrap(), + new_prerequisites + ); + ``` + + # Edge Cases + + * **Complete replacement**: All old prerequisites are removed + * **Empty vector**: Can clear all prerequisites with empty vector + * **Circular dependencies**: Cannot add self as prere + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(new_prerequisites), + type_: Vec( + ScSpecTypeVec { + element_type: String, + }, + ), + }, + ], + ) + Output: VecM( + [], + ) + + • Function: edit_course + Docs: Edit course information. + + This function allows the course creator to update various aspects + of the course using the provided parameters. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course to edit + * `params` - Parameters containing the fields to update + + # Returns + + Returns the updated `Course` object. + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If any field validation fails + + # Examples + + ```rust + let params = EditCourseParams { + title: Some("Updated Course Title".try_into().unwrap()), + description: Some("Updated description".try_into().unwrap()), + price: Some(7500), + level: Some(CourseLevel::Intermediate), + ..Default::default() + }; + + let updated_course = contract.edit_course( + env.clone(), + course_creator_address, + "course_123".try_into().unwrap(), + params + ); + ``` + + # Edge Cases + + * **Partial updates**: Only provided fields are updated + * **Validation**: All fields must pass validation rules + * **Cre + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(params), + type_: Udt( + ScSpecTypeUdt { + name: StringM(EditCourseParams), + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + ], + ) + + • Function: archive_course + Docs: Archive a course. + + This function marks a course as archived, making it unavailable for new enrollments + while preserving existing data and access for current students. + + # Arguments + + * `env` - The Soroban environment + * `creator` - The address of the course creator + * `course_id` - The unique identifier of the course to archive + + # Returns + + Returns the updated `Course` object with archived status. + + # Panics + + * If course doesn't exist + * If creator is not the course creator + * If course is already archived + + # Examples + + ```rust + // Archive a course + let archived_course = contract.archive_course( + &env, + course_creator_address, + "course_123".try_into().unwrap() + ); + ``` + + # Edge Cases + + * **Already archived**: Will panic if course is already archived + * **Creator only**: Only course creator can archive course + * **Student access**: Current students retain access + * **Reversible**: Course can be unarchived if needed + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(creator), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + ], + ) + + • Function: is_course_creator + Docs: Check if a user is the creator of a specific course. + + This function verifies whether the specified user is the original creator + of the given course. + + # Arguments + + * `env` - The Soroban environment + * `course_id` - The unique identifier of the course + * `user` - The address of the user to check + + # Returns + + Returns `true` if the user is the course creator, `false` otherwise. + + # Panics + + * If course doesn't exist + + # Examples + + ```rust + // Check if user is course creator + let is_creator = contract.is_course_creator( + &env, + "course_123".try_into().unwrap(), + user_address + ); + + if is_creator { + // User can edit this course + } + ``` + + # Edge Cases + + * **Non-existent course**: Will panic if course doesn't exist + * **Public access**: Anyone can check creator status + * **Creator verification**: Useful for permission checks + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(course_id), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: list_categories + Docs: List all available course categories. + + This function retrieves all course categories that have been created + in the system. + + # Arguments + + * `env` - The Soroban environment + + # Returns + + Returns a vector of all available `Category` objects. + + # Examples + + ```rust + // Get all categories + let categories = contract.list_categories(env.clone()); + for category in categories { + println!("Category: {}", category.name); + } + ``` + + # Edge Cases + + * **Empty system**: Returns empty vector if no categories exist + * **Public access**: Anyone can list categories + * **Order**: Categories are returned in creation order + Inputs: VecM( + [], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Category), + }, + ), + }, + ), + ], + ) + + • Function: list_courses_with_filters + Docs: List courses with filtering and pagination. + + This function retrieves courses based on the provided filters + with optional pagination support. + + # Arguments + + * `env` - The Soroban environment + * `filters` - Filtering criteria for courses + * `limit` - Optional maximum number of courses to return + * `offset` - Optional number of courses to skip for pagination + + # Returns + + Returns a vector of `Course` objects matching the filter criteria. + + # Examples + + ```rust + // List first 10 courses + let courses = contract.list_courses_with_filters( + env.clone(), + CourseFilters::default(), + Some(10), + Some(0) + ); + + // Filter by category + let mut filters = CourseFilters::default(); + filters.category = Some("Programming".try_into().unwrap()); + let programming_courses = contract.list_courses_with_filters( + env.clone(), + filters, + Some(20), + None + ); + ``` + + # Edge Cases + + * **No matches**: Returns empty vector if no courses match filters + * **Large limits**: Limit should be reasonable to avoid gas issues + * **Public access**: Anyone can list courses + * **Arch + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(filters), + type_: Udt( + ScSpecTypeUdt { + name: StringM(CourseFilters), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(limit), + type_: Option( + ScSpecTypeOption { + value_type: U32, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(offset), + type_: Option( + ScSpecTypeOption { + value_type: U32, + }, + ), + }, + ], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Course), + }, + ), + }, + ), + ], + ) + + • Function: export_course_data + Docs: Export all course data for backup purposes (admin only) + + This function exports all course data including courses, categories, + modules, goals, and prerequisites for backup and recovery purposes. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the export (must be admin) + + # Returns + * `CourseBackupData` - Complete backup data structure + + # Panics + * If caller is not an admin + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(CourseBackupData), + }, + ), + ], + ) + + • Function: import_course_data + Docs: Import course data from backup (admin only) + + This function imports course data from a backup structure. + Only admins can perform this operation. This will overwrite existing data. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the import (must be admin) + * `backup_data` - Backup data structure to import + + # Returns + * `u32` - Number of courses imported + + # Panics + * If caller is not an admin + * If backup data is invalid + * If import operation fails + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(backup_data), + type_: Udt( + ScSpecTypeUdt { + name: StringM(CourseBackupData), + }, + ), + }, + ], + ) + Output: VecM( + [ + U32, + ], + ) + + • Function: get_contract_version + Docs: Get the current contract version + + Returns the semantic version of the current contract deployment. + This is useful for tracking contract upgrades and compatibility. + + # Arguments + * `_env` - The Soroban environment (unused) + + # Returns + * `String` - The current contract version + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + • Function: get_version_history + Docs: Get contract version history + + Returns a list of all versions that have been deployed for this contract. + This helps track the evolution of the contract over time. + + # Arguments + * `env` - The Soroban environment + + # Returns + * `Vec` - Vector of version strings in chronological order + Inputs: VecM( + [], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: String, + }, + ), + ], + ) + + • Function: is_version_compatible + Docs: Check compatibility between contract versions + + Determines if data from one version can be safely used with another version. + This is crucial for migration processes and backward compatibility. + + # Arguments + * `env` - The Soroban environment + * `from_version` - The source version to check compatibility from + * `to_version` - The target version to check compatibility to + + # Returns + * `bool` - True if the versions are compatible, false otherwise + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(from_version), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(to_version), + type_: String, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: migrate_course_data + Docs: Migrate course data between contract versions + + Performs data migration from one contract version to another. + This function handles the transformation of course data structures + when upgrading contract versions. + + # Arguments + * `env` - The Soroban environment + * `caller` - The address performing the migration (must be course creator or admin) + * `from_version` - The source version to migrate from + * `to_version` - The target version to migrate to + + # Returns + * `bool` - True if migration was successful, false otherwise + + # Events + Emits a migration event upon successful completion + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(from_version), + type_: String, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(to_version), + type_: String, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: get_migration_status + Docs: Get migration status for the current contract + + Returns information about the current migration status and any + pending migrations that need to be completed. + + # Arguments + * `env` - The Soroban environment + + # Returns + * `String` - Migration status information + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + diff --git a/schemas/course_registry_schema.json b/schemas/course_registry_schema.json new file mode 100644 index 0000000..705207c --- /dev/null +++ b/schemas/course_registry_schema.json @@ -0,0 +1,1771 @@ +[ + { + "type": "enum", + "doc": "", + "name": "Error", + "cases": [ + { + "doc": "", + "name": "OnlyCreatorCanAddGoals", + "value": 1 + }, + { + "doc": "", + "name": "EmptyGoalContent", + "value": 2 + }, + { + "doc": "", + "name": "CourseIdNotExist", + "value": 3 + }, + { + "doc": "", + "name": "OnlyCreatorCanArchive", + "value": 4 + }, + { + "doc": "", + "name": "CourseAlreadyArchived", + "value": 5 + }, + { + "doc": "", + "name": "Unauthorized", + "value": 6 + }, + { + "doc": "", + "name": "NameRequired", + "value": 7 + }, + { + "doc": "", + "name": "EmptyCourseTitle", + "value": 8 + }, + { + "doc": "", + "name": "InvalidPrice", + "value": 9 + }, + { + "doc": "", + "name": "DuplicateCourseTitle", + "value": 10 + }, + { + "doc": "", + "name": "DuplicateCourseId", + "value": 11 + }, + { + "doc": "", + "name": "OnlyCreatorCanEditPrereqs", + "value": 12 + }, + { + "doc": "", + "name": "PrereqCourseNotFound", + "value": 13 + }, + { + "doc": "", + "name": "SelfPrerequisite", + "value": 14 + }, + { + "doc": "", + "name": "CircularDependency", + "value": 15 + }, + { + "doc": "", + "name": "EmptyCourseId", + "value": 16 + }, + { + "doc": "", + "name": "CourseNotFound", + "value": 17 + }, + { + "doc": "", + "name": "EmptyNewGoalContent", + "value": 18 + }, + { + "doc": "", + "name": "EmptyGoalId", + "value": 19 + }, + { + "doc": "", + "name": "GoalCourseMismatch", + "value": 20 + }, + { + "doc": "", + "name": "ModuleNotFound", + "value": 21 + }, + { + "doc": "", + "name": "UnauthorizedCaller", + "value": 401 + }, + { + "doc": "", + "name": "UnauthorizedCourseAccess", + "value": 402 + }, + { + "doc": "", + "name": "InvalidAdminOperation", + "value": 403 + }, + { + "doc": "", + "name": "EmptyModuleTitle", + "value": 404 + }, + { + "doc": "", + "name": "DuplicateModulePosition", + "value": 405 + }, + { + "doc": "", + "name": "EmptyModuleId", + "value": 22 + }, + { + "doc": "", + "name": "PrereqNotInList", + "value": 23 + }, + { + "doc": "", + "name": "InvalidModulePosition", + "value": 24 + }, + { + "doc": "", + "name": "InvalidModuleTitle", + "value": 25 + }, + { + "doc": "", + "name": "InvalidCourseDescription", + "value": 26 + }, + { + "doc": "", + "name": "InvalidCategoryName", + "value": 27 + }, + { + "doc": "", + "name": "EmptyCategory", + "value": 28 + }, + { + "doc": "", + "name": "InvalidTitleLength", + "value": 29 + }, + { + "doc": "", + "name": "InvalidLanguageLength", + "value": 43 + }, + { + "doc": "", + "name": "InvalidThumbnailUrlLength", + "value": 44 + }, + { + "doc": "", + "name": "InvalidDurationValue", + "value": 45 + }, + { + "doc": "", + "name": "InvalidLimitValue", + "value": 46 + }, + { + "doc": "", + "name": "InvalidOffsetValue", + "value": 47 + }, + { + "doc": "", + "name": "InvalidGoalContent", + "value": 48 + }, + { + "doc": "", + "name": "InvalidPrerequisiteId", + "value": 49 + }, + { + "doc": "", + "name": "EmptyPrerequisiteList", + "value": 50 + }, + { + "doc": "", + "name": "TooManyPrerequisites", + "value": 51 + }, + { + "doc": "", + "name": "EmptyPrerequisiteId", + "value": 52 + }, + { + "doc": "", + "name": "InvalidCourseId", + "value": 53 + }, + { + "doc": "", + "name": "InvalidPrice100", + "value": 54 + }, + { + "doc": "", + "name": "AlreadyInitialized", + "value": 55 + }, + { + "doc": "", + "name": "DuplicatePrerequisite", + "value": 56 + }, + { + "doc": "", + "name": "CourseRateLimitExceeded", + "value": 57 + }, + { + "doc": "", + "name": "CourseRateLimitNotConfigured", + "value": 58 + } + ] + }, + { + "type": "enum", + "doc": "Errors that can occur during contract versioning operations", + "name": "VersioningError", + "cases": [ + { + "doc": "Invalid version format", + "name": "InvalidVersion", + "value": 1 + }, + { + "doc": "Version not found in history", + "name": "VersionNotFound", + "value": 2 + }, + { + "doc": "Migration not compatible", + "name": "MigrationNotCompatible", + "value": 3 + }, + { + "doc": "Migration already completed", + "name": "MigrationAlreadyCompleted", + "value": 4 + }, + { + "doc": "Unauthorized migration attempt", + "name": "UnauthorizedMigration", + "value": 5 + }, + { + "doc": "Migration failed", + "name": "MigrationFailed", + "value": 6 + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "CourseModule", + "fields": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "created_at", + "value": { + "type": "u64" + } + }, + { + "doc": "", + "name": "id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "position", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "title", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "CourseGoal", + "fields": [ + { + "doc": "", + "name": "content", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "created_at", + "value": { + "type": "u64" + } + }, + { + "doc": "", + "name": "created_by", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "goal_id", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "struct", + "doc": "Rate limiting configuration for course operations.\n\nTracks rate limiting settings for spam protection in course creation.", + "name": "CourseRateLimitConfig", + "fields": [ + { + "doc": "Maximum course creations allowed per window", + "name": "max_courses_per_window", + "value": { + "type": "u32" + } + }, + { + "doc": "Time window for rate limiting in seconds", + "name": "window_seconds", + "value": { + "type": "u64" + } + } + ] + }, + { + "type": "struct", + "doc": "Rate limiting tracking data for course operations per address.\n\nStores the current usage count and window start time for course rate limiting.", + "name": "CourseRateLimitData", + "fields": [ + { + "doc": "Current count of course creations in this window", + "name": "count", + "value": { + "type": "u32" + } + }, + { + "doc": "Timestamp when the current window started", + "name": "window_start", + "value": { + "type": "u64" + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "CourseCategory", + "fields": [ + { + "doc": "", + "name": "description", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "id", + "value": { + "type": "u128" + } + }, + { + "doc": "", + "name": "name", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "union", + "doc": "", + "name": "DataKey", + "cases": [ + { + "doc": "", + "name": "Module", + "values": [ + { + "type": "string" + } + ] + }, + { + "doc": "", + "name": "Courses", + "values": [] + }, + { + "doc": "", + "name": "CourseGoalList", + "values": [ + { + "type": "string" + } + ] + }, + { + "doc": "", + "name": "CourseGoal", + "values": [ + { + "type": "string" + }, + { + "type": "string" + } + ] + }, + { + "doc": "", + "name": "CoursePrerequisites", + "values": [ + { + "type": "string" + } + ] + }, + { + "doc": "", + "name": "CategorySeq", + "values": [] + }, + { + "doc": "", + "name": "CourseCategory", + "values": [ + { + "type": "u128" + } + ] + }, + { + "doc": "", + "name": "Admins", + "values": [] + }, + { + "doc": "Key for storing course rate limiting configuration", + "name": "CourseRateLimitConfig", + "values": [] + }, + { + "doc": "Key for storing course rate limiting data per address: address -> CourseRateLimitData", + "name": "CourseRateLimit", + "values": [ + { + "type": "address" + } + ] + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "Course", + "fields": [ + { + "doc": "", + "name": "category", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "description", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "duration_hours", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + }, + { + "doc": "", + "name": "id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "is_archived", + "value": { + "type": "bool" + } + }, + { + "doc": "", + "name": "language", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "level", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "CourseLevel" + } + } + }, + { + "doc": "", + "name": "prerequisites", + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "CourseId" + } + } + }, + { + "doc": "", + "name": "price", + "value": { + "type": "u128" + } + }, + { + "doc": "", + "name": "published", + "value": { + "type": "bool" + } + }, + { + "doc": "", + "name": "thumbnail_url", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "title", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "CourseId", + "fields": [ + { + "doc": "", + "name": "count", + "value": { + "type": "u128" + } + }, + { + "doc": "", + "name": "id", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "Category", + "fields": [ + { + "doc": "", + "name": "count", + "value": { + "type": "u128" + } + }, + { + "doc": "", + "name": "name", + "value": { + "type": "string" + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "CourseFilters", + "fields": [ + { + "doc": "", + "name": "category", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "level", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "CourseLevel" + } + } + }, + { + "doc": "", + "name": "max_duration", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + }, + { + "doc": "", + "name": "max_price", + "value": { + "type": "option", + "value": { + "type": "u128" + } + } + }, + { + "doc": "", + "name": "min_duration", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + }, + { + "doc": "", + "name": "min_price", + "value": { + "type": "option", + "value": { + "type": "u128" + } + } + }, + { + "doc": "Text search in course title and description", + "name": "search_text", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "EditCourseParams", + "fields": [ + { + "doc": "", + "name": "new_category", + "value": { + "type": "option", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + }, + { + "doc": "", + "name": "new_description", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "new_duration_hours", + "value": { + "type": "option", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + } + }, + { + "doc": "", + "name": "new_language", + "value": { + "type": "option", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + }, + { + "doc": "", + "name": "new_level", + "value": { + "type": "option", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "CourseLevel" + } + } + } + }, + { + "doc": "", + "name": "new_price", + "value": { + "type": "option", + "value": { + "type": "u128" + } + } + }, + { + "doc": "", + "name": "new_published", + "value": { + "type": "option", + "value": { + "type": "bool" + } + } + }, + { + "doc": "", + "name": "new_thumbnail_url", + "value": { + "type": "option", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + }, + { + "doc": "", + "name": "new_title", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "struct", + "doc": "Backup data structure for course registry system.\n\nContains all course data, categories, modules, goals, and prerequisites\nfor backup and recovery operations.", + "name": "CourseBackupData", + "fields": [ + { + "doc": "List of admin addresses", + "name": "admins", + "value": { + "type": "vec", + "element": { + "type": "address" + } + } + }, + { + "doc": "Backup timestamp", + "name": "backup_timestamp", + "value": { + "type": "u64" + } + }, + { + "doc": "Backup version for compatibility", + "name": "backup_version", + "value": { + "type": "string" + } + }, + { + "doc": "All course categories", + "name": "categories", + "value": { + "type": "map", + "key": { + "type": "u128" + }, + "value": { + "type": "custom", + "name": "CourseCategory" + } + } + }, + { + "doc": "Category sequence counter", + "name": "category_seq", + "value": { + "type": "u128" + } + }, + { + "doc": "All courses in the system", + "name": "courses", + "value": { + "type": "map", + "key": { + "type": "string" + }, + "value": { + "type": "custom", + "name": "Course" + } + } + }, + { + "doc": "All course goals mapped by (course_id, goal_id)", + "name": "goals", + "value": { + "type": "map", + "key": { + "type": "string" + }, + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "CourseGoal" + } + } + } + }, + { + "doc": "All course modules", + "name": "modules", + "value": { + "type": "map", + "key": { + "type": "string" + }, + "value": { + "type": "custom", + "name": "CourseModule" + } + } + }, + { + "doc": "Course prerequisites mapping", + "name": "prerequisites", + "value": { + "type": "map", + "key": { + "type": "string" + }, + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "CourseId" + } + } + } + } + ] + }, + { + "type": "function", + "doc": "Create a new course in the registry.\n\nThis function creates a new course with the specified metadata and\nreturns the created course object with a unique identifier.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `title` - The course title\n* `description` - The course description\n* `price` - The course price in the platform's currency\n* `category` - Optional course category\n* `language` - Optional course language\n* `thumbnail_url` - Optional URL for the course thumbnail image\n* `level` - Optional course difficulty level\n* `duration_hours` - Optional estimated duration in hours\n\n# Returns\n\nReturns the created `Course` object with all metadata and a unique ID.\n\n# Panics\n\n* If title or description are empty\n* If creator address is invalid\n* If price exceeds maximum allowed value\n\n# Examples\n\n```rust\nlet course = contract.create_course(\nenv.clone(),\ninstructor_address,\n\"Rust Programming Basics\".try_into().unwrap(),\n\"Learn Rust from scratch\".try_into().unwrap(),\n50", + "name": "create_course", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "title", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "description", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "price", + "value": { + "type": "u128" + } + }, + { + "doc": "", + "name": "category", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "language", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "thumbnail_url", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "level", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "CourseLevel" + } + } + }, + { + "doc": "", + "name": "duration_hours", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "Course" + } + ] + }, + { + "type": "function", + "doc": "Create a new course category.\n\nThis function creates a new category that can be used to classify courses.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `caller` - The address of the user creating the category\n* `name` - The name of the category\n* `description` - Optional description of the category\n\n# Returns\n\nReturns the unique ID of the created category.\n\n# Panics\n\n* If category name is empty\n* If category with same name already exists\n\n# Examples\n\n```rust\n// Create a programming category\nlet category_id = contract.create_course_category(\nenv.clone(),\nadmin_address,\n\"Programming\".try_into().unwrap(),\nSome(\"Computer programming courses\".try_into().unwrap())\n);\n```\n\n# Edge Cases\n\n* **Duplicate names**: Cannot create categories with existing names\n* **Empty names**: Category name cannot be empty\n* **Unique IDs**: Each category gets a unique auto-generated ID", + "name": "create_course_category", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "name", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "description", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ], + "outputs": [ + { + "type": "u128" + } + ] + }, + { + "type": "function", + "doc": "Retrieve a course by its ID.\n\nThis function fetches a course's complete information using its unique identifier.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course to retrieve\n\n# Returns\n\nReturns the `Course` object containing all course metadata.\n\n# Panics\n\n* If course with given ID doesn't exist\n* If course_id is invalid or empty\n\n# Examples\n\n```rust\n// Get course by ID\nlet course = contract.get_course(env.clone(), \"course_123\".try_into().unwrap());\nprintln!(\"Course title: {}\", course.title);\n```\n\n# Edge Cases\n\n* **Non-existent course**: Will panic if course ID doesn't exist\n* **Archived courses**: Still retrievable but marked as archived\n* **Public access**: Anyone can retrieve course information", + "name": "get_course", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "Course" + } + ] + }, + { + "type": "function", + "doc": "Retrieve a course category by its ID.\n\nThis function fetches a category's information using its unique identifier.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `category_id` - The unique identifier of the category to retrieve\n\n# Returns\n\nReturns `Some(CourseCategory)` if found, `None` if the category doesn't exist.\n\n# Examples\n\n```rust\n// Get category by ID\nif let Some(category) = contract.get_course_category(env.clone(), 1) {\nprintln!(\"Category: {}\", category.name);\n} else {\nprintln!(\"Category not found\");\n}\n```\n\n# Edge Cases\n\n* **Non-existent category**: Returns `None` instead of panicking\n* **Invalid ID**: Returns `None` for invalid category IDs\n* **Public access**: Anyone can retrieve category information", + "name": "get_course_category", + "inputs": [ + { + "doc": "", + "name": "category_id", + "value": { + "type": "u128" + } + } + ], + "outputs": [ + { + "type": "option", + "value": { + "type": "custom", + "name": "CourseCategory" + } + } + ] + }, + { + "type": "function", + "doc": "Get all courses created by a specific instructor.\n\nThis function retrieves all courses that were created by the specified instructor.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `instructor` - The address of the instructor to query courses for\n\n# Returns\n\nReturns a vector of `Course` objects created by the instructor.\n\n# Examples\n\n```rust\n// Get all courses by an instructor\nlet instructor_courses = contract.get_courses_by_instructor(env.clone(), instructor_address);\nfor course in instructor_courses {\nprintln!(\"Course: {}\", course.title);\n}\n```\n\n# Edge Cases\n\n* **No courses**: Returns empty vector if instructor has no courses\n* **Archived courses**: Includes archived courses in results\n* **Public access**: Anyone can query instructor courses\n* **Invalid instructor**: Returns empty vector for non-existent instructors", + "name": "get_courses_by_instructor", + "inputs": [ + { + "doc": "", + "name": "instructor", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "vec", + "element": { + "type": "custom", + "name": "Course" + } + } + ] + }, + { + "type": "function", + "doc": "Remove a module from a course.\n\nThis function removes a specific module from its associated course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `module_id` - The unique identifier of the module to remove\n\n# Panics\n\nRemove a module from a course.\n\nThis function removes a specific module from its associated course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `module_id` - The unique identifier of the module to remove\n\n# Panics\n\n* If the module doesn't exist\n* If the module_id is invalid or empty\n* If module removal operation fails\n\n# Examples\n\n```rust\n// Remove a module from a course\ncontract.remove_module(env.clone(), \"module_123\".try_into().unwrap());\n```\n\n# Edge Cases\n\n* **Non-existent module**: Will panic if module ID doesn't exist\n* **Invalid ID**: Will panic for invalid or empty module IDs\n* **Course updates**: Automatically updates course module count\n\nPanics if the module removal fails or if the module doesn't exist.", + "name": "remove_module", + "inputs": [ + { + "doc": "", + "name": "module_id", + "value": { + "type": "string" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Add a new module to a course.\n\nThis function creates and adds a new module to the specified course\nat the given position.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course to add the module to\n* `position` - The position where the module should be inserted\n* `title` - The title of the new module\n\n# Returns\n\nReturns the created `CourseModule` object.\n\n# Panics\n\n* If course doesn't exist\n* If caller is not the course creator\n* If module title is empty\n* If position is invalid\n\n# Examples\n\n```rust\n// Add a module at position 1\nlet module = contract.add_module(\nenv.clone(),\ncourse_creator_address,\n\"course_123\".try_into().unwrap(),\n1,\n\"Introduction to Variables\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Invalid position**: Position must be valid for the course\n* **Empty title**: Module title cannot be empty\n* **Creator only**: Only course creator can add modules\n* **Auto-generated ID**: Module gets unique auto-generated ID", + "name": "add_module", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "position", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "title", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "CourseModule" + } + ] + }, + { + "type": "function", + "doc": "Delete a course from the registry.\n\nThis function permanently removes a course from the registry.\nOnly the course creator can delete their own courses.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course to delete\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the actual course creator\n* If course_id is invalid or empty\n\n# Examples\n\n```rust\n// Course creator deleting their course\ncontract.delete_course(env.clone(), course_creator_address, \"course_123\".try_into().unwrap());\n```\n\n# Edge Cases\n\n* **Permission denied**: Only course creator can delete their courses\n* **Non-existent course**: Will panic if course doesn't exist\n* **Permanent deletion**: Course and all associated data are permanently removed\n* **Enrolled students**: Consider impact on enrolled students before deletion\n\nPanics if the deletion fails or if the creator is not authorized.", + "name": "delete_course", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Simple hello world function for testing.\n\nThis is a basic function that returns a greeting message,\nprimarily used for testing contract deployment and basic functionality.\n\n# Arguments\n\n* `_env` - The Soroban environment (unused)\n\n# Returns\n\nReturns a greeting string.\n\n# Examples\n\n```rust\n// Test contract deployment\nlet greeting = contract.hello_world(env.clone());\nassert_eq!(greeting, \"Hello from Web3 👋\");\n```\n\n# Edge Cases\n\n* **Always succeeds**: This function never fails\n* **No dependencies**: Requires no external data or state\n* **Testing only**: Primarily intended for contract testing", + "name": "hello_world", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + }, + { + "type": "function", + "doc": "Edit an existing course goal.\n\nThis function allows the course creator to modify the content of an existing goal.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course\n* `goal_id` - The unique identifier of the goal to edit\n* `new_content` - The new content for the goal\n\n# Returns\n\nReturns the updated `CourseGoal` object.\n\n# Panics\n\n* If course doesn't exist\n* If goal doesn't exist\n* If creator is not the course creator\n* If new_content is empty\n\n# Examples\n\n```rust\n// Edit a course goal\nlet updated_goal = contract.edit_goal(\nenv.clone(),\ncourse_creator_address,\n\"course_123\".try_into().unwrap(),\n\"goal_456\".try_into().unwrap(),\n\"Updated learning objective\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Empty content**: New content cannot be empty\n* **Creator only**: Only course creator can edit goals\n* **Non-existent goal**: Will panic if goal ID doesn't exist\n* **Content validation**: New content must meet validation ", + "name": "edit_goal", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "goal_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "new_content", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "CourseGoal" + } + ] + }, + { + "type": "function", + "doc": "Add a new goal to a course.\n\nThis function creates and adds a new learning goal to the specified course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course\n* `content` - The content/description of the goal\n\n# Returns\n\nReturns the created `CourseGoal` object.\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If content is empty\n\n# Examples\n\n```rust\n// Add a learning goal to a course\nlet goal = contract.add_goal(\nenv.clone(),\ncourse_creator_address,\n\"course_123\".try_into().unwrap(),\n\"Students will learn basic programming concepts\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Empty content**: Goal content cannot be empty\n* **Creator only**: Only course creator can add goals\n* **Auto-generated ID**: Goal gets unique auto-generated ID\n* **Content validation**: Goal content must meet validation requirements", + "name": "add_goal", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "content", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "CourseGoal" + } + ] + }, + { + "type": "function", + "doc": "Remove a goal from a course.\n\nThis function removes a specific learning goal from the course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `caller` - The address of the user requesting the removal\n* `course_id` - The unique identifier of the course\n* `goal_id` - The unique identifier of the goal to remove\n\n# Panics\n\n* If course doesn't exist\n* If goal doesn't exist\n* If caller is not the course creator\n\n# Examples\n\n```rust\n// Remove a goal from a course\ncontract.remove_goal(\nenv.clone(),\ncourse_creator_address,\n\"course_123\".try_into().unwrap(),\n\"goal_456\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Creator only**: Only course creator can remove goals\n* **Non-existent goal**: Will panic if goal ID doesn't exist\n* **Permanent removal**: Goal is permanently deleted from course\n* **Goal count**: Automatically updates course goal count", + "name": "remove_goal", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "goal_id", + "value": { + "type": "string" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Add prerequisites to a course.\n\nThis function adds prerequisite courses that must be completed\nbefore a student can enroll in the target course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course\n* `prerequisite_course_ids` - Vector of course IDs that are prerequisites\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If any prerequisite course doesn't exist\n* If trying to add self as prerequisite\n\n# Examples\n\n```rust\nlet mut prerequisites = Vec::new(&env);\nprerequisites.push_back(\"basic_rust\".try_into().unwrap());\nprerequisites.push_back(\"programming_fundamentals\".try_into().unwrap());\n\ncontract.add_prerequisite(\nenv.clone(),\ncourse_creator_address,\n\"advanced_rust\".try_into().unwrap(),\nprerequisites\n);\n```\n\n# Edge Cases\n\n* **Circular dependencies**: Cannot add self as prerequisite\n* **Non-existent courses**: All prerequisite courses must exist\n* **Creator only**: Only course creator ca", + "name": "add_prerequisite", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "prerequisite_course_ids", + "value": { + "type": "vec", + "element": { + "type": "string" + } + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Remove a prerequisite from a course.\n\nThis function removes a specific prerequisite course requirement\nfrom the target course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course\n* `prerequisite_course_id` - The ID of the prerequisite course to remove\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If prerequisite doesn't exist for the course\n\n# Examples\n\n```rust\n// Remove a prerequisite from a course\ncontract.remove_prerequisite(\nenv.clone(),\ncourse_creator_address,\n\"advanced_rust\".try_into().unwrap(),\n\"basic_rust\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Non-existent prerequisite**: Will panic if prerequisite doesn't exist\n* **Creator only**: Only course creator can remove prerequisites\n* **No effect**: Removing non-existent prerequisite has no effect\n* **Student impact**: Consider impact on enrolled students", + "name": "remove_prerequisite", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "prerequisite_course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Edit the prerequisites for a course.\n\nThis function replaces all existing prerequisites with a new set\nof prerequisite courses.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course\n* `new_prerequisites` - Vector of new prerequisite course IDs\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If any prerequisite course doesn't exist\n* If trying to add self as prerequisite\n\n# Examples\n\n```rust\nlet mut new_prerequisites = Vec::new(&env);\nnew_prerequisites.push_back(\"updated_course_1\".try_into().unwrap());\nnew_prerequisites.push_back(\"updated_course_2\".try_into().unwrap());\n\ncontract.edit_prerequisite(\nenv.clone(),\ncourse_creator_address,\n\"target_course\".try_into().unwrap(),\nnew_prerequisites\n);\n```\n\n# Edge Cases\n\n* **Complete replacement**: All old prerequisites are removed\n* **Empty vector**: Can clear all prerequisites with empty vector\n* **Circular dependencies**: Cannot add self as prere", + "name": "edit_prerequisite", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "new_prerequisites", + "value": { + "type": "vec", + "element": { + "type": "string" + } + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Edit course information.\n\nThis function allows the course creator to update various aspects\nof the course using the provided parameters.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course to edit\n* `params` - Parameters containing the fields to update\n\n# Returns\n\nReturns the updated `Course` object.\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If any field validation fails\n\n# Examples\n\n```rust\nlet params = EditCourseParams {\ntitle: Some(\"Updated Course Title\".try_into().unwrap()),\ndescription: Some(\"Updated description\".try_into().unwrap()),\nprice: Some(7500),\nlevel: Some(CourseLevel::Intermediate),\n..Default::default()\n};\n\nlet updated_course = contract.edit_course(\nenv.clone(),\ncourse_creator_address,\n\"course_123\".try_into().unwrap(),\nparams\n);\n```\n\n# Edge Cases\n\n* **Partial updates**: Only provided fields are updated\n* **Validation**: All fields must pass validation rules\n* **Cre", + "name": "edit_course", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "params", + "value": { + "type": "custom", + "name": "EditCourseParams" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "Course" + } + ] + }, + { + "type": "function", + "doc": "Archive a course.\n\nThis function marks a course as archived, making it unavailable for new enrollments\nwhile preserving existing data and access for current students.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `creator` - The address of the course creator\n* `course_id` - The unique identifier of the course to archive\n\n# Returns\n\nReturns the updated `Course` object with archived status.\n\n# Panics\n\n* If course doesn't exist\n* If creator is not the course creator\n* If course is already archived\n\n# Examples\n\n```rust\n// Archive a course\nlet archived_course = contract.archive_course(\n&env,\ncourse_creator_address,\n\"course_123\".try_into().unwrap()\n);\n```\n\n# Edge Cases\n\n* **Already archived**: Will panic if course is already archived\n* **Creator only**: Only course creator can archive course\n* **Student access**: Current students retain access\n* **Reversible**: Course can be unarchived if needed", + "name": "archive_course", + "inputs": [ + { + "doc": "", + "name": "creator", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "Course" + } + ] + }, + { + "type": "function", + "doc": "Check if a user is the creator of a specific course.\n\nThis function verifies whether the specified user is the original creator\nof the given course.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `course_id` - The unique identifier of the course\n* `user` - The address of the user to check\n\n# Returns\n\nReturns `true` if the user is the course creator, `false` otherwise.\n\n# Panics\n\n* If course doesn't exist\n\n# Examples\n\n```rust\n// Check if user is course creator\nlet is_creator = contract.is_course_creator(\n&env,\n\"course_123\".try_into().unwrap(),\nuser_address\n);\n\nif is_creator {\n// User can edit this course\n}\n```\n\n# Edge Cases\n\n* **Non-existent course**: Will panic if course doesn't exist\n* **Public access**: Anyone can check creator status\n* **Creator verification**: Useful for permission checks", + "name": "is_course_creator", + "inputs": [ + { + "doc": "", + "name": "course_id", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "List all available course categories.\n\nThis function retrieves all course categories that have been created\nin the system.\n\n# Arguments\n\n* `env` - The Soroban environment\n\n# Returns\n\nReturns a vector of all available `Category` objects.\n\n# Examples\n\n```rust\n// Get all categories\nlet categories = contract.list_categories(env.clone());\nfor category in categories {\nprintln!(\"Category: {}\", category.name);\n}\n```\n\n# Edge Cases\n\n* **Empty system**: Returns empty vector if no categories exist\n* **Public access**: Anyone can list categories\n* **Order**: Categories are returned in creation order", + "name": "list_categories", + "inputs": [], + "outputs": [ + { + "type": "vec", + "element": { + "type": "custom", + "name": "Category" + } + } + ] + }, + { + "type": "function", + "doc": "List courses with filtering and pagination.\n\nThis function retrieves courses based on the provided filters\nwith optional pagination support.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `filters` - Filtering criteria for courses\n* `limit` - Optional maximum number of courses to return\n* `offset` - Optional number of courses to skip for pagination\n\n# Returns\n\nReturns a vector of `Course` objects matching the filter criteria.\n\n# Examples\n\n```rust\n// List first 10 courses\nlet courses = contract.list_courses_with_filters(\nenv.clone(),\nCourseFilters::default(),\nSome(10),\nSome(0)\n);\n\n// Filter by category\nlet mut filters = CourseFilters::default();\nfilters.category = Some(\"Programming\".try_into().unwrap());\nlet programming_courses = contract.list_courses_with_filters(\nenv.clone(),\nfilters,\nSome(20),\nNone\n);\n```\n\n# Edge Cases\n\n* **No matches**: Returns empty vector if no courses match filters\n* **Large limits**: Limit should be reasonable to avoid gas issues\n* **Public access**: Anyone can list courses\n* **Arch", + "name": "list_courses_with_filters", + "inputs": [ + { + "doc": "", + "name": "filters", + "value": { + "type": "custom", + "name": "CourseFilters" + } + }, + { + "doc": "", + "name": "limit", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + }, + { + "doc": "", + "name": "offset", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + } + ], + "outputs": [ + { + "type": "vec", + "element": { + "type": "custom", + "name": "Course" + } + } + ] + }, + { + "type": "function", + "doc": "Export all course data for backup purposes (admin only)\n\nThis function exports all course data including courses, categories,\nmodules, goals, and prerequisites for backup and recovery purposes.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the export (must be admin)\n\n# Returns\n* `CourseBackupData` - Complete backup data structure\n\n# Panics\n* If caller is not an admin", + "name": "export_course_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "CourseBackupData" + } + ] + }, + { + "type": "function", + "doc": "Import course data from backup (admin only)\n\nThis function imports course data from a backup structure.\nOnly admins can perform this operation. This will overwrite existing data.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the import (must be admin)\n* `backup_data` - Backup data structure to import\n\n# Returns\n* `u32` - Number of courses imported\n\n# Panics\n* If caller is not an admin\n* If backup data is invalid\n* If import operation fails", + "name": "import_course_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "backup_data", + "value": { + "type": "custom", + "name": "CourseBackupData" + } + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "type": "function", + "doc": "Get the current contract version\n\nReturns the semantic version of the current contract deployment.\nThis is useful for tracking contract upgrades and compatibility.\n\n# Arguments\n* `_env` - The Soroban environment (unused)\n\n# Returns\n* `String` - The current contract version", + "name": "get_contract_version", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + }, + { + "type": "function", + "doc": "Get contract version history\n\nReturns a list of all versions that have been deployed for this contract.\nThis helps track the evolution of the contract over time.\n\n# Arguments\n* `env` - The Soroban environment\n\n# Returns\n* `Vec` - Vector of version strings in chronological order", + "name": "get_version_history", + "inputs": [], + "outputs": [ + { + "type": "vec", + "element": { + "type": "string" + } + } + ] + }, + { + "type": "function", + "doc": "Check compatibility between contract versions\n\nDetermines if data from one version can be safely used with another version.\nThis is crucial for migration processes and backward compatibility.\n\n# Arguments\n* `env` - The Soroban environment\n* `from_version` - The source version to check compatibility from\n* `to_version` - The target version to check compatibility to\n\n# Returns\n* `bool` - True if the versions are compatible, false otherwise", + "name": "is_version_compatible", + "inputs": [ + { + "doc": "", + "name": "from_version", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "to_version", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Migrate course data between contract versions\n\nPerforms data migration from one contract version to another.\nThis function handles the transformation of course data structures\nwhen upgrading contract versions.\n\n# Arguments\n* `env` - The Soroban environment\n* `caller` - The address performing the migration (must be course creator or admin)\n* `from_version` - The source version to migrate from\n* `to_version` - The target version to migrate to\n\n# Returns\n* `bool` - True if migration was successful, false otherwise\n\n# Events\nEmits a migration event upon successful completion", + "name": "migrate_course_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "from_version", + "value": { + "type": "string" + } + }, + { + "doc": "", + "name": "to_version", + "value": { + "type": "string" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Get migration status for the current contract\n\nReturns information about the current migration status and any\npending migrations that need to be completed.\n\n# Arguments\n* `env` - The Soroban environment\n\n# Returns\n* `String` - Migration status information", + "name": "get_migration_status", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + } +] diff --git a/schemas/course_registry_spec.json b/schemas/course_registry_spec.json new file mode 100644 index 0000000..e69de29 diff --git a/schemas/user_management-ts/.gitignore b/schemas/user_management-ts/.gitignore new file mode 100644 index 0000000..72aae85 --- /dev/null +++ b/schemas/user_management-ts/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +out/ diff --git a/schemas/user_management-ts/README.md b/schemas/user_management-ts/README.md new file mode 100644 index 0000000..2f2c886 --- /dev/null +++ b/schemas/user_management-ts/README.md @@ -0,0 +1,54 @@ +# user_management-ts JS + +JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `user_management-ts` via Soroban RPC. + +This library was automatically generated by Soroban CLI using a command similar to: + +```bash +soroban contract bindings ts \ + --rpc-url INSERT_RPC_URL_HERE \ + --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \ + --contract-id INSERT_CONTRACT_ID_HERE \ + --output-dir ./path/to/user_management-ts +``` + +The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily. + +# To publish or not to publish + +This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command. + +But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path: + +```json +"dependencies": { + "user_management-ts": "./path/to/this/folder" +} +``` + +However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract. + +```json +"scripts": { + "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name user_management-ts" +} +``` + +Obviously you need to adjust the above command based on the actual command you used to generate the library. + +# Use it + +Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods: + +```js +import { Contract, networks } from "user_management-ts" + +const contract = new Contract({ + ...networks.futurenet, // for example; check which networks this library exports + rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers +}) + +contract.| +``` + +As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code. diff --git a/schemas/user_management-ts/package.json b/schemas/user_management-ts/package.json new file mode 100644 index 0000000..b923caf --- /dev/null +++ b/schemas/user_management-ts/package.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.0", + "name": "user_management-ts", + "type": "module", + "exports": "./dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@stellar/stellar-sdk": "^14.1.1", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } +} diff --git a/schemas/user_management-ts/src/index.ts b/schemas/user_management-ts/src/index.ts new file mode 100644 index 0000000..899876b --- /dev/null +++ b/schemas/user_management-ts/src/index.ts @@ -0,0 +1,1334 @@ +import { Buffer } from "buffer"; +import { Address } from '@stellar/stellar-sdk'; +import { + AssembledTransaction, + Client as ContractClient, + ClientOptions as ContractClientOptions, + MethodOptions, + Result, + Spec as ContractSpec, +} from '@stellar/stellar-sdk/contract'; +import type { + u32, + i32, + u64, + i64, + u128, + i128, + u256, + i256, + Option, + Typepoint, + Duration, +} from '@stellar/stellar-sdk/contract'; +export * from '@stellar/stellar-sdk' +export * as contract from '@stellar/stellar-sdk/contract' +export * as rpc from '@stellar/stellar-sdk/rpc' + +if (typeof window !== 'undefined') { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || Buffer; +} + + + + +export const Errors = { + 1: {message:"AlreadInitialized"}, + 2: {message:"InvalidMaxPageSize"}, + 3: {message:"SystemNotInitialized"}, + 4: {message:"AccessDenied"}, + 5: {message:"SuperAdminNotRegular"}, + 6: {message:"OperationFailed"}, + 7: {message:"MaxAdminsReached"}, + 8: {message:"CannotRemoveSuperAdmin"}, + 9: {message:"UserProfileExists"}, + 10: {message:"NameRequired"}, + 11: {message:"EmailRequired"}, + 12: {message:"CountryRequired"}, + 15: {message:"InvalidEmailFormat"}, + 16: {message:"EmailAlreadyExists"}, + 17: {message:"InvalidField"}, + 19: {message:"InvalidProfilePicURL"}, + 20: {message:"UserNotFound"}, + 21: {message:"UserProfileNotFound"}, + 22: {message:"InactiveUser"}, + 23: {message:"PageParamTooLarge"}, + 24: {message:"InvalidTitleLength"}, + 25: {message:"PasswordMismatch"}, + 26: {message:"RateLimitExceeded"}, + 27: {message:"RateLimitNotConfigured"}, + 28: {message:"PasswordTooShort"}, + 29: {message:"PasswordTooLong"}, + 30: {message:"PasswordMissingUppercase"}, + 31: {message:"PasswordMissingLowercase"}, + 32: {message:"PasswordMissingDigit"}, + 33: {message:"PasswordMissingSpecialChar"}, + 34: {message:"RequiredFieldMissing"}, + 35: {message:"Unauthorized"} +} + +/** + * Errors that can occur during contract versioning operations + */ +export const VersioningError = { + /** + * Invalid version format + */ + 1: {message:"InvalidVersion"}, + /** + * Version not found in history + */ + 2: {message:"VersionNotFound"}, + /** + * Migration not compatible + */ + 3: {message:"MigrationNotCompatible"}, + /** + * Migration already completed + */ + 4: {message:"MigrationAlreadyCompleted"}, + /** + * Unauthorized migration attempt + */ + 5: {message:"UnauthorizedMigration"}, + /** + * Migration failed + */ + 6: {message:"MigrationFailed"} +} + + +export interface UserProfile { + /** + * User's contact email address (required, must be unique) + */ +contact_email: string; + /** + * User's country of residence (optional) + */ +country: Option; + /** + * User's full name (required) + */ +full_name: string; + /** + * User's profession or job title (optional) + */ +profession: Option; + /** + * User's profile picture URL (optional) + */ +profile_picture_url: Option; + /** + * User's learning goals or purpose (optional) + */ +purpose: Option; +} + +/** + * Data keys for contract storage + * + * Currently includes only UserProfile keyed by user Address + */ +export type DataKey = {tag: "UserProfile", values: readonly [string]} | {tag: "EmailIndex", values: readonly [string]}; + + +/** + * User profile information matching UI definition. + * + * This struct contains user profile data with required and optional fields + * as defined by the user interface requirements. + */ +export interface UserProfile { + /** + * User's contact email address (required, must be unique) + */ +contact_email: string; + /** + * User's country of residence (optional) + */ +country: Option; + /** + * User's full name (required) + */ +full_name: string; + /** + * User's profession or job title (optional) + */ +profession: Option; + /** + * User's profile picture URL (optional) + */ +profile_picture_url: Option; + /** + * User's learning goals or purpose (optional) + */ +purpose: Option; +} + + +/** + * Struct for profile update parameters + * Only includes fields that can be updated + */ +export interface ProfileUpdateParams { + /** + * User's country of residence + */ +country: Option; + /** + * User's full name (optional update) + */ +full_name: Option; + /** + * User's profession or job title + */ +profession: Option; + /** + * User's profile picture URL + */ +profile_picture_url: Option; + /** + * User's learning goals or purpose + */ +purpose: Option; +} + +/** + * User roles in the SkillCert platform. + * + * Defines the different types of users and their permission levels. + */ +export type UserRole = {tag: "Student", values: void} | {tag: "Instructor", values: void} | {tag: "Admin", values: void} | {tag: "SuperAdmin", values: void} | {tag: "Moderator", values: void} | {tag: "Support", values: void}; + +/** + * Granular permissions for RBAC system. + * + * Defines specific actions that can be granted or denied to users. + */ +export type Permission = {tag: "ViewUsers", values: void} | {tag: "EditUsers", values: void} | {tag: "DeleteUsers", values: void} | {tag: "CreateUsers", values: void} | {tag: "ViewCourses", values: void} | {tag: "CreateCourses", values: void} | {tag: "EditCourses", values: void} | {tag: "DeleteCourses", values: void} | {tag: "ManageCourseAccess", values: void} | {tag: "ManageSystem", values: void} | {tag: "ManageAdmins", values: void} | {tag: "ViewAnalytics", values: void} | {tag: "ModerateContent", values: void} | {tag: "ProvideSupport", values: void} | {tag: "ViewSupport", values: void}; + + +/** + * Role-based permissions mapping. + * + * Defines which permissions are granted to each role by default. + */ +export interface RolePermissions { + /** + * List of permissions granted to this role + */ +permissions: Array; + /** + * The role this permission set applies to + */ +role: UserRole; +} + + +/** + * User-specific permission overrides. + * + * Allows granting or revoking specific permissions to individual users. + */ +export interface UserPermissions { + /** + * Additional permissions granted beyond role defaults + */ +granted_permissions: Array; + /** + * Permissions explicitly revoked from role defaults + */ +revoked_permissions: Array; + /** + * The user address + */ +user: string; +} + +/** + * User account status. + * + * Represents the current state of a user's account. + */ +export type UserStatus = {tag: "Active", values: void} | {tag: "Inactive", values: void} | {tag: "Suspended", values: void}; + + +/** + * Lightweight user profile for listing operations. + * + * Contains essential user information for efficient querying and display in user lists. + */ +export interface LightProfile { + /** + * User's country of residence + */ +country: Option; + /** + * User's full name + */ +full_name: string; + /** + * User's profession or job title + */ +profession: Option; + /** + * User's role in the platform + */ +role: UserRole; + /** + * User's account status + */ +status: UserStatus; + /** + * User's blockchain address + */ +user_address: string; +} + + +/** + * Rate limiting configuration for user operations. + * + * Tracks rate limiting settings and current usage for spam protection. + */ +export interface RateLimitConfig { + /** + * Maximum operations allowed per window + */ +max_operations_per_window: u32; + /** + * Time window for rate limiting in seconds + */ +window_seconds: u64; +} + + +/** + * Rate limiting tracking data for a specific address. + * + * Stores the current usage count and window start time for rate limiting. + */ +export interface RateLimitData { + /** + * Current count of operations in this window + */ +count: u32; + /** + * Timestamp when the current window started + */ +window_start: u64; +} + + +/** + * Administrative configuration for the user management system. + * + * Contains system-wide settings and administrative information. + */ +export interface AdminConfig { + /** + * Whether the system has been initialized + */ +initialized: boolean; + /** + * Maximum allowed page size for queries + */ +max_page_size: u32; + /** + * Rate limiting configuration for user creation + */ +rate_limit_config: RateLimitConfig; + /** + * Address of the super administrator + */ +super_admin: string; + /** + * Total number of registered users + */ +total_user_count: u32; +} + + +/** + * Backup data structure for user management system. + * + * Contains all user data and system configuration for backup and recovery operations. + */ +export interface UserBackupData { + /** + * Administrative configuration + */ +admin_config: AdminConfig; + /** + * List of admin addresses + */ +admins: Array; + /** + * Backup timestamp + */ +backup_timestamp: u64; + /** + * Backup version for compatibility + */ +backup_version: string; + /** + * Email to address mapping for uniqueness + */ +email_mappings: Map; + /** + * All lightweight profiles for efficient queries + */ +light_profiles: Map; + /** + * All user profiles in the system + */ +user_profiles: Map; + /** + * List of all registered user addresses + */ +users_index: Array; +} + + +/** + * Pagination parameters for cursor-based pagination. + * + * Used to implement efficient pagination that avoids gas limit issues + * with large datasets by using cursor-based navigation. + */ +export interface PaginationParams { + /** + * Cursor for pagination (address of the last item from previous page) + */ +cursor: Option; + /** + * Maximum number of items to return per page + */ +limit: u32; +} + + +/** + * Pagination result with metadata for efficient navigation. + * + * Contains the paginated data along with pagination metadata + * to enable cursor-based navigation. + */ +export interface PaginatedLightProfiles { + /** + * The paginated data items + */ +data: Array; + /** + * Whether there are more pages available + */ +has_more: boolean; + /** + * Cursor for the next page (None if this is the last page) + */ +next_cursor: Option; + /** + * Total count of items matching the filter (optional, may be expensive to compute) + */ +total_count: Option; +} + +/** + * Storage keys for different data types in the user management contract. + * + * This enum defines the various keys used to store and retrieve + * user data from the contract's persistent storage. + */ +export type DataKey = {tag: "UserProfile", values: readonly [string]} | {tag: "Admin", values: readonly [string]} | {tag: "UserProfileLight", values: readonly [string]} | {tag: "UsersIndex", values: void} | {tag: "EmailIndex", values: readonly [string]} | {tag: "Admins", values: void} | {tag: "UserRole", values: readonly [string]} | {tag: "AdminConfig", values: void} | {tag: "RateLimit", values: readonly [string]} | {tag: "RolePermissions", values: readonly [UserRole]} | {tag: "UserPermissions", values: readonly [string]} | {tag: "DefaultRolePermissions", values: void}; + +export interface Client { + /** + * Construct and simulate a get_user_profile transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Retrieve a user profile for the authenticated user. + * + * This function fetches the complete user profile associated with the provided + * blockchain address. The user must be authenticated; otherwise, the function + * will panic. + * + * ### Arguments + * + * * `env` - The Soroban environment. + * * `user` - The address of the user whose profile is being requested. + * + * ### Returns + * + * Returns the `UserProfile` corresponding to the authenticated user. + * + * ### Panics + * + * * If the user is not authenticated (`require_auth` fails). + * * If the user profile does not exist (`UserNotFound` error). + * + * ### Examples + * + * ```rust + * // Assuming the user is authenticated in the environment + * let profile = contract.get_user_profile(env.clone(), my_address); + * println!("User full name: {}", profile.full_name); + * ``` + * + * ### Notes + * + * * Only the user themselves can fetch their profile; there is no admin override + * in this function. + * * If the profile is not found in storage, the function will panic with + * `UserNotFound`. + */ + get_user_profile: ({user}: {user: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a get_user_by_id transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Retrieve a user profile by their address. + * + * This function fetches a complete user profile using the user's blockchain address. + * Access may be restricted based on the requester's permissions. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `requester` - The address of the user requesting the profile + * * `user_id` - The address of the user whose profile is being requested + * + * # Returns + * + * Returns the requested `UserProfile`. + * + * # Panics + * + * * If the user profile doesn't exist + * * If the requester doesn't have permission to view the profile + * * If the requester is not the user themselves or an admin + * + * # Examples + * + * ```rust + * // Get your own profile + * let my_profile = contract.get_user_by_id(env.clone(), my_address, my_address); + * + * // Admin getting any user's profile + * let user_profile = contract.get_user_by_id(env.clone(), admin_address, user_address); + * ``` + * + * # Edge Cases + * + * * **Non-existent user**: Will panic with appropriate error message + * * **Inactive user**: Returns profile but status will be `UserStatus::Inactive` + * * **Permission denied**: + */ + get_user_by_id: ({requester, user_id}: {requester: string, user_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a create_user_profile transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Create a new user profile + * + * Creates a new user profile using a UserProfile struct. + * Validates mandatory fields (full_name and contact_email) and saves the profile. + * + * # Arguments + * * `env` - Soroban environment + * * `user` - Address of the user whose profile is being created + * * `profile` - UserProfile struct containing all profile data + * + * # Returns + * * `UserProfile` - The created user profile + * + * # Panics + * * If mandatory fields (full_name, contact_email) are missing + * * If user profile already exists + * * If email format is invalid + * * If validation rules are violated + * + * # Events + * Emits a user creation event upon successful creation + * + * # Examples + * + * ```rust + * let profile = UserProfile { + * full_name: "John Doe".try_into().unwrap(), + * contact_email: "john@example.com".try_into().unwrap(), + * role: UserRole::Student, + * status: UserStatus::Active, + * country: Some("US".try_into().unwrap()), + * ..Default::default() + * }; + * + * let created_profile = contract.create_user_profile(env, user_address, profile); + * ``` + * + * # Edge Cases + * + * * **Duplicate profile**: Will panic if user al + */ + create_user_profile: ({user, profile}: {user: string, profile: UserProfile}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a edit_user_profile transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Edit an existing user profile + * + * Updates an existing user profile with new values for allowed fields. + * Only the user themselves or administrators can perform updates. + * Email and role fields cannot be updated through this function. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address of the user performing the update + * * `user_id` - Address of the user whose profile is being updated + * * `updates` - ProfileUpdateParams containing fields to update + * + * # Returns + * * `UserProfile` - The updated user profile + * + * # Panics + * * If caller authentication fails + * * If user profile doesn't exist + * * If caller lacks permission to edit + * * If any field validation fails + * * If user is inactive + * + * # Events + * Emits a user update event upon successful profile update + * + * # Examples + * + * ```rust + * let updates = ProfileUpdateParams { + * full_name: Some("Jane Doe".try_into().unwrap()), + * country: Some("CA".try_into().unwrap()), + * bio: Some("Updated bio".try_into().unwrap()), + * ..Default::default() + * }; + * + * let updated_profile = contract.edit_user_profile(env, caller_addres + */ + edit_user_profile: ({caller, user_id, updates}: {caller: string, user_id: string, updates: ProfileUpdateParams}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a is_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Check if an address has admin privileges. + * + * This function is used by other contracts to verify admin status + * for cross-contract authorization checks. + * + * # Arguments + * + * * `env` - The Soroban environment + * * `who` - The address to check for admin privileges + * + * # Returns + * + * Returns `true` if the address has admin privileges, `false` otherwise. + * + * # Examples + * + * ```rust + * // Check if user is admin + * let is_admin = contract.is_admin(env.clone(), user_address); + * if is_admin { + * // Perform admin operations + * } + * + * // Cross-contract admin check + * let can_perform_action = contract.is_admin(env.clone(), caller_address); + * ``` + * + * # Edge Cases + * + * * **System not initialized**: Returns `false` if admin system hasn't been set up + * * **Non-existent user**: Returns `false` for addresses that don't exist + * * **Regular users**: Always returns `false` for non-admin users + */ + is_admin: ({who}: {who: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a delete_user transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Delete (deactivate) a user account + * + * Performs a soft delete by marking the user as inactive instead of permanent deletion. + * Only admins or the user themselves can trigger deletion. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the deletion (must be admin or the user themselves) + * * `user_id` - Address of the user to be deactivated + * + * # Panics + * * If caller authentication fails + * * If user doesn't exist + * * If caller is neither admin nor the user themselves + * * If user is already inactive + * + * # Events + * Emits a user deactivation event upon successful deletion + * + * # Examples + * + * ```rust + * // User deleting their own account + * contract.delete_user(env.clone(), user_address, user_address); + * + * // Admin deleting another user's account + * contract.delete_user(env.clone(), admin_address, user_to_delete); + * ``` + * + * # Edge Cases + * + * * **Already inactive**: Will panic if trying to delete an already inactive user + * * **Permission denied**: Non-admin users can only delete their own accounts + * * **Data preservation**: User data is preserved + */ + delete_user: ({caller, user_id}: {caller: string, user_id: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a list_all_users transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Lists all registered users with pagination and filtering (admin-only) + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be admin) + * * `page` - Zero-based page index + * * `page_size` - Number of items per page (must be > 0) + * * `role_filter` - Optional role filter + * * `country_filter` - Optional country filter + * * `status_filter` - Optional status filter + * + * # Returns + * * `Vec` - Filtered and paginated lightweight user profiles + * + * # Panics + * * If caller is not an admin + * * If page_size is 0 or exceeds maximum allowed + * * If system is not initialized + * + * # Examples + * + * ```rust + * // Get first page with 10 users + * let users = contract.list_all_users( + * env.clone(), + * admin_address, + * 0, // page 0 + * 10, // page size + * None, None, None // no filters + * ); + * + * // Filter by role and country + * let students = contract.list_all_users( + * env.clone(), + * admin_address, + * 0, 20, + * Some(UserRole::Student), + * Some("US".try_into().unwrap()), + * None + * ); + * ``` + * + * # Edge Cases + * + * * **Empty results**: Returns empty vector if no users match filter + */ + list_all_users: ({caller, page, page_size, role_filter, country_filter, status_filter}: {caller: string, page: u32, page_size: u32, role_filter: Option, country_filter: Option, status_filter: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a list_all_users_advanced transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Lists all registered users with advanced filtering including text search (admin-only). + * + * This is the new version that supports text search functionality. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be admin) + * * `page` - Zero-based page index + * * `page_size` - Number of items per page + * * `role_filter` - Optional role filter + * * `country_filter` - Optional country filter + * * `status_filter` - Optional status filter + * * `search_text` - Optional text search in name and profession + * + * # Returns + * * `Vec` - Filtered and paginated lightweight user profiles + */ + list_all_users_advanced: ({caller, page, page_size, role_filter, country_filter, status_filter, search_text}: {caller: string, page: u32, page_size: u32, role_filter: Option, country_filter: Option, status_filter: Option, search_text: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a list_all_users_cursor transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Lists all registered users with cursor-based pagination and filtering (admin-only) + * + * This function implements efficient cursor-based pagination to avoid gas limit issues + * when dealing with large datasets. It returns a PaginatedResult with metadata for + * efficient navigation. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be admin) + * * `pagination` - Pagination parameters including cursor and limit + * * `role_filter` - Optional filter for user role + * * `status_filter` - Optional filter for user status + * + * # Returns + * * `PaginatedLightProfiles` - Paginated results with navigation metadata + */ + list_all_users_cursor: ({caller, pagination, role_filter, status_filter}: {caller: string, pagination: PaginationParams, role_filter: Option, status_filter: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a initialize_system transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Initialize the admin system (one-time only) + * + * # Arguments + * * `env` - Soroban environment + * * `initializer` - Address performing the initialization + * * `super_admin` - Address that will become the super admin + * * `max_page_size` - Optional maximum page size (default: 100, max: 1000) + * + * # Returns + * * `AdminConfig` - The created admin configuration + * + * # Panics + * * If system has already been initialized + * * If max_page_size exceeds 1000 + * + * # Examples + * + * ```rust + * // Initialize with default settings + * let config = contract.initialize_system( + * env.clone(), + * deployer_address, + * super_admin_address, + * None + * ); + * + * // Initialize with custom page size + * let config = contract.initialize_system( + * env.clone(), + * deployer_address, + * super_admin_address, + * Some(500) + * ); + * ``` + * + * # Edge Cases + * + * * **Double initialization**: Will panic if called more than once + * * **Invalid page size**: Will panic if max_page_size > 1000 + * * **Super admin privileges**: Super admin cannot be removed after initialization + */ + initialize_system: ({initializer, super_admin, max_page_size}: {initializer: string, super_admin: string, max_page_size: Option}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a add_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Add a new admin (super admin only) + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be super admin) + * * `new_admin` - Address to be added as admin + * + * # Panics + * * If caller is not the super admin + * * If system is not initialized + * * If new_admin is already an admin + * + * # Examples + * + * ```rust + * // Super admin adding a new admin + * contract.add_admin(env.clone(), super_admin_address, new_admin_address); + * ``` + * + * # Edge Cases + * + * * **Already admin**: Will panic if trying to add an existing admin + * * **Self-promotion**: Super admin cannot add themselves (redundant) + * * **Non-existent user**: Can add admin privileges to any address + */ + add_admin: ({caller, new_admin}: {caller: string, new_admin: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a remove_admin transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Remove an admin (super admin only) + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be super admin) + * * `admin_to_remove` - Address to be removed from admins + * + * # Panics + * * If caller is not the super admin + * * If system is not initialized + * * If admin_to_remove is not an admin + * * If trying to remove the super admin + * + * # Examples + * + * ```rust + * // Super admin removing another admin + * contract.remove_admin(env.clone(), super_admin_address, admin_to_remove); + * ``` + * + * # Edge Cases + * + * * **Super admin protection**: Cannot remove the super admin + * * **Non-admin**: Will panic if trying to remove a non-admin address + * * **Self-removal**: Super admin cannot remove themselves + */ + remove_admin: ({caller, admin_to_remove}: {caller: string, admin_to_remove: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_admins transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get list of all admins (admin only) + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the call (must be admin) + * + * # Returns + * * `Vec
` - List of all admin addresses including super admin + * + * # Panics + * * If caller is not an admin + * * If system is not initialized + * + * # Examples + * + * ```rust + * // Get all admin addresses + * let admins = contract.get_admins(env.clone(), admin_address); + * for admin in admins { + * // Process each admin address + * } + * ``` + * + * # Edge Cases + * + * * **Empty list**: Returns vector with only super admin if no other admins exist + * * **Admin only**: Only admins can view the admin list + * * **Order**: Super admin is typically first in the list + */ + get_admins: ({caller}: {caller: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise>> + + /** + * Construct and simulate a is_system_initialized transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Check if the system is initialized + * + * # Arguments + * * `env` - Soroban environment + * + * # Returns + * * `bool` - True if system is initialized + * + * # Examples + * + * ```rust + * // Check if admin system is ready + * let is_initialized = contract.is_system_initialized(env.clone()); + * if !is_initialized { + * // Initialize the system first + * contract.initialize_system(env, deployer, super_admin, None); + * } + * ``` + * + * # Edge Cases + * + * * **Fresh deployment**: Returns `false` for newly deployed contracts + * * **Public access**: Anyone can check initialization status + * * **One-time check**: Once initialized, always returns `true` + */ + is_system_initialized: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a get_contract_version transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Get the current contract version + * + * Returns the semantic version of the current contract deployment. + * This is useful for tracking contract upgrades and compatibility. + * + * # Arguments + * * `_env` - The Soroban environment (unused) + * + * # Returns + * * `String` - The current contract version + */ + get_contract_version: (options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a export_user_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Export all user data for backup purposes (admin only) + * + * This function exports all user profiles and administrative data + * for backup and recovery purposes. Only admins can perform this operation. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the export (must be admin) + * + * # Returns + * * `UserBackupData` - Complete backup data structure + * + * # Panics + * * If caller is not an admin + * * If system is not initialized + */ + export_user_data: ({caller}: {caller: string}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + + /** + * Construct and simulate a import_user_data transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. + * Import user data from backup (admin only) + * + * This function imports user data from a backup structure. + * Only admins can perform this operation. This will overwrite existing data. + * + * # Arguments + * * `env` - Soroban environment + * * `caller` - Address performing the import (must be admin) + * * `backup_data` - Backup data structure to import + * + * # Returns + * * `u32` - Number of users imported + * + * # Panics + * * If caller is not an admin + * * If backup data is invalid + * * If import operation fails + */ + import_user_data: ({caller, backup_data}: {caller: string, backup_data: UserBackupData}, options?: { + /** + * The fee to pay for the transaction. Default: BASE_FEE + */ + fee?: number; + + /** + * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT + */ + timeoutInSeconds?: number; + + /** + * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true + */ + simulate?: boolean; + }) => Promise> + +} +export class Client extends ContractClient { + static async deploy( + /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ + options: MethodOptions & + Omit & { + /** The hash of the Wasm blob, which must already be installed on-chain. */ + wasmHash: Buffer | string; + /** Salt used to generate the contract's ID. Passed through to {@link Operation.createCustomContract}. Default: random. */ + salt?: Buffer | Uint8Array; + /** The format used to decode `wasmHash`, if it's provided as a string. */ + format?: "hex" | "base64"; + } + ): Promise> { + return ContractClient.deploy(null, options) + } + constructor(public readonly options: ContractClientOptions) { + super( + new ContractSpec([ "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAIAAAAAAAAAARQWxyZWFkSW5pdGlhbGl6ZWQAAAAAAAABAAAAAAAAABJJbnZhbGlkTWF4UGFnZVNpemUAAAAAAAIAAAAAAAAAFFN5c3RlbU5vdEluaXRpYWxpemVkAAAAAwAAAAAAAAAMQWNjZXNzRGVuaWVkAAAABAAAAAAAAAAUU3VwZXJBZG1pbk5vdFJlZ3VsYXIAAAAFAAAAAAAAAA9PcGVyYXRpb25GYWlsZWQAAAAABgAAAAAAAAAQTWF4QWRtaW5zUmVhY2hlZAAAAAcAAAAAAAAAFkNhbm5vdFJlbW92ZVN1cGVyQWRtaW4AAAAAAAgAAAAAAAAAEVVzZXJQcm9maWxlRXhpc3RzAAAAAAAACQAAAAAAAAAMTmFtZVJlcXVpcmVkAAAACgAAAAAAAAANRW1haWxSZXF1aXJlZAAAAAAAAAsAAAAAAAAAD0NvdW50cnlSZXF1aXJlZAAAAAAMAAAAAAAAABJJbnZhbGlkRW1haWxGb3JtYXQAAAAAAA8AAAAAAAAAEkVtYWlsQWxyZWFkeUV4aXN0cwAAAAAAEAAAAAAAAAAMSW52YWxpZEZpZWxkAAAAEQAAAAAAAAAUSW52YWxpZFByb2ZpbGVQaWNVUkwAAAATAAAAAAAAAAxVc2VyTm90Rm91bmQAAAAUAAAAAAAAABNVc2VyUHJvZmlsZU5vdEZvdW5kAAAAABUAAAAAAAAADEluYWN0aXZlVXNlcgAAABYAAAAAAAAAEVBhZ2VQYXJhbVRvb0xhcmdlAAAAAAAAFwAAAAAAAAASSW52YWxpZFRpdGxlTGVuZ3RoAAAAAAAYAAAAAAAAABBQYXNzd29yZE1pc21hdGNoAAAAGQAAAAAAAAARUmF0ZUxpbWl0RXhjZWVkZWQAAAAAAAAaAAAAAAAAABZSYXRlTGltaXROb3RDb25maWd1cmVkAAAAAAAbAAAAAAAAABBQYXNzd29yZFRvb1Nob3J0AAAAHAAAAAAAAAAPUGFzc3dvcmRUb29Mb25nAAAAAB0AAAAAAAAAGFBhc3N3b3JkTWlzc2luZ1VwcGVyY2FzZQAAAB4AAAAAAAAAGFBhc3N3b3JkTWlzc2luZ0xvd2VyY2FzZQAAAB8AAAAAAAAAFFBhc3N3b3JkTWlzc2luZ0RpZ2l0AAAAIAAAAAAAAAAaUGFzc3dvcmRNaXNzaW5nU3BlY2lhbENoYXIAAAAAACEAAAAAAAAAFFJlcXVpcmVkRmllbGRNaXNzaW5nAAAAIgAAAAAAAAAMVW5hdXRob3JpemVkAAAAIw==", + "AAAABAAAADtFcnJvcnMgdGhhdCBjYW4gb2NjdXIgZHVyaW5nIGNvbnRyYWN0IHZlcnNpb25pbmcgb3BlcmF0aW9ucwAAAAAAAAAAD1ZlcnNpb25pbmdFcnJvcgAAAAAGAAAAFkludmFsaWQgdmVyc2lvbiBmb3JtYXQAAAAAAA5JbnZhbGlkVmVyc2lvbgAAAAAAAQAAABxWZXJzaW9uIG5vdCBmb3VuZCBpbiBoaXN0b3J5AAAAD1ZlcnNpb25Ob3RGb3VuZAAAAAACAAAAGE1pZ3JhdGlvbiBub3QgY29tcGF0aWJsZQAAABZNaWdyYXRpb25Ob3RDb21wYXRpYmxlAAAAAAADAAAAG01pZ3JhdGlvbiBhbHJlYWR5IGNvbXBsZXRlZAAAAAAZTWlncmF0aW9uQWxyZWFkeUNvbXBsZXRlZAAAAAAAAAQAAAAeVW5hdXRob3JpemVkIG1pZ3JhdGlvbiBhdHRlbXB0AAAAAAAVVW5hdXRob3JpemVkTWlncmF0aW9uAAAAAAAABQAAABBNaWdyYXRpb24gZmFpbGVkAAAAD01pZ3JhdGlvbkZhaWxlZAAAAAAG", + "AAAAAQAAAAAAAAAAAAAAC1VzZXJQcm9maWxlAAAAAAYAAAA3VXNlcidzIGNvbnRhY3QgZW1haWwgYWRkcmVzcyAocmVxdWlyZWQsIG11c3QgYmUgdW5pcXVlKQAAAAANY29udGFjdF9lbWFpbAAAAAAAABAAAAAmVXNlcidzIGNvdW50cnkgb2YgcmVzaWRlbmNlIChvcHRpb25hbCkAAAAAAAdjb3VudHJ5AAAAA+gAAAAQAAAAG1VzZXIncyBmdWxsIG5hbWUgKHJlcXVpcmVkKQAAAAAJZnVsbF9uYW1lAAAAAAAAEAAAAClVc2VyJ3MgcHJvZmVzc2lvbiBvciBqb2IgdGl0bGUgKG9wdGlvbmFsKQAAAAAAAApwcm9mZXNzaW9uAAAAAAPoAAAAEAAAACVVc2VyJ3MgcHJvZmlsZSBwaWN0dXJlIFVSTCAob3B0aW9uYWwpAAAAAAAAE3Byb2ZpbGVfcGljdHVyZV91cmwAAAAD6AAAABAAAAArVXNlcidzIGxlYXJuaW5nIGdvYWxzIG9yIHB1cnBvc2UgKG9wdGlvbmFsKQAAAAAHcHVycG9zZQAAAAPoAAAAEA==", + "AAAAAgAAAFlEYXRhIGtleXMgZm9yIGNvbnRyYWN0IHN0b3JhZ2UKCkN1cnJlbnRseSBpbmNsdWRlcyBvbmx5IFVzZXJQcm9maWxlIGtleWVkIGJ5IHVzZXIgQWRkcmVzcwAAAAAAAAAAAAAHRGF0YUtleQAAAAACAAAAAQAAAAAAAAALVXNlclByb2ZpbGUAAAAAAQAAABMAAAABAAAAAAAAAApFbWFpbEluZGV4AAAAAAABAAAAEA==", + "AAAAAQAAAKlVc2VyIHByb2ZpbGUgaW5mb3JtYXRpb24gbWF0Y2hpbmcgVUkgZGVmaW5pdGlvbi4KClRoaXMgc3RydWN0IGNvbnRhaW5zIHVzZXIgcHJvZmlsZSBkYXRhIHdpdGggcmVxdWlyZWQgYW5kIG9wdGlvbmFsIGZpZWxkcwphcyBkZWZpbmVkIGJ5IHRoZSB1c2VyIGludGVyZmFjZSByZXF1aXJlbWVudHMuAAAAAAAAAAAAAAtVc2VyUHJvZmlsZQAAAAAGAAAAN1VzZXIncyBjb250YWN0IGVtYWlsIGFkZHJlc3MgKHJlcXVpcmVkLCBtdXN0IGJlIHVuaXF1ZSkAAAAADWNvbnRhY3RfZW1haWwAAAAAAAAQAAAAJlVzZXIncyBjb3VudHJ5IG9mIHJlc2lkZW5jZSAob3B0aW9uYWwpAAAAAAAHY291bnRyeQAAAAPoAAAAEAAAABtVc2VyJ3MgZnVsbCBuYW1lIChyZXF1aXJlZCkAAAAACWZ1bGxfbmFtZQAAAAAAABAAAAApVXNlcidzIHByb2Zlc3Npb24gb3Igam9iIHRpdGxlIChvcHRpb25hbCkAAAAAAAAKcHJvZmVzc2lvbgAAAAAD6AAAABAAAAAlVXNlcidzIHByb2ZpbGUgcGljdHVyZSBVUkwgKG9wdGlvbmFsKQAAAAAAABNwcm9maWxlX3BpY3R1cmVfdXJsAAAAA+gAAAAQAAAAK1VzZXIncyBsZWFybmluZyBnb2FscyBvciBwdXJwb3NlIChvcHRpb25hbCkAAAAAB3B1cnBvc2UAAAAD6AAAABA=", + "AAAAAQAAAE1TdHJ1Y3QgZm9yIHByb2ZpbGUgdXBkYXRlIHBhcmFtZXRlcnMKT25seSBpbmNsdWRlcyBmaWVsZHMgdGhhdCBjYW4gYmUgdXBkYXRlZAAAAAAAAAAAAAATUHJvZmlsZVVwZGF0ZVBhcmFtcwAAAAAFAAAAG1VzZXIncyBjb3VudHJ5IG9mIHJlc2lkZW5jZQAAAAAHY291bnRyeQAAAAPoAAAAEAAAACJVc2VyJ3MgZnVsbCBuYW1lIChvcHRpb25hbCB1cGRhdGUpAAAAAAAJZnVsbF9uYW1lAAAAAAAD6AAAABAAAAAeVXNlcidzIHByb2Zlc3Npb24gb3Igam9iIHRpdGxlAAAAAAAKcHJvZmVzc2lvbgAAAAAD6AAAABAAAAAaVXNlcidzIHByb2ZpbGUgcGljdHVyZSBVUkwAAAAAABNwcm9maWxlX3BpY3R1cmVfdXJsAAAAA+gAAAAQAAAAIFVzZXIncyBsZWFybmluZyBnb2FscyBvciBwdXJwb3NlAAAAB3B1cnBvc2UAAAAD6AAAABA=", + "AAAAAgAAAGhVc2VyIHJvbGVzIGluIHRoZSBTa2lsbENlcnQgcGxhdGZvcm0uCgpEZWZpbmVzIHRoZSBkaWZmZXJlbnQgdHlwZXMgb2YgdXNlcnMgYW5kIHRoZWlyIHBlcm1pc3Npb24gbGV2ZWxzLgAAAAAAAAAIVXNlclJvbGUAAAAGAAAAAAAAAC9SZWd1bGFyIHBsYXRmb3JtIHVzZXIgd2hvIGNhbiBlbnJvbGwgaW4gY291cnNlcwAAAAAHU3R1ZGVudAAAAAAAAAAAJlVzZXIgd2hvIGNhbiBjcmVhdGUgYW5kIG1hbmFnZSBjb3Vyc2VzAAAAAAAKSW5zdHJ1Y3RvcgAAAAAAAAAAAC9QbGF0Zm9ybSBhZG1pbmlzdHJhdG9yIHdpdGggZWxldmF0ZWQgcHJpdmlsZWdlcwAAAAAFQWRtaW4AAAAAAAAAAAAAK1N1cGVyIGFkbWluaXN0cmF0b3Igd2l0aCBmdWxsIHN5c3RlbSBhY2Nlc3MAAAAAClN1cGVyQWRtaW4AAAAAAAAAAAAxQ29udGVudCBtb2RlcmF0b3Igd2l0aCBjb3Vyc2UgY29udGVudCBwZXJtaXNzaW9ucwAAAAAAAAlNb2RlcmF0b3IAAAAAAAAAAAAALlN1cHBvcnQgc3RhZmYgd2l0aCB1c2VyIGFzc2lzdGFuY2UgcGVybWlzc2lvbnMAAAAAAAdTdXBwb3J0AA==", + "AAAAAgAAAGdHcmFudWxhciBwZXJtaXNzaW9ucyBmb3IgUkJBQyBzeXN0ZW0uCgpEZWZpbmVzIHNwZWNpZmljIGFjdGlvbnMgdGhhdCBjYW4gYmUgZ3JhbnRlZCBvciBkZW5pZWQgdG8gdXNlcnMuAAAAAAAAAAAKUGVybWlzc2lvbgAAAAAADwAAAAAAAAAWQ2FuIHZpZXcgdXNlciBwcm9maWxlcwAAAAAACVZpZXdVc2VycwAAAAAAAAAAAAAmQ2FuIGVkaXQgdXNlciBwcm9maWxlcyAob3duIG9yIG90aGVycykAAAAAAAlFZGl0VXNlcnMAAAAAAAAAAAAAG0NhbiBkZWxldGUvZGVhY3RpdmF0ZSB1c2VycwAAAAALRGVsZXRlVXNlcnMAAAAAAAAAABxDYW4gY3JlYXRlIG5ldyB1c2VyIGFjY291bnRzAAAAC0NyZWF0ZVVzZXJzAAAAAAAAAAAXQ2FuIHZpZXcgY291cnNlIGRldGFpbHMAAAAAC1ZpZXdDb3Vyc2VzAAAAAAAAAAAWQ2FuIGNyZWF0ZSBuZXcgY291cnNlcwAAAAAADUNyZWF0ZUNvdXJzZXMAAAAAAAAAAAAAF0NhbiBlZGl0IGNvdXJzZSBjb250ZW50AAAAAAtFZGl0Q291cnNlcwAAAAAAAAAAEkNhbiBkZWxldGUgY291cnNlcwAAAAAADURlbGV0ZUNvdXJzZXMAAAAAAAAAAAAAJ0NhbiBtYW5hZ2UgY291cnNlIGFjY2VzcyAoZ3JhbnQvcmV2b2tlKQAAAAASTWFuYWdlQ291cnNlQWNjZXNzAAAAAAAAAAAAH0NhbiBtYW5hZ2Ugc3lzdGVtIGNvbmZpZ3VyYXRpb24AAAAADE1hbmFnZVN5c3RlbQAAAAAAAAAWQ2FuIG1hbmFnZSBhZG1pbiByb2xlcwAAAAAADE1hbmFnZUFkbWlucwAAAAAAAAAZQ2FuIHZpZXcgc3lzdGVtIGFuYWx5dGljcwAAAAAAAA1WaWV3QW5hbHl0aWNzAAAAAAAAAAAAABRDYW4gbW9kZXJhdGUgY29udGVudAAAAA9Nb2RlcmF0ZUNvbnRlbnQAAAAAAAAAABhDYW4gcHJvdmlkZSB1c2VyIHN1cHBvcnQAAAAOUHJvdmlkZVN1cHBvcnQAAAAAAAAAAAAYQ2FuIHZpZXcgc3VwcG9ydCB0aWNrZXRzAAAAC1ZpZXdTdXBwb3J0AA==", + "AAAAAQAAAF9Sb2xlLWJhc2VkIHBlcm1pc3Npb25zIG1hcHBpbmcuCgpEZWZpbmVzIHdoaWNoIHBlcm1pc3Npb25zIGFyZSBncmFudGVkIHRvIGVhY2ggcm9sZSBieSBkZWZhdWx0LgAAAAAAAAAAD1JvbGVQZXJtaXNzaW9ucwAAAAACAAAAKExpc3Qgb2YgcGVybWlzc2lvbnMgZ3JhbnRlZCB0byB0aGlzIHJvbGUAAAALcGVybWlzc2lvbnMAAAAD6gAAB9AAAAAKUGVybWlzc2lvbgAAAAAAJ1RoZSByb2xlIHRoaXMgcGVybWlzc2lvbiBzZXQgYXBwbGllcyB0bwAAAAAEcm9sZQAAB9AAAAAIVXNlclJvbGU=", + "AAAAAQAAAGpVc2VyLXNwZWNpZmljIHBlcm1pc3Npb24gb3ZlcnJpZGVzLgoKQWxsb3dzIGdyYW50aW5nIG9yIHJldm9raW5nIHNwZWNpZmljIHBlcm1pc3Npb25zIHRvIGluZGl2aWR1YWwgdXNlcnMuAAAAAAAAAAAAD1VzZXJQZXJtaXNzaW9ucwAAAAADAAAAM0FkZGl0aW9uYWwgcGVybWlzc2lvbnMgZ3JhbnRlZCBiZXlvbmQgcm9sZSBkZWZhdWx0cwAAAAATZ3JhbnRlZF9wZXJtaXNzaW9ucwAAAAPqAAAH0AAAAApQZXJtaXNzaW9uAAAAAAAxUGVybWlzc2lvbnMgZXhwbGljaXRseSByZXZva2VkIGZyb20gcm9sZSBkZWZhdWx0cwAAAAAAABNyZXZva2VkX3Blcm1pc3Npb25zAAAAA+oAAAfQAAAAClBlcm1pc3Npb24AAAAAABBUaGUgdXNlciBhZGRyZXNzAAAABHVzZXIAAAAT", + "AAAAAgAAAEdVc2VyIGFjY291bnQgc3RhdHVzLgoKUmVwcmVzZW50cyB0aGUgY3VycmVudCBzdGF0ZSBvZiBhIHVzZXIncyBhY2NvdW50LgAAAAAAAAAAClVzZXJTdGF0dXMAAAAAAAMAAAAAAAAAJVVzZXIgYWNjb3VudCBpcyBhY3RpdmUgYW5kIGZ1bmN0aW9uYWwAAAAAAAAGQWN0aXZlAAAAAAAAAAAAG1VzZXIgYWNjb3VudCBpcyBkZWFjdGl2YXRlZAAAAAAISW5hY3RpdmUAAAAAAAAAJVVzZXIgYWNjb3VudCBpcyB0ZW1wb3JhcmlseSBzdXNwZW5kZWQAAAAAAAAJU3VzcGVuZGVkAAAA", + "AAAAAQAAAIdMaWdodHdlaWdodCB1c2VyIHByb2ZpbGUgZm9yIGxpc3Rpbmcgb3BlcmF0aW9ucy4KCkNvbnRhaW5zIGVzc2VudGlhbCB1c2VyIGluZm9ybWF0aW9uIGZvciBlZmZpY2llbnQgcXVlcnlpbmcgYW5kIGRpc3BsYXkgaW4gdXNlciBsaXN0cy4AAAAAAAAAAAxMaWdodFByb2ZpbGUAAAAGAAAAG1VzZXIncyBjb3VudHJ5IG9mIHJlc2lkZW5jZQAAAAAHY291bnRyeQAAAAPoAAAAEAAAABBVc2VyJ3MgZnVsbCBuYW1lAAAACWZ1bGxfbmFtZQAAAAAAABAAAAAeVXNlcidzIHByb2Zlc3Npb24gb3Igam9iIHRpdGxlAAAAAAAKcHJvZmVzc2lvbgAAAAAD6AAAABAAAAAbVXNlcidzIHJvbGUgaW4gdGhlIHBsYXRmb3JtAAAAAARyb2xlAAAH0AAAAAhVc2VyUm9sZQAAABVVc2VyJ3MgYWNjb3VudCBzdGF0dXMAAAAAAAAGc3RhdHVzAAAAAAfQAAAAClVzZXJTdGF0dXMAAAAAABlVc2VyJ3MgYmxvY2tjaGFpbiBhZGRyZXNzAAAAAAAADHVzZXJfYWRkcmVzcwAAABM=", + "AAAAAQAAAHZSYXRlIGxpbWl0aW5nIGNvbmZpZ3VyYXRpb24gZm9yIHVzZXIgb3BlcmF0aW9ucy4KClRyYWNrcyByYXRlIGxpbWl0aW5nIHNldHRpbmdzIGFuZCBjdXJyZW50IHVzYWdlIGZvciBzcGFtIHByb3RlY3Rpb24uAAAAAAAAAAAAD1JhdGVMaW1pdENvbmZpZwAAAAACAAAAJU1heGltdW0gb3BlcmF0aW9ucyBhbGxvd2VkIHBlciB3aW5kb3cAAAAAAAAZbWF4X29wZXJhdGlvbnNfcGVyX3dpbmRvdwAAAAAAAAQAAAAoVGltZSB3aW5kb3cgZm9yIHJhdGUgbGltaXRpbmcgaW4gc2Vjb25kcwAAAA53aW5kb3dfc2Vjb25kcwAAAAAABg==", + "AAAAAQAAAHxSYXRlIGxpbWl0aW5nIHRyYWNraW5nIGRhdGEgZm9yIGEgc3BlY2lmaWMgYWRkcmVzcy4KClN0b3JlcyB0aGUgY3VycmVudCB1c2FnZSBjb3VudCBhbmQgd2luZG93IHN0YXJ0IHRpbWUgZm9yIHJhdGUgbGltaXRpbmcuAAAAAAAAAA1SYXRlTGltaXREYXRhAAAAAAAAAgAAACpDdXJyZW50IGNvdW50IG9mIG9wZXJhdGlvbnMgaW4gdGhpcyB3aW5kb3cAAAAAAAVjb3VudAAAAAAAAAQAAAApVGltZXN0YW1wIHdoZW4gdGhlIGN1cnJlbnQgd2luZG93IHN0YXJ0ZWQAAAAAAAAMd2luZG93X3N0YXJ0AAAABg==", + "AAAAAQAAAHtBZG1pbmlzdHJhdGl2ZSBjb25maWd1cmF0aW9uIGZvciB0aGUgdXNlciBtYW5hZ2VtZW50IHN5c3RlbS4KCkNvbnRhaW5zIHN5c3RlbS13aWRlIHNldHRpbmdzIGFuZCBhZG1pbmlzdHJhdGl2ZSBpbmZvcm1hdGlvbi4AAAAAAAAAAAtBZG1pbkNvbmZpZwAAAAAFAAAAJ1doZXRoZXIgdGhlIHN5c3RlbSBoYXMgYmVlbiBpbml0aWFsaXplZAAAAAALaW5pdGlhbGl6ZWQAAAAAAQAAACVNYXhpbXVtIGFsbG93ZWQgcGFnZSBzaXplIGZvciBxdWVyaWVzAAAAAAAADW1heF9wYWdlX3NpemUAAAAAAAAEAAAALVJhdGUgbGltaXRpbmcgY29uZmlndXJhdGlvbiBmb3IgdXNlciBjcmVhdGlvbgAAAAAAABFyYXRlX2xpbWl0X2NvbmZpZwAAAAAAB9AAAAAPUmF0ZUxpbWl0Q29uZmlnAAAAACJBZGRyZXNzIG9mIHRoZSBzdXBlciBhZG1pbmlzdHJhdG9yAAAAAAALc3VwZXJfYWRtaW4AAAAAEwAAACBUb3RhbCBudW1iZXIgb2YgcmVnaXN0ZXJlZCB1c2VycwAAABB0b3RhbF91c2VyX2NvdW50AAAABA==", + "AAAAAQAAAIZCYWNrdXAgZGF0YSBzdHJ1Y3R1cmUgZm9yIHVzZXIgbWFuYWdlbWVudCBzeXN0ZW0uCgpDb250YWlucyBhbGwgdXNlciBkYXRhIGFuZCBzeXN0ZW0gY29uZmlndXJhdGlvbiBmb3IgYmFja3VwIGFuZCByZWNvdmVyeSBvcGVyYXRpb25zLgAAAAAAAAAAAA5Vc2VyQmFja3VwRGF0YQAAAAAACAAAABxBZG1pbmlzdHJhdGl2ZSBjb25maWd1cmF0aW9uAAAADGFkbWluX2NvbmZpZwAAB9AAAAALQWRtaW5Db25maWcAAAAAF0xpc3Qgb2YgYWRtaW4gYWRkcmVzc2VzAAAAAAZhZG1pbnMAAAAAA+oAAAATAAAAEEJhY2t1cCB0aW1lc3RhbXAAAAAQYmFja3VwX3RpbWVzdGFtcAAAAAYAAAAgQmFja3VwIHZlcnNpb24gZm9yIGNvbXBhdGliaWxpdHkAAAAOYmFja3VwX3ZlcnNpb24AAAAAABAAAAAnRW1haWwgdG8gYWRkcmVzcyBtYXBwaW5nIGZvciB1bmlxdWVuZXNzAAAAAA5lbWFpbF9tYXBwaW5ncwAAAAAD7AAAABAAAAATAAAALkFsbCBsaWdodHdlaWdodCBwcm9maWxlcyBmb3IgZWZmaWNpZW50IHF1ZXJpZXMAAAAAAA5saWdodF9wcm9maWxlcwAAAAAD7AAAABMAAAfQAAAADExpZ2h0UHJvZmlsZQAAAB9BbGwgdXNlciBwcm9maWxlcyBpbiB0aGUgc3lzdGVtAAAAAA11c2VyX3Byb2ZpbGVzAAAAAAAD7AAAABMAAAfQAAAAC1VzZXJQcm9maWxlAAAAACVMaXN0IG9mIGFsbCByZWdpc3RlcmVkIHVzZXIgYWRkcmVzc2VzAAAAAAAAC3VzZXJzX2luZGV4AAAAA+oAAAAT", + "AAAAAQAAAK1QYWdpbmF0aW9uIHBhcmFtZXRlcnMgZm9yIGN1cnNvci1iYXNlZCBwYWdpbmF0aW9uLgoKVXNlZCB0byBpbXBsZW1lbnQgZWZmaWNpZW50IHBhZ2luYXRpb24gdGhhdCBhdm9pZHMgZ2FzIGxpbWl0IGlzc3Vlcwp3aXRoIGxhcmdlIGRhdGFzZXRzIGJ5IHVzaW5nIGN1cnNvci1iYXNlZCBuYXZpZ2F0aW9uLgAAAAAAAAAAAAAQUGFnaW5hdGlvblBhcmFtcwAAAAIAAABDQ3Vyc29yIGZvciBwYWdpbmF0aW9uIChhZGRyZXNzIG9mIHRoZSBsYXN0IGl0ZW0gZnJvbSBwcmV2aW91cyBwYWdlKQAAAAAGY3Vyc29yAAAAAAPoAAAAEwAAACpNYXhpbXVtIG51bWJlciBvZiBpdGVtcyB0byByZXR1cm4gcGVyIHBhZ2UAAAAAAAVsaW1pdAAAAAAAAAQ=", + "AAAAAQAAAJhQYWdpbmF0aW9uIHJlc3VsdCB3aXRoIG1ldGFkYXRhIGZvciBlZmZpY2llbnQgbmF2aWdhdGlvbi4KCkNvbnRhaW5zIHRoZSBwYWdpbmF0ZWQgZGF0YSBhbG9uZyB3aXRoIHBhZ2luYXRpb24gbWV0YWRhdGEKdG8gZW5hYmxlIGN1cnNvci1iYXNlZCBuYXZpZ2F0aW9uLgAAAAAAAAAWUGFnaW5hdGVkTGlnaHRQcm9maWxlcwAAAAAABAAAABhUaGUgcGFnaW5hdGVkIGRhdGEgaXRlbXMAAAAEZGF0YQAAA+oAAAfQAAAADExpZ2h0UHJvZmlsZQAAACZXaGV0aGVyIHRoZXJlIGFyZSBtb3JlIHBhZ2VzIGF2YWlsYWJsZQAAAAAACGhhc19tb3JlAAAAAQAAADhDdXJzb3IgZm9yIHRoZSBuZXh0IHBhZ2UgKE5vbmUgaWYgdGhpcyBpcyB0aGUgbGFzdCBwYWdlKQAAAAtuZXh0X2N1cnNvcgAAAAPoAAAAEwAAAFBUb3RhbCBjb3VudCBvZiBpdGVtcyBtYXRjaGluZyB0aGUgZmlsdGVyIChvcHRpb25hbCwgbWF5IGJlIGV4cGVuc2l2ZSB0byBjb21wdXRlKQAAAAt0b3RhbF9jb3VudAAAAAPoAAAABA==", + "AAAAAgAAALdTdG9yYWdlIGtleXMgZm9yIGRpZmZlcmVudCBkYXRhIHR5cGVzIGluIHRoZSB1c2VyIG1hbmFnZW1lbnQgY29udHJhY3QuCgpUaGlzIGVudW0gZGVmaW5lcyB0aGUgdmFyaW91cyBrZXlzIHVzZWQgdG8gc3RvcmUgYW5kIHJldHJpZXZlCnVzZXIgZGF0YSBmcm9tIHRoZSBjb250cmFjdCdzIHBlcnNpc3RlbnQgc3RvcmFnZS4AAAAAAAAAAAdEYXRhS2V5AAAAAAwAAAABAAAAQ0tleSBmb3Igc3RvcmluZyBjb21wbGV0ZSB1c2VyIHByb2ZpbGVzOiB1c2VyX2FkZHJlc3MgLT4gVXNlclByb2ZpbGUAAAAAC1VzZXJQcm9maWxlAAAAAAEAAAATAAAAAQAAACxLZXkgZm9yIHN0b3JpbmcgYWRtaW4gZmxhZ3M6IGFkZHJlc3MgLT4gYm9vbAAAAAVBZG1pbgAAAAAAAAEAAAATAAAAAQAAAEdLZXkgZm9yIHN0b3JpbmcgbGlnaHR3ZWlnaHQgdXNlciBwcm9maWxlczogdXNlcl9hZGRyZXNzIC0+IExpZ2h0UHJvZmlsZQAAAAAQVXNlclByb2ZpbGVMaWdodAAAAAEAAAATAAAAAAAAADlLZXkgZm9yIHN0b3JpbmcgdGhlIGxpc3Qgb2YgYWxsIHJlZ2lzdGVyZWQgdXNlciBhZGRyZXNzZXMAAAAAAAAKVXNlcnNJbmRleAAAAAAAAQAAAE1LZXkgZm9yIGVtYWlsIHRvIGFkZHJlc3MgbWFwcGluZyB0byBlbnN1cmUgZW1haWwgdW5pcXVlbmVzczogZW1haWwgLT4gQWRkcmVzcwAAAAAAAApFbWFpbEluZGV4AAAAAAABAAAAEAAAAAAAAAArS2V5IGZvciBzdG9yaW5nIHRoZSBsaXN0IG9mIGFkbWluIGFkZHJlc3NlcwAAAAAGQWRtaW5zAAAAAAABAAAAP0tleSBmb3Igc3RvcmluZyB1c2VyIHJvbGUgYXNzaWdubWVudHM6IHVzZXJfYWRkcmVzcyAtPiBVc2VyUm9sZQAAAAAIVXNlclJvbGUAAAABAAAAEwAAAAAAAAAsS2V5IGZvciBzdG9yaW5nIGFkbWluaXN0cmF0aXZlIGNvbmZpZ3VyYXRpb24AAAALQWRtaW5Db25maWcAAAAAAQAAAEhLZXkgZm9yIHN0b3JpbmcgcmF0ZSBsaW1pdGluZyBkYXRhIHBlciBhZGRyZXNzOiBhZGRyZXNzIC0+IFJhdGVMaW1pdERhdGEAAAAJUmF0ZUxpbWl0AAAAAAAAAQAAABMAAAABAAAAP0tleSBmb3Igc3RvcmluZyByb2xlLWJhc2VkIHBlcm1pc3Npb25zOiByb2xlIC0+IFJvbGVQZXJtaXNzaW9ucwAAAAAPUm9sZVBlcm1pc3Npb25zAAAAAAEAAAfQAAAACFVzZXJSb2xlAAAAAQAAAFNLZXkgZm9yIHN0b3JpbmcgdXNlci1zcGVjaWZpYyBwZXJtaXNzaW9uIG92ZXJyaWRlczogdXNlcl9hZGRyZXNzIC0+IFVzZXJQZXJtaXNzaW9ucwAAAAAPVXNlclBlcm1pc3Npb25zAAAAAAEAAAATAAAAAAAAADZLZXkgZm9yIHN0b3JpbmcgZGVmYXVsdCByb2xlIHBlcm1pc3Npb25zIGNvbmZpZ3VyYXRpb24AAAAAABZEZWZhdWx0Um9sZVBlcm1pc3Npb25zAAA=", + "AAAAAAAAA7RSZXRyaWV2ZSBhIHVzZXIgcHJvZmlsZSBmb3IgdGhlIGF1dGhlbnRpY2F0ZWQgdXNlci4KClRoaXMgZnVuY3Rpb24gZmV0Y2hlcyB0aGUgY29tcGxldGUgdXNlciBwcm9maWxlIGFzc29jaWF0ZWQgd2l0aCB0aGUgcHJvdmlkZWQKYmxvY2tjaGFpbiBhZGRyZXNzLiBUaGUgdXNlciBtdXN0IGJlIGF1dGhlbnRpY2F0ZWQ7IG90aGVyd2lzZSwgdGhlIGZ1bmN0aW9uCndpbGwgcGFuaWMuCgojIyMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQuCiogYHVzZXJgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIHVzZXIgd2hvc2UgcHJvZmlsZSBpcyBiZWluZyByZXF1ZXN0ZWQuCgojIyMgUmV0dXJucwoKUmV0dXJucyB0aGUgYFVzZXJQcm9maWxlYCBjb3JyZXNwb25kaW5nIHRvIHRoZSBhdXRoZW50aWNhdGVkIHVzZXIuCgojIyMgUGFuaWNzCgoqIElmIHRoZSB1c2VyIGlzIG5vdCBhdXRoZW50aWNhdGVkIChgcmVxdWlyZV9hdXRoYCBmYWlscykuCiogSWYgdGhlIHVzZXIgcHJvZmlsZSBkb2VzIG5vdCBleGlzdCAoYFVzZXJOb3RGb3VuZGAgZXJyb3IpLgoKIyMjIEV4YW1wbGVzCgpgYGBydXN0Ci8vIEFzc3VtaW5nIHRoZSB1c2VyIGlzIGF1dGhlbnRpY2F0ZWQgaW4gdGhlIGVudmlyb25tZW50CmxldCBwcm9maWxlID0gY29udHJhY3QuZ2V0X3VzZXJfcHJvZmlsZShlbnYuY2xvbmUoKSwgbXlfYWRkcmVzcyk7CnByaW50bG4hKCJVc2VyIGZ1bGwgbmFtZToge30iLCBwcm9maWxlLmZ1bGxfbmFtZSk7CmBgYAoKIyMjIE5vdGVzCgoqIE9ubHkgdGhlIHVzZXIgdGhlbXNlbHZlcyBjYW4gZmV0Y2ggdGhlaXIgcHJvZmlsZTsgdGhlcmUgaXMgbm8gYWRtaW4gb3ZlcnJpZGUKaW4gdGhpcyBmdW5jdGlvbi4KKiBJZiB0aGUgcHJvZmlsZSBpcyBub3QgZm91bmQgaW4gc3RvcmFnZSwgdGhlIGZ1bmN0aW9uIHdpbGwgcGFuaWMgd2l0aApgVXNlck5vdEZvdW5kYC4AAAAQZ2V0X3VzZXJfcHJvZmlsZQAAAAEAAAAAAAAABHVzZXIAAAATAAAAAQAAA+kAAAfQAAAAC1VzZXJQcm9maWxlAAAAAAM=", + "AAAAAAAABABSZXRyaWV2ZSBhIHVzZXIgcHJvZmlsZSBieSB0aGVpciBhZGRyZXNzLgoKVGhpcyBmdW5jdGlvbiBmZXRjaGVzIGEgY29tcGxldGUgdXNlciBwcm9maWxlIHVzaW5nIHRoZSB1c2VyJ3MgYmxvY2tjaGFpbiBhZGRyZXNzLgpBY2Nlc3MgbWF5IGJlIHJlc3RyaWN0ZWQgYmFzZWQgb24gdGhlIHJlcXVlc3RlcidzIHBlcm1pc3Npb25zLgoKIyBBcmd1bWVudHMKCiogYGVudmAgLSBUaGUgU29yb2JhbiBlbnZpcm9ubWVudAoqIGByZXF1ZXN0ZXJgIC0gVGhlIGFkZHJlc3Mgb2YgdGhlIHVzZXIgcmVxdWVzdGluZyB0aGUgcHJvZmlsZQoqIGB1c2VyX2lkYCAtIFRoZSBhZGRyZXNzIG9mIHRoZSB1c2VyIHdob3NlIHByb2ZpbGUgaXMgYmVpbmcgcmVxdWVzdGVkCgojIFJldHVybnMKClJldHVybnMgdGhlIHJlcXVlc3RlZCBgVXNlclByb2ZpbGVgLgoKIyBQYW5pY3MKCiogSWYgdGhlIHVzZXIgcHJvZmlsZSBkb2Vzbid0IGV4aXN0CiogSWYgdGhlIHJlcXVlc3RlciBkb2Vzbid0IGhhdmUgcGVybWlzc2lvbiB0byB2aWV3IHRoZSBwcm9maWxlCiogSWYgdGhlIHJlcXVlc3RlciBpcyBub3QgdGhlIHVzZXIgdGhlbXNlbHZlcyBvciBhbiBhZG1pbgoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBHZXQgeW91ciBvd24gcHJvZmlsZQpsZXQgbXlfcHJvZmlsZSA9IGNvbnRyYWN0LmdldF91c2VyX2J5X2lkKGVudi5jbG9uZSgpLCBteV9hZGRyZXNzLCBteV9hZGRyZXNzKTsKCi8vIEFkbWluIGdldHRpbmcgYW55IHVzZXIncyBwcm9maWxlCmxldCB1c2VyX3Byb2ZpbGUgPSBjb250cmFjdC5nZXRfdXNlcl9ieV9pZChlbnYuY2xvbmUoKSwgYWRtaW5fYWRkcmVzcywgdXNlcl9hZGRyZXNzKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipOb24tZXhpc3RlbnQgdXNlcioqOiBXaWxsIHBhbmljIHdpdGggYXBwcm9wcmlhdGUgZXJyb3IgbWVzc2FnZQoqICoqSW5hY3RpdmUgdXNlcioqOiBSZXR1cm5zIHByb2ZpbGUgYnV0IHN0YXR1cyB3aWxsIGJlIGBVc2VyU3RhdHVzOjpJbmFjdGl2ZWAKKiAqKlBlcm1pc3Npb24gZGVuaWVkKio6AAAADmdldF91c2VyX2J5X2lkAAAAAAACAAAAAAAAAAlyZXF1ZXN0ZXIAAAAAAAATAAAAAAAAAAd1c2VyX2lkAAAAABMAAAABAAAH0AAAAAtVc2VyUHJvZmlsZQA=", + "AAAAAAAABABDcmVhdGUgYSBuZXcgdXNlciBwcm9maWxlCgpDcmVhdGVzIGEgbmV3IHVzZXIgcHJvZmlsZSB1c2luZyBhIFVzZXJQcm9maWxlIHN0cnVjdC4KVmFsaWRhdGVzIG1hbmRhdG9yeSBmaWVsZHMgKGZ1bGxfbmFtZSBhbmQgY29udGFjdF9lbWFpbCkgYW5kIHNhdmVzIHRoZSBwcm9maWxlLgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgdXNlcmAgLSBBZGRyZXNzIG9mIHRoZSB1c2VyIHdob3NlIHByb2ZpbGUgaXMgYmVpbmcgY3JlYXRlZAoqIGBwcm9maWxlYCAtIFVzZXJQcm9maWxlIHN0cnVjdCBjb250YWluaW5nIGFsbCBwcm9maWxlIGRhdGEKCiMgUmV0dXJucwoqIGBVc2VyUHJvZmlsZWAgLSBUaGUgY3JlYXRlZCB1c2VyIHByb2ZpbGUKCiMgUGFuaWNzCiogSWYgbWFuZGF0b3J5IGZpZWxkcyAoZnVsbF9uYW1lLCBjb250YWN0X2VtYWlsKSBhcmUgbWlzc2luZwoqIElmIHVzZXIgcHJvZmlsZSBhbHJlYWR5IGV4aXN0cwoqIElmIGVtYWlsIGZvcm1hdCBpcyBpbnZhbGlkCiogSWYgdmFsaWRhdGlvbiBydWxlcyBhcmUgdmlvbGF0ZWQKCiMgRXZlbnRzCkVtaXRzIGEgdXNlciBjcmVhdGlvbiBldmVudCB1cG9uIHN1Y2Nlc3NmdWwgY3JlYXRpb24KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKbGV0IHByb2ZpbGUgPSBVc2VyUHJvZmlsZSB7CmZ1bGxfbmFtZTogIkpvaG4gRG9lIi50cnlfaW50bygpLnVud3JhcCgpLApjb250YWN0X2VtYWlsOiAiam9obkBleGFtcGxlLmNvbSIudHJ5X2ludG8oKS51bndyYXAoKSwKcm9sZTogVXNlclJvbGU6OlN0dWRlbnQsCnN0YXR1czogVXNlclN0YXR1czo6QWN0aXZlLApjb3VudHJ5OiBTb21lKCJVUyIudHJ5X2ludG8oKS51bndyYXAoKSksCi4uRGVmYXVsdDo6ZGVmYXVsdCgpCn07CgpsZXQgY3JlYXRlZF9wcm9maWxlID0gY29udHJhY3QuY3JlYXRlX3VzZXJfcHJvZmlsZShlbnYsIHVzZXJfYWRkcmVzcywgcHJvZmlsZSk7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqRHVwbGljYXRlIHByb2ZpbGUqKjogV2lsbCBwYW5pYyBpZiB1c2VyIGFsAAAAE2NyZWF0ZV91c2VyX3Byb2ZpbGUAAAAAAgAAAAAAAAAEdXNlcgAAABMAAAAAAAAAB3Byb2ZpbGUAAAAH0AAAAAtVc2VyUHJvZmlsZQAAAAABAAAH0AAAAAtVc2VyUHJvZmlsZQA=", + "AAAAAAAABABFZGl0IGFuIGV4aXN0aW5nIHVzZXIgcHJvZmlsZQoKVXBkYXRlcyBhbiBleGlzdGluZyB1c2VyIHByb2ZpbGUgd2l0aCBuZXcgdmFsdWVzIGZvciBhbGxvd2VkIGZpZWxkcy4KT25seSB0aGUgdXNlciB0aGVtc2VsdmVzIG9yIGFkbWluaXN0cmF0b3JzIGNhbiBwZXJmb3JtIHVwZGF0ZXMuCkVtYWlsIGFuZCByb2xlIGZpZWxkcyBjYW5ub3QgYmUgdXBkYXRlZCB0aHJvdWdoIHRoaXMgZnVuY3Rpb24uCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBvZiB0aGUgdXNlciBwZXJmb3JtaW5nIHRoZSB1cGRhdGUKKiBgdXNlcl9pZGAgLSBBZGRyZXNzIG9mIHRoZSB1c2VyIHdob3NlIHByb2ZpbGUgaXMgYmVpbmcgdXBkYXRlZAoqIGB1cGRhdGVzYCAtIFByb2ZpbGVVcGRhdGVQYXJhbXMgY29udGFpbmluZyBmaWVsZHMgdG8gdXBkYXRlCgojIFJldHVybnMKKiBgVXNlclByb2ZpbGVgIC0gVGhlIHVwZGF0ZWQgdXNlciBwcm9maWxlCgojIFBhbmljcwoqIElmIGNhbGxlciBhdXRoZW50aWNhdGlvbiBmYWlscwoqIElmIHVzZXIgcHJvZmlsZSBkb2Vzbid0IGV4aXN0CiogSWYgY2FsbGVyIGxhY2tzIHBlcm1pc3Npb24gdG8gZWRpdAoqIElmIGFueSBmaWVsZCB2YWxpZGF0aW9uIGZhaWxzCiogSWYgdXNlciBpcyBpbmFjdGl2ZQoKIyBFdmVudHMKRW1pdHMgYSB1c2VyIHVwZGF0ZSBldmVudCB1cG9uIHN1Y2Nlc3NmdWwgcHJvZmlsZSB1cGRhdGUKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKbGV0IHVwZGF0ZXMgPSBQcm9maWxlVXBkYXRlUGFyYW1zIHsKZnVsbF9uYW1lOiBTb21lKCJKYW5lIERvZSIudHJ5X2ludG8oKS51bndyYXAoKSksCmNvdW50cnk6IFNvbWUoIkNBIi50cnlfaW50bygpLnVud3JhcCgpKSwKYmlvOiBTb21lKCJVcGRhdGVkIGJpbyIudHJ5X2ludG8oKS51bndyYXAoKSksCi4uRGVmYXVsdDo6ZGVmYXVsdCgpCn07CgpsZXQgdXBkYXRlZF9wcm9maWxlID0gY29udHJhY3QuZWRpdF91c2VyX3Byb2ZpbGUoZW52LCBjYWxsZXJfYWRkcmVzAAAAEWVkaXRfdXNlcl9wcm9maWxlAAAAAAAAAwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAAd1c2VyX2lkAAAAABMAAAAAAAAAB3VwZGF0ZXMAAAAH0AAAABNQcm9maWxlVXBkYXRlUGFyYW1zAAAAAAEAAAfQAAAAC1VzZXJQcm9maWxlAA==", + "AAAAAAAAAzZDaGVjayBpZiBhbiBhZGRyZXNzIGhhcyBhZG1pbiBwcml2aWxlZ2VzLgoKVGhpcyBmdW5jdGlvbiBpcyB1c2VkIGJ5IG90aGVyIGNvbnRyYWN0cyB0byB2ZXJpZnkgYWRtaW4gc3RhdHVzCmZvciBjcm9zcy1jb250cmFjdCBhdXRob3JpemF0aW9uIGNoZWNrcy4KCiMgQXJndW1lbnRzCgoqIGBlbnZgIC0gVGhlIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgd2hvYCAtIFRoZSBhZGRyZXNzIHRvIGNoZWNrIGZvciBhZG1pbiBwcml2aWxlZ2VzCgojIFJldHVybnMKClJldHVybnMgYHRydWVgIGlmIHRoZSBhZGRyZXNzIGhhcyBhZG1pbiBwcml2aWxlZ2VzLCBgZmFsc2VgIG90aGVyd2lzZS4KCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gQ2hlY2sgaWYgdXNlciBpcyBhZG1pbgpsZXQgaXNfYWRtaW4gPSBjb250cmFjdC5pc19hZG1pbihlbnYuY2xvbmUoKSwgdXNlcl9hZGRyZXNzKTsKaWYgaXNfYWRtaW4gewovLyBQZXJmb3JtIGFkbWluIG9wZXJhdGlvbnMKfQoKLy8gQ3Jvc3MtY29udHJhY3QgYWRtaW4gY2hlY2sKbGV0IGNhbl9wZXJmb3JtX2FjdGlvbiA9IGNvbnRyYWN0LmlzX2FkbWluKGVudi5jbG9uZSgpLCBjYWxsZXJfYWRkcmVzcyk7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqU3lzdGVtIG5vdCBpbml0aWFsaXplZCoqOiBSZXR1cm5zIGBmYWxzZWAgaWYgYWRtaW4gc3lzdGVtIGhhc24ndCBiZWVuIHNldCB1cAoqICoqTm9uLWV4aXN0ZW50IHVzZXIqKjogUmV0dXJucyBgZmFsc2VgIGZvciBhZGRyZXNzZXMgdGhhdCBkb24ndCBleGlzdAoqICoqUmVndWxhciB1c2VycyoqOiBBbHdheXMgcmV0dXJucyBgZmFsc2VgIGZvciBub24tYWRtaW4gdXNlcnMAAAAAAAhpc19hZG1pbgAAAAEAAAAAAAAAA3dobwAAAAATAAAAAQAAAAE=", + "AAAAAAAABABEZWxldGUgKGRlYWN0aXZhdGUpIGEgdXNlciBhY2NvdW50CgpQZXJmb3JtcyBhIHNvZnQgZGVsZXRlIGJ5IG1hcmtpbmcgdGhlIHVzZXIgYXMgaW5hY3RpdmUgaW5zdGVhZCBvZiBwZXJtYW5lbnQgZGVsZXRpb24uCk9ubHkgYWRtaW5zIG9yIHRoZSB1c2VyIHRoZW1zZWx2ZXMgY2FuIHRyaWdnZXIgZGVsZXRpb24uCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBkZWxldGlvbiAobXVzdCBiZSBhZG1pbiBvciB0aGUgdXNlciB0aGVtc2VsdmVzKQoqIGB1c2VyX2lkYCAtIEFkZHJlc3Mgb2YgdGhlIHVzZXIgdG8gYmUgZGVhY3RpdmF0ZWQKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGF1dGhlbnRpY2F0aW9uIGZhaWxzCiogSWYgdXNlciBkb2Vzbid0IGV4aXN0CiogSWYgY2FsbGVyIGlzIG5laXRoZXIgYWRtaW4gbm9yIHRoZSB1c2VyIHRoZW1zZWx2ZXMKKiBJZiB1c2VyIGlzIGFscmVhZHkgaW5hY3RpdmUKCiMgRXZlbnRzCkVtaXRzIGEgdXNlciBkZWFjdGl2YXRpb24gZXZlbnQgdXBvbiBzdWNjZXNzZnVsIGRlbGV0aW9uCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIFVzZXIgZGVsZXRpbmcgdGhlaXIgb3duIGFjY291bnQKY29udHJhY3QuZGVsZXRlX3VzZXIoZW52LmNsb25lKCksIHVzZXJfYWRkcmVzcywgdXNlcl9hZGRyZXNzKTsKCi8vIEFkbWluIGRlbGV0aW5nIGFub3RoZXIgdXNlcidzIGFjY291bnQKY29udHJhY3QuZGVsZXRlX3VzZXIoZW52LmNsb25lKCksIGFkbWluX2FkZHJlc3MsIHVzZXJfdG9fZGVsZXRlKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipBbHJlYWR5IGluYWN0aXZlKio6IFdpbGwgcGFuaWMgaWYgdHJ5aW5nIHRvIGRlbGV0ZSBhbiBhbHJlYWR5IGluYWN0aXZlIHVzZXIKKiAqKlBlcm1pc3Npb24gZGVuaWVkKio6IE5vbi1hZG1pbiB1c2VycyBjYW4gb25seSBkZWxldGUgdGhlaXIgb3duIGFjY291bnRzCiogKipEYXRhIHByZXNlcnZhdGlvbioqOiBVc2VyIGRhdGEgaXMgcHJlc2VydmVkAAAAC2RlbGV0ZV91c2VyAAAAAAIAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAAAAAAHdXNlcl9pZAAAAAATAAAAAA==", + "AAAAAAAABABMaXN0cyBhbGwgcmVnaXN0ZXJlZCB1c2VycyB3aXRoIHBhZ2luYXRpb24gYW5kIGZpbHRlcmluZyAoYWRtaW4tb25seSkKCiMgQXJndW1lbnRzCiogYGVudmAgLSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNhbGxlcmAgLSBBZGRyZXNzIHBlcmZvcm1pbmcgdGhlIGNhbGwgKG11c3QgYmUgYWRtaW4pCiogYHBhZ2VgIC0gWmVyby1iYXNlZCBwYWdlIGluZGV4CiogYHBhZ2Vfc2l6ZWAgLSBOdW1iZXIgb2YgaXRlbXMgcGVyIHBhZ2UgKG11c3QgYmUgPiAwKQoqIGByb2xlX2ZpbHRlcmAgLSBPcHRpb25hbCByb2xlIGZpbHRlcgoqIGBjb3VudHJ5X2ZpbHRlcmAgLSBPcHRpb25hbCBjb3VudHJ5IGZpbHRlcgoqIGBzdGF0dXNfZmlsdGVyYCAtIE9wdGlvbmFsIHN0YXR1cyBmaWx0ZXIKCiMgUmV0dXJucwoqIGBWZWM8TGlnaHRQcm9maWxlPmAgLSBGaWx0ZXJlZCBhbmQgcGFnaW5hdGVkIGxpZ2h0d2VpZ2h0IHVzZXIgcHJvZmlsZXMKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGlzIG5vdCBhbiBhZG1pbgoqIElmIHBhZ2Vfc2l6ZSBpcyAwIG9yIGV4Y2VlZHMgbWF4aW11bSBhbGxvd2VkCiogSWYgc3lzdGVtIGlzIG5vdCBpbml0aWFsaXplZAoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBHZXQgZmlyc3QgcGFnZSB3aXRoIDEwIHVzZXJzCmxldCB1c2VycyA9IGNvbnRyYWN0Lmxpc3RfYWxsX3VzZXJzKAplbnYuY2xvbmUoKSwKYWRtaW5fYWRkcmVzcywKMCwgIC8vIHBhZ2UgMAoxMCwgLy8gcGFnZSBzaXplCk5vbmUsIE5vbmUsIE5vbmUgLy8gbm8gZmlsdGVycwopOwoKLy8gRmlsdGVyIGJ5IHJvbGUgYW5kIGNvdW50cnkKbGV0IHN0dWRlbnRzID0gY29udHJhY3QubGlzdF9hbGxfdXNlcnMoCmVudi5jbG9uZSgpLAphZG1pbl9hZGRyZXNzLAowLCAyMCwKU29tZShVc2VyUm9sZTo6U3R1ZGVudCksClNvbWUoIlVTIi50cnlfaW50bygpLnVud3JhcCgpKSwKTm9uZQopOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKkVtcHR5IHJlc3VsdHMqKjogUmV0dXJucyBlbXB0eSB2ZWN0b3IgaWYgbm8gdXNlcnMgbWF0Y2ggZmlsdGVyAAAADmxpc3RfYWxsX3VzZXJzAAAAAAAGAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAABHBhZ2UAAAAEAAAAAAAAAAlwYWdlX3NpemUAAAAAAAAEAAAAAAAAAAtyb2xlX2ZpbHRlcgAAAAPoAAAH0AAAAAhVc2VyUm9sZQAAAAAAAAAOY291bnRyeV9maWx0ZXIAAAAAA+gAAAAQAAAAAAAAAA1zdGF0dXNfZmlsdGVyAAAAAAAD6AAAB9AAAAAKVXNlclN0YXR1cwAAAAAAAQAAA+oAAAfQAAAADExpZ2h0UHJvZmlsZQ==", + "AAAAAAAAAldMaXN0cyBhbGwgcmVnaXN0ZXJlZCB1c2VycyB3aXRoIGFkdmFuY2VkIGZpbHRlcmluZyBpbmNsdWRpbmcgdGV4dCBzZWFyY2ggKGFkbWluLW9ubHkpLgoKVGhpcyBpcyB0aGUgbmV3IHZlcnNpb24gdGhhdCBzdXBwb3J0cyB0ZXh0IHNlYXJjaCBmdW5jdGlvbmFsaXR5LgoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2FsbGVyYCAtIEFkZHJlc3MgcGVyZm9ybWluZyB0aGUgY2FsbCAobXVzdCBiZSBhZG1pbikKKiBgcGFnZWAgLSBaZXJvLWJhc2VkIHBhZ2UgaW5kZXgKKiBgcGFnZV9zaXplYCAtIE51bWJlciBvZiBpdGVtcyBwZXIgcGFnZQoqIGByb2xlX2ZpbHRlcmAgLSBPcHRpb25hbCByb2xlIGZpbHRlcgoqIGBjb3VudHJ5X2ZpbHRlcmAgLSBPcHRpb25hbCBjb3VudHJ5IGZpbHRlcgoqIGBzdGF0dXNfZmlsdGVyYCAtIE9wdGlvbmFsIHN0YXR1cyBmaWx0ZXIKKiBgc2VhcmNoX3RleHRgIC0gT3B0aW9uYWwgdGV4dCBzZWFyY2ggaW4gbmFtZSBhbmQgcHJvZmVzc2lvbgoKIyBSZXR1cm5zCiogYFZlYzxMaWdodFByb2ZpbGU+YCAtIEZpbHRlcmVkIGFuZCBwYWdpbmF0ZWQgbGlnaHR3ZWlnaHQgdXNlciBwcm9maWxlcwAAAAAXbGlzdF9hbGxfdXNlcnNfYWR2YW5jZWQAAAAABwAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAARwYWdlAAAABAAAAAAAAAAJcGFnZV9zaXplAAAAAAAABAAAAAAAAAALcm9sZV9maWx0ZXIAAAAD6AAAB9AAAAAIVXNlclJvbGUAAAAAAAAADmNvdW50cnlfZmlsdGVyAAAAAAPoAAAAEAAAAAAAAAANc3RhdHVzX2ZpbHRlcgAAAAAAA+gAAAfQAAAAClVzZXJTdGF0dXMAAAAAAAAAAAALc2VhcmNoX3RleHQAAAAD6AAAABAAAAABAAAD6gAAB9AAAAAMTGlnaHRQcm9maWxl", + "AAAAAAAAAmxMaXN0cyBhbGwgcmVnaXN0ZXJlZCB1c2VycyB3aXRoIGN1cnNvci1iYXNlZCBwYWdpbmF0aW9uIGFuZCBmaWx0ZXJpbmcgKGFkbWluLW9ubHkpCgpUaGlzIGZ1bmN0aW9uIGltcGxlbWVudHMgZWZmaWNpZW50IGN1cnNvci1iYXNlZCBwYWdpbmF0aW9uIHRvIGF2b2lkIGdhcyBsaW1pdCBpc3N1ZXMKd2hlbiBkZWFsaW5nIHdpdGggbGFyZ2UgZGF0YXNldHMuIEl0IHJldHVybnMgYSBQYWdpbmF0ZWRSZXN1bHQgd2l0aCBtZXRhZGF0YSBmb3IKZWZmaWNpZW50IG5hdmlnYXRpb24uCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBjYWxsIChtdXN0IGJlIGFkbWluKQoqIGBwYWdpbmF0aW9uYCAtIFBhZ2luYXRpb24gcGFyYW1ldGVycyBpbmNsdWRpbmcgY3Vyc29yIGFuZCBsaW1pdAoqIGByb2xlX2ZpbHRlcmAgLSBPcHRpb25hbCBmaWx0ZXIgZm9yIHVzZXIgcm9sZQoqIGBzdGF0dXNfZmlsdGVyYCAtIE9wdGlvbmFsIGZpbHRlciBmb3IgdXNlciBzdGF0dXMKCiMgUmV0dXJucwoqIGBQYWdpbmF0ZWRMaWdodFByb2ZpbGVzYCAtIFBhZ2luYXRlZCByZXN1bHRzIHdpdGggbmF2aWdhdGlvbiBtZXRhZGF0YQAAABVsaXN0X2FsbF91c2Vyc19jdXJzb3IAAAAAAAAEAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAACnBhZ2luYXRpb24AAAAAB9AAAAAQUGFnaW5hdGlvblBhcmFtcwAAAAAAAAALcm9sZV9maWx0ZXIAAAAD6AAAB9AAAAAIVXNlclJvbGUAAAAAAAAADXN0YXR1c19maWx0ZXIAAAAAAAPoAAAH0AAAAApVc2VyU3RhdHVzAAAAAAABAAAH0AAAABZQYWdpbmF0ZWRMaWdodFByb2ZpbGVzAAA=", + "AAAAAAAAA7BJbml0aWFsaXplIHRoZSBhZG1pbiBzeXN0ZW0gKG9uZS10aW1lIG9ubHkpCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBpbml0aWFsaXplcmAgLSBBZGRyZXNzIHBlcmZvcm1pbmcgdGhlIGluaXRpYWxpemF0aW9uCiogYHN1cGVyX2FkbWluYCAtIEFkZHJlc3MgdGhhdCB3aWxsIGJlY29tZSB0aGUgc3VwZXIgYWRtaW4KKiBgbWF4X3BhZ2Vfc2l6ZWAgLSBPcHRpb25hbCBtYXhpbXVtIHBhZ2Ugc2l6ZSAoZGVmYXVsdDogMTAwLCBtYXg6IDEwMDApCgojIFJldHVybnMKKiBgQWRtaW5Db25maWdgIC0gVGhlIGNyZWF0ZWQgYWRtaW4gY29uZmlndXJhdGlvbgoKIyBQYW5pY3MKKiBJZiBzeXN0ZW0gaGFzIGFscmVhZHkgYmVlbiBpbml0aWFsaXplZAoqIElmIG1heF9wYWdlX3NpemUgZXhjZWVkcyAxMDAwCgojIEV4YW1wbGVzCgpgYGBydXN0Ci8vIEluaXRpYWxpemUgd2l0aCBkZWZhdWx0IHNldHRpbmdzCmxldCBjb25maWcgPSBjb250cmFjdC5pbml0aWFsaXplX3N5c3RlbSgKZW52LmNsb25lKCksCmRlcGxveWVyX2FkZHJlc3MsCnN1cGVyX2FkbWluX2FkZHJlc3MsCk5vbmUKKTsKCi8vIEluaXRpYWxpemUgd2l0aCBjdXN0b20gcGFnZSBzaXplCmxldCBjb25maWcgPSBjb250cmFjdC5pbml0aWFsaXplX3N5c3RlbSgKZW52LmNsb25lKCksCmRlcGxveWVyX2FkZHJlc3MsCnN1cGVyX2FkbWluX2FkZHJlc3MsClNvbWUoNTAwKQopOwpgYGAKCiMgRWRnZSBDYXNlcwoKKiAqKkRvdWJsZSBpbml0aWFsaXphdGlvbioqOiBXaWxsIHBhbmljIGlmIGNhbGxlZCBtb3JlIHRoYW4gb25jZQoqICoqSW52YWxpZCBwYWdlIHNpemUqKjogV2lsbCBwYW5pYyBpZiBtYXhfcGFnZV9zaXplID4gMTAwMAoqICoqU3VwZXIgYWRtaW4gcHJpdmlsZWdlcyoqOiBTdXBlciBhZG1pbiBjYW5ub3QgYmUgcmVtb3ZlZCBhZnRlciBpbml0aWFsaXphdGlvbgAAABFpbml0aWFsaXplX3N5c3RlbQAAAAAAAAMAAAAAAAAAC2luaXRpYWxpemVyAAAAABMAAAAAAAAAC3N1cGVyX2FkbWluAAAAABMAAAAAAAAADW1heF9wYWdlX3NpemUAAAAAAAPoAAAABAAAAAEAAAfQAAAAC0FkbWluQ29uZmlnAA==", + "AAAAAAAAAoNBZGQgYSBuZXcgYWRtaW4gKHN1cGVyIGFkbWluIG9ubHkpCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBjYWxsIChtdXN0IGJlIHN1cGVyIGFkbWluKQoqIGBuZXdfYWRtaW5gIC0gQWRkcmVzcyB0byBiZSBhZGRlZCBhcyBhZG1pbgoKIyBQYW5pY3MKKiBJZiBjYWxsZXIgaXMgbm90IHRoZSBzdXBlciBhZG1pbgoqIElmIHN5c3RlbSBpcyBub3QgaW5pdGlhbGl6ZWQKKiBJZiBuZXdfYWRtaW4gaXMgYWxyZWFkeSBhbiBhZG1pbgoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBTdXBlciBhZG1pbiBhZGRpbmcgYSBuZXcgYWRtaW4KY29udHJhY3QuYWRkX2FkbWluKGVudi5jbG9uZSgpLCBzdXBlcl9hZG1pbl9hZGRyZXNzLCBuZXdfYWRtaW5fYWRkcmVzcyk7CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqQWxyZWFkeSBhZG1pbioqOiBXaWxsIHBhbmljIGlmIHRyeWluZyB0byBhZGQgYW4gZXhpc3RpbmcgYWRtaW4KKiAqKlNlbGYtcHJvbW90aW9uKio6IFN1cGVyIGFkbWluIGNhbm5vdCBhZGQgdGhlbXNlbHZlcyAocmVkdW5kYW50KQoqICoqTm9uLWV4aXN0ZW50IHVzZXIqKjogQ2FuIGFkZCBhZG1pbiBwcml2aWxlZ2VzIHRvIGFueSBhZGRyZXNzAAAAAAlhZGRfYWRtaW4AAAAAAAACAAAAAAAAAAZjYWxsZXIAAAAAABMAAAAAAAAACW5ld19hZG1pbgAAAAAAABMAAAAA", + "AAAAAAAAAqxSZW1vdmUgYW4gYWRtaW4gKHN1cGVyIGFkbWluIG9ubHkpCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoqIGBjYWxsZXJgIC0gQWRkcmVzcyBwZXJmb3JtaW5nIHRoZSBjYWxsIChtdXN0IGJlIHN1cGVyIGFkbWluKQoqIGBhZG1pbl90b19yZW1vdmVgIC0gQWRkcmVzcyB0byBiZSByZW1vdmVkIGZyb20gYWRtaW5zCgojIFBhbmljcwoqIElmIGNhbGxlciBpcyBub3QgdGhlIHN1cGVyIGFkbWluCiogSWYgc3lzdGVtIGlzIG5vdCBpbml0aWFsaXplZAoqIElmIGFkbWluX3RvX3JlbW92ZSBpcyBub3QgYW4gYWRtaW4KKiBJZiB0cnlpbmcgdG8gcmVtb3ZlIHRoZSBzdXBlciBhZG1pbgoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBTdXBlciBhZG1pbiByZW1vdmluZyBhbm90aGVyIGFkbWluCmNvbnRyYWN0LnJlbW92ZV9hZG1pbihlbnYuY2xvbmUoKSwgc3VwZXJfYWRtaW5fYWRkcmVzcywgYWRtaW5fdG9fcmVtb3ZlKTsKYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipTdXBlciBhZG1pbiBwcm90ZWN0aW9uKio6IENhbm5vdCByZW1vdmUgdGhlIHN1cGVyIGFkbWluCiogKipOb24tYWRtaW4qKjogV2lsbCBwYW5pYyBpZiB0cnlpbmcgdG8gcmVtb3ZlIGEgbm9uLWFkbWluIGFkZHJlc3MKKiAqKlNlbGYtcmVtb3ZhbCoqOiBTdXBlciBhZG1pbiBjYW5ub3QgcmVtb3ZlIHRoZW1zZWx2ZXMAAAAMcmVtb3ZlX2FkbWluAAAAAgAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAAAAAA9hZG1pbl90b19yZW1vdmUAAAAAEwAAAAA=", + "AAAAAAAAApFHZXQgbGlzdCBvZiBhbGwgYWRtaW5zIChhZG1pbiBvbmx5KQoKIyBBcmd1bWVudHMKKiBgZW52YCAtIFNvcm9iYW4gZW52aXJvbm1lbnQKKiBgY2FsbGVyYCAtIEFkZHJlc3MgcGVyZm9ybWluZyB0aGUgY2FsbCAobXVzdCBiZSBhZG1pbikKCiMgUmV0dXJucwoqIGBWZWM8QWRkcmVzcz5gIC0gTGlzdCBvZiBhbGwgYWRtaW4gYWRkcmVzc2VzIGluY2x1ZGluZyBzdXBlciBhZG1pbgoKIyBQYW5pY3MKKiBJZiBjYWxsZXIgaXMgbm90IGFuIGFkbWluCiogSWYgc3lzdGVtIGlzIG5vdCBpbml0aWFsaXplZAoKIyBFeGFtcGxlcwoKYGBgcnVzdAovLyBHZXQgYWxsIGFkbWluIGFkZHJlc3NlcwpsZXQgYWRtaW5zID0gY29udHJhY3QuZ2V0X2FkbWlucyhlbnYuY2xvbmUoKSwgYWRtaW5fYWRkcmVzcyk7CmZvciBhZG1pbiBpbiBhZG1pbnMgewovLyBQcm9jZXNzIGVhY2ggYWRtaW4gYWRkcmVzcwp9CmBgYAoKIyBFZGdlIENhc2VzCgoqICoqRW1wdHkgbGlzdCoqOiBSZXR1cm5zIHZlY3RvciB3aXRoIG9ubHkgc3VwZXIgYWRtaW4gaWYgbm8gb3RoZXIgYWRtaW5zIGV4aXN0CiogKipBZG1pbiBvbmx5Kio6IE9ubHkgYWRtaW5zIGNhbiB2aWV3IHRoZSBhZG1pbiBsaXN0CiogKipPcmRlcioqOiBTdXBlciBhZG1pbiBpcyB0eXBpY2FsbHkgZmlyc3QgaW4gdGhlIGxpc3QAAAAAAAAKZ2V0X2FkbWlucwAAAAAAAQAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAQAAA+oAAAAT", + "AAAAAAAAAkBDaGVjayBpZiB0aGUgc3lzdGVtIGlzIGluaXRpYWxpemVkCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudAoKIyBSZXR1cm5zCiogYGJvb2xgIC0gVHJ1ZSBpZiBzeXN0ZW0gaXMgaW5pdGlhbGl6ZWQKCiMgRXhhbXBsZXMKCmBgYHJ1c3QKLy8gQ2hlY2sgaWYgYWRtaW4gc3lzdGVtIGlzIHJlYWR5CmxldCBpc19pbml0aWFsaXplZCA9IGNvbnRyYWN0LmlzX3N5c3RlbV9pbml0aWFsaXplZChlbnYuY2xvbmUoKSk7CmlmICFpc19pbml0aWFsaXplZCB7Ci8vIEluaXRpYWxpemUgdGhlIHN5c3RlbSBmaXJzdApjb250cmFjdC5pbml0aWFsaXplX3N5c3RlbShlbnYsIGRlcGxveWVyLCBzdXBlcl9hZG1pbiwgTm9uZSk7Cn0KYGBgCgojIEVkZ2UgQ2FzZXMKCiogKipGcmVzaCBkZXBsb3ltZW50Kio6IFJldHVybnMgYGZhbHNlYCBmb3IgbmV3bHkgZGVwbG95ZWQgY29udHJhY3RzCiogKipQdWJsaWMgYWNjZXNzKio6IEFueW9uZSBjYW4gY2hlY2sgaW5pdGlhbGl6YXRpb24gc3RhdHVzCiogKipPbmUtdGltZSBjaGVjayoqOiBPbmNlIGluaXRpYWxpemVkLCBhbHdheXMgcmV0dXJucyBgdHJ1ZWAAAAAVaXNfc3lzdGVtX2luaXRpYWxpemVkAAAAAAAAAAAAAAEAAAAB", + "AAAAAAAAARFHZXQgdGhlIGN1cnJlbnQgY29udHJhY3QgdmVyc2lvbgoKUmV0dXJucyB0aGUgc2VtYW50aWMgdmVyc2lvbiBvZiB0aGUgY3VycmVudCBjb250cmFjdCBkZXBsb3ltZW50LgpUaGlzIGlzIHVzZWZ1bCBmb3IgdHJhY2tpbmcgY29udHJhY3QgdXBncmFkZXMgYW5kIGNvbXBhdGliaWxpdHkuCgojIEFyZ3VtZW50cwoqIGBfZW52YCAtIFRoZSBTb3JvYmFuIGVudmlyb25tZW50ICh1bnVzZWQpCgojIFJldHVybnMKKiBgU3RyaW5nYCAtIFRoZSBjdXJyZW50IGNvbnRyYWN0IHZlcnNpb24AAAAAAAAUZ2V0X2NvbnRyYWN0X3ZlcnNpb24AAAAAAAAAAQAAABA=", + "AAAAAAAAAapFeHBvcnQgYWxsIHVzZXIgZGF0YSBmb3IgYmFja3VwIHB1cnBvc2VzIChhZG1pbiBvbmx5KQoKVGhpcyBmdW5jdGlvbiBleHBvcnRzIGFsbCB1c2VyIHByb2ZpbGVzIGFuZCBhZG1pbmlzdHJhdGl2ZSBkYXRhCmZvciBiYWNrdXAgYW5kIHJlY292ZXJ5IHB1cnBvc2VzLiBPbmx5IGFkbWlucyBjYW4gcGVyZm9ybSB0aGlzIG9wZXJhdGlvbi4KCiMgQXJndW1lbnRzCiogYGVudmAgLSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNhbGxlcmAgLSBBZGRyZXNzIHBlcmZvcm1pbmcgdGhlIGV4cG9ydCAobXVzdCBiZSBhZG1pbikKCiMgUmV0dXJucwoqIGBVc2VyQmFja3VwRGF0YWAgLSBDb21wbGV0ZSBiYWNrdXAgZGF0YSBzdHJ1Y3R1cmUKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGlzIG5vdCBhbiBhZG1pbgoqIElmIHN5c3RlbSBpcyBub3QgaW5pdGlhbGl6ZWQAAAAAABBleHBvcnRfdXNlcl9kYXRhAAAAAQAAAAAAAAAGY2FsbGVyAAAAAAATAAAAAQAAB9AAAAAOVXNlckJhY2t1cERhdGEAAA==", + "AAAAAAAAAdJJbXBvcnQgdXNlciBkYXRhIGZyb20gYmFja3VwIChhZG1pbiBvbmx5KQoKVGhpcyBmdW5jdGlvbiBpbXBvcnRzIHVzZXIgZGF0YSBmcm9tIGEgYmFja3VwIHN0cnVjdHVyZS4KT25seSBhZG1pbnMgY2FuIHBlcmZvcm0gdGhpcyBvcGVyYXRpb24uIFRoaXMgd2lsbCBvdmVyd3JpdGUgZXhpc3RpbmcgZGF0YS4KCiMgQXJndW1lbnRzCiogYGVudmAgLSBTb3JvYmFuIGVudmlyb25tZW50CiogYGNhbGxlcmAgLSBBZGRyZXNzIHBlcmZvcm1pbmcgdGhlIGltcG9ydCAobXVzdCBiZSBhZG1pbikKKiBgYmFja3VwX2RhdGFgIC0gQmFja3VwIGRhdGEgc3RydWN0dXJlIHRvIGltcG9ydAoKIyBSZXR1cm5zCiogYHUzMmAgLSBOdW1iZXIgb2YgdXNlcnMgaW1wb3J0ZWQKCiMgUGFuaWNzCiogSWYgY2FsbGVyIGlzIG5vdCBhbiBhZG1pbgoqIElmIGJhY2t1cCBkYXRhIGlzIGludmFsaWQKKiBJZiBpbXBvcnQgb3BlcmF0aW9uIGZhaWxzAAAAAAAQaW1wb3J0X3VzZXJfZGF0YQAAAAIAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAAAAAALYmFja3VwX2RhdGEAAAAH0AAAAA5Vc2VyQmFja3VwRGF0YQAAAAAAAQAAAAQ=" ]), + options + ) + } + public readonly fromJSON = { + get_user_profile: this.txFromJSON>, + get_user_by_id: this.txFromJSON, + create_user_profile: this.txFromJSON, + edit_user_profile: this.txFromJSON, + is_admin: this.txFromJSON, + delete_user: this.txFromJSON, + list_all_users: this.txFromJSON>, + list_all_users_advanced: this.txFromJSON>, + list_all_users_cursor: this.txFromJSON, + initialize_system: this.txFromJSON, + add_admin: this.txFromJSON, + remove_admin: this.txFromJSON, + get_admins: this.txFromJSON>, + is_system_initialized: this.txFromJSON, + get_contract_version: this.txFromJSON, + export_user_data: this.txFromJSON, + import_user_data: this.txFromJSON + } +} \ No newline at end of file diff --git a/schemas/user_management-ts/tsconfig.json b/schemas/user_management-ts/tsconfig.json new file mode 100644 index 0000000..acac142 --- /dev/null +++ b/schemas/user_management-ts/tsconfig.json @@ -0,0 +1,98 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + /* Modules */ + "module": "NodeNext", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + /* Type Checking */ + // "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": [ + "src/*" + ] +} \ No newline at end of file diff --git a/schemas/user_management_docs.md b/schemas/user_management_docs.md new file mode 100644 index 0000000..94705a9 --- /dev/null +++ b/schemas/user_management_docs.md @@ -0,0 +1,1825 @@ +Env Meta: AAAAAAAAABYAAAAA + • Protocol Version: 22 + +Contract Meta: + • rsver: 1.90.0-nightly + • rssdkver: 22.0.8#f46e9e0610213bbb72285566f9dd960ff96d03d8 + +Contract Spec: + • Error: Error + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(AlreadInitialized), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidMaxPageSize), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(SystemNotInitialized), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(AccessDenied), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(SuperAdminNotRegular), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(OperationFailed), + value: 6, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(MaxAdminsReached), + value: 7, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CannotRemoveSuperAdmin), + value: 8, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UserProfileExists), + value: 9, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(NameRequired), + value: 10, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmailRequired), + value: 11, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(CountryRequired), + value: 12, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidEmailFormat), + value: 15, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(EmailAlreadyExists), + value: 16, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidField), + value: 17, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidProfilePicURL), + value: 19, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UserNotFound), + value: 20, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(UserProfileNotFound), + value: 21, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InactiveUser), + value: 22, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PageParamTooLarge), + value: 23, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(InvalidTitleLength), + value: 24, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordMismatch), + value: 25, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(RateLimitExceeded), + value: 26, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(RateLimitNotConfigured), + value: 27, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordTooShort), + value: 28, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordTooLong), + value: 29, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordMissingUppercase), + value: 30, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordMissingLowercase), + value: 31, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordMissingDigit), + value: 32, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(PasswordMissingSpecialChar), + value: 33, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(RequiredFieldMissing), + value: 34, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(), + name: StringM(Unauthorized), + value: 35, + } + + • Error: VersioningError + Docs: Errors that can occur during contract versioning operations + Cases: + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Invalid version format), + name: StringM(InvalidVersion), + value: 1, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Version not found in history), + name: StringM(VersionNotFound), + value: 2, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration not compatible), + name: StringM(MigrationNotCompatible), + value: 3, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration already completed), + name: StringM(MigrationAlreadyCompleted), + value: 4, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Unauthorized migration attempt), + name: StringM(UnauthorizedMigration), + value: 5, + } + • ScSpecUdtErrorEnumCaseV0 { + doc: StringM(Migration failed), + name: StringM(MigrationFailed), + value: 6, + } + + • Struct: UserProfile + Fields: + • contact_email: String + StringM(User's contact email address (required, must be unique)) + • country: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's country of residence (optional)) + • full_name: String + StringM(User's full name (required)) + • profession: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profession or job title (optional)) + • profile_picture_url: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profile picture URL (optional)) + • purpose: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's learning goals or purpose (optional)) + + • Union: DataKey + Docs: Data keys for contract storage + + Currently includes only UserProfile keyed by user Address + Cases: + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(UserProfile), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(), + name: StringM(EmailIndex), + type_: VecM( + [ + String, + ], + ), + }, + ) + + • Struct: UserProfile + Docs: User profile information matching UI definition. + + This struct contains user profile data with required and optional fields + as defined by the user interface requirements. + Fields: + • contact_email: String + StringM(User's contact email address (required, must be unique)) + • country: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's country of residence (optional)) + • full_name: String + StringM(User's full name (required)) + • profession: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profession or job title (optional)) + • profile_picture_url: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profile picture URL (optional)) + • purpose: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's learning goals or purpose (optional)) + + • Struct: ProfileUpdateParams + Docs: Struct for profile update parameters + Only includes fields that can be updated + Fields: + • country: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's country of residence) + • full_name: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's full name (optional update)) + • profession: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profession or job title) + • profile_picture_url: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profile picture URL) + • purpose: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's learning goals or purpose) + + • Union: UserRole + Docs: User roles in the SkillCert platform. + + Defines the different types of users and their permission levels. + Cases: + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Regular platform user who can enroll in courses), + name: StringM(Student), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(User who can create and manage courses), + name: StringM(Instructor), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Platform administrator with elevated privileges), + name: StringM(Admin), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Super administrator with full system access), + name: StringM(SuperAdmin), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Content moderator with course content permissions), + name: StringM(Moderator), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Support staff with user assistance permissions), + name: StringM(Support), + }, + ) + + • Union: Permission + Docs: Granular permissions for RBAC system. + + Defines specific actions that can be granted or denied to users. + Cases: + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can view user profiles), + name: StringM(ViewUsers), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can edit user profiles (own or others)), + name: StringM(EditUsers), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can delete/deactivate users), + name: StringM(DeleteUsers), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can create new user accounts), + name: StringM(CreateUsers), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can view course details), + name: StringM(ViewCourses), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can create new courses), + name: StringM(CreateCourses), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can edit course content), + name: StringM(EditCourses), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can delete courses), + name: StringM(DeleteCourses), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can manage course access (grant/revoke)), + name: StringM(ManageCourseAccess), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can manage system configuration), + name: StringM(ManageSystem), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can manage admin roles), + name: StringM(ManageAdmins), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can view system analytics), + name: StringM(ViewAnalytics), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can moderate content), + name: StringM(ModerateContent), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can provide user support), + name: StringM(ProvideSupport), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Can view support tickets), + name: StringM(ViewSupport), + }, + ) + + • Struct: RolePermissions + Docs: Role-based permissions mapping. + + Defines which permissions are granted to each role by default. + Fields: + • permissions: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Permission), + }, + ), + }, + ) + StringM(List of permissions granted to this role) + • role: Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ) + StringM(The role this permission set applies to) + + • Struct: UserPermissions + Docs: User-specific permission overrides. + + Allows granting or revoking specific permissions to individual users. + Fields: + • granted_permissions: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Permission), + }, + ), + }, + ) + StringM(Additional permissions granted beyond role defaults) + • revoked_permissions: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(Permission), + }, + ), + }, + ) + StringM(Permissions explicitly revoked from role defaults) + • user: Address + StringM(The user address) + + • Union: UserStatus + Docs: User account status. + + Represents the current state of a user's account. + Cases: + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(User account is active and functional), + name: StringM(Active), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(User account is deactivated), + name: StringM(Inactive), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(User account is temporarily suspended), + name: StringM(Suspended), + }, + ) + + • Struct: LightProfile + Docs: Lightweight user profile for listing operations. + + Contains essential user information for efficient querying and display in user lists. + Fields: + • country: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's country of residence) + • full_name: String + StringM(User's full name) + • profession: Option( + ScSpecTypeOption { + value_type: String, + }, + ) + StringM(User's profession or job title) + • role: Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ) + StringM(User's role in the platform) + • status: Udt( + ScSpecTypeUdt { + name: StringM(UserStatus), + }, + ) + StringM(User's account status) + • user_address: Address + StringM(User's blockchain address) + + • Struct: RateLimitConfig + Docs: Rate limiting configuration for user operations. + + Tracks rate limiting settings and current usage for spam protection. + Fields: + • max_operations_per_window: U32 + StringM(Maximum operations allowed per window) + • window_seconds: U64 + StringM(Time window for rate limiting in seconds) + + • Struct: RateLimitData + Docs: Rate limiting tracking data for a specific address. + + Stores the current usage count and window start time for rate limiting. + Fields: + • count: U32 + StringM(Current count of operations in this window) + • window_start: U64 + StringM(Timestamp when the current window started) + + • Struct: AdminConfig + Docs: Administrative configuration for the user management system. + + Contains system-wide settings and administrative information. + Fields: + • initialized: Bool + StringM(Whether the system has been initialized) + • max_page_size: U32 + StringM(Maximum allowed page size for queries) + • rate_limit_config: Udt( + ScSpecTypeUdt { + name: StringM(RateLimitConfig), + }, + ) + StringM(Rate limiting configuration for user creation) + • super_admin: Address + StringM(Address of the super administrator) + • total_user_count: U32 + StringM(Total number of registered users) + + • Struct: UserBackupData + Docs: Backup data structure for user management system. + + Contains all user data and system configuration for backup and recovery operations. + Fields: + • admin_config: Udt( + ScSpecTypeUdt { + name: StringM(AdminConfig), + }, + ) + StringM(Administrative configuration) + • admins: Vec( + ScSpecTypeVec { + element_type: Address, + }, + ) + StringM(List of admin addresses) + • backup_timestamp: U64 + StringM(Backup timestamp) + • backup_version: String + StringM(Backup version for compatibility) + • email_mappings: Map( + ScSpecTypeMap { + key_type: String, + value_type: Address, + }, + ) + StringM(Email to address mapping for uniqueness) + • light_profiles: Map( + ScSpecTypeMap { + key_type: Address, + value_type: Udt( + ScSpecTypeUdt { + name: StringM(LightProfile), + }, + ), + }, + ) + StringM(All lightweight profiles for efficient queries) + • user_profiles: Map( + ScSpecTypeMap { + key_type: Address, + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + }, + ) + StringM(All user profiles in the system) + • users_index: Vec( + ScSpecTypeVec { + element_type: Address, + }, + ) + StringM(List of all registered user addresses) + + • Struct: PaginationParams + Docs: Pagination parameters for cursor-based pagination. + + Used to implement efficient pagination that avoids gas limit issues + with large datasets by using cursor-based navigation. + Fields: + • cursor: Option( + ScSpecTypeOption { + value_type: Address, + }, + ) + StringM(Cursor for pagination (address of the last item from previous page)) + • limit: U32 + StringM(Maximum number of items to return per page) + + • Struct: PaginatedLightProfiles + Docs: Pagination result with metadata for efficient navigation. + + Contains the paginated data along with pagination metadata + to enable cursor-based navigation. + Fields: + • data: Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(LightProfile), + }, + ), + }, + ) + StringM(The paginated data items) + • has_more: Bool + StringM(Whether there are more pages available) + • next_cursor: Option( + ScSpecTypeOption { + value_type: Address, + }, + ) + StringM(Cursor for the next page (None if this is the last page)) + • total_count: Option( + ScSpecTypeOption { + value_type: U32, + }, + ) + StringM(Total count of items matching the filter (optional, may be expensive to compute)) + + • Union: DataKey + Docs: Storage keys for different data types in the user management contract. + + This enum defines the various keys used to store and retrieve + user data from the contract's persistent storage. + Cases: + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing complete user profiles: user_address -> UserProfile), + name: StringM(UserProfile), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing admin flags: address -> bool), + name: StringM(Admin), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing lightweight user profiles: user_address -> LightProfile), + name: StringM(UserProfileLight), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Key for storing the list of all registered user addresses), + name: StringM(UsersIndex), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for email to address mapping to ensure email uniqueness: email -> Address), + name: StringM(EmailIndex), + type_: VecM( + [ + String, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Key for storing the list of admin addresses), + name: StringM(Admins), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing user role assignments: user_address -> UserRole), + name: StringM(UserRole), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Key for storing administrative configuration), + name: StringM(AdminConfig), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing rate limiting data per address: address -> RateLimitData), + name: StringM(RateLimit), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing role-based permissions: role -> RolePermissions), + name: StringM(RolePermissions), + type_: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ), + ], + ), + }, + ) + • TupleV0( + ScSpecUdtUnionCaseTupleV0 { + doc: StringM(Key for storing user-specific permission overrides: user_address -> UserPermissions), + name: StringM(UserPermissions), + type_: VecM( + [ + Address, + ], + ), + }, + ) + • VoidV0( + ScSpecUdtUnionCaseVoidV0 { + doc: StringM(Key for storing default role permissions configuration), + name: StringM(DefaultRolePermissions), + }, + ) + + • Function: get_user_profile + Docs: Retrieve a user profile for the authenticated user. + + This function fetches the complete user profile associated with the provided + blockchain address. The user must be authenticated; otherwise, the function + will panic. + + ### Arguments + + * `env` - The Soroban environment. + * `user` - The address of the user whose profile is being requested. + + ### Returns + + Returns the `UserProfile` corresponding to the authenticated user. + + ### Panics + + * If the user is not authenticated (`require_auth` fails). + * If the user profile does not exist (`UserNotFound` error). + + ### Examples + + ```rust + // Assuming the user is authenticated in the environment + let profile = contract.get_user_profile(env.clone(), my_address); + println!("User full name: {}", profile.full_name); + ``` + + ### Notes + + * Only the user themselves can fetch their profile; there is no admin override + in this function. + * If the profile is not found in storage, the function will panic with + `UserNotFound`. + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Result( + ScSpecTypeResult { + ok_type: Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + error_type: Error, + }, + ), + ], + ) + + • Function: get_user_by_id + Docs: Retrieve a user profile by their address. + + This function fetches a complete user profile using the user's blockchain address. + Access may be restricted based on the requester's permissions. + + # Arguments + + * `env` - The Soroban environment + * `requester` - The address of the user requesting the profile + * `user_id` - The address of the user whose profile is being requested + + # Returns + + Returns the requested `UserProfile`. + + # Panics + + * If the user profile doesn't exist + * If the requester doesn't have permission to view the profile + * If the requester is not the user themselves or an admin + + # Examples + + ```rust + // Get your own profile + let my_profile = contract.get_user_by_id(env.clone(), my_address, my_address); + + // Admin getting any user's profile + let user_profile = contract.get_user_by_id(env.clone(), admin_address, user_address); + ``` + + # Edge Cases + + * **Non-existent user**: Will panic with appropriate error message + * **Inactive user**: Returns profile but status will be `UserStatus::Inactive` + * **Permission denied**: + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(requester), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user_id), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + ], + ) + + • Function: create_user_profile + Docs: Create a new user profile + + Creates a new user profile using a UserProfile struct. + Validates mandatory fields (full_name and contact_email) and saves the profile. + + # Arguments + * `env` - Soroban environment + * `user` - Address of the user whose profile is being created + * `profile` - UserProfile struct containing all profile data + + # Returns + * `UserProfile` - The created user profile + + # Panics + * If mandatory fields (full_name, contact_email) are missing + * If user profile already exists + * If email format is invalid + * If validation rules are violated + + # Events + Emits a user creation event upon successful creation + + # Examples + + ```rust + let profile = UserProfile { + full_name: "John Doe".try_into().unwrap(), + contact_email: "john@example.com".try_into().unwrap(), + role: UserRole::Student, + status: UserStatus::Active, + country: Some("US".try_into().unwrap()), + ..Default::default() + }; + + let created_profile = contract.create_user_profile(env, user_address, profile); + ``` + + # Edge Cases + + * **Duplicate profile**: Will panic if user al + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(profile), + type_: Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + ], + ) + + • Function: edit_user_profile + Docs: Edit an existing user profile + + Updates an existing user profile with new values for allowed fields. + Only the user themselves or administrators can perform updates. + Email and role fields cannot be updated through this function. + + # Arguments + * `env` - Soroban environment + * `caller` - Address of the user performing the update + * `user_id` - Address of the user whose profile is being updated + * `updates` - ProfileUpdateParams containing fields to update + + # Returns + * `UserProfile` - The updated user profile + + # Panics + * If caller authentication fails + * If user profile doesn't exist + * If caller lacks permission to edit + * If any field validation fails + * If user is inactive + + # Events + Emits a user update event upon successful profile update + + # Examples + + ```rust + let updates = ProfileUpdateParams { + full_name: Some("Jane Doe".try_into().unwrap()), + country: Some("CA".try_into().unwrap()), + bio: Some("Updated bio".try_into().unwrap()), + ..Default::default() + }; + + let updated_profile = contract.edit_user_profile(env, caller_addres + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user_id), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(updates), + type_: Udt( + ScSpecTypeUdt { + name: StringM(ProfileUpdateParams), + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserProfile), + }, + ), + ], + ) + + • Function: is_admin + Docs: Check if an address has admin privileges. + + This function is used by other contracts to verify admin status + for cross-contract authorization checks. + + # Arguments + + * `env` - The Soroban environment + * `who` - The address to check for admin privileges + + # Returns + + Returns `true` if the address has admin privileges, `false` otherwise. + + # Examples + + ```rust + // Check if user is admin + let is_admin = contract.is_admin(env.clone(), user_address); + if is_admin { + // Perform admin operations + } + + // Cross-contract admin check + let can_perform_action = contract.is_admin(env.clone(), caller_address); + ``` + + # Edge Cases + + * **System not initialized**: Returns `false` if admin system hasn't been set up + * **Non-existent user**: Returns `false` for addresses that don't exist + * **Regular users**: Always returns `false` for non-admin users + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(who), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: delete_user + Docs: Delete (deactivate) a user account + + Performs a soft delete by marking the user as inactive instead of permanent deletion. + Only admins or the user themselves can trigger deletion. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the deletion (must be admin or the user themselves) + * `user_id` - Address of the user to be deactivated + + # Panics + * If caller authentication fails + * If user doesn't exist + * If caller is neither admin nor the user themselves + * If user is already inactive + + # Events + Emits a user deactivation event upon successful deletion + + # Examples + + ```rust + // User deleting their own account + contract.delete_user(env.clone(), user_address, user_address); + + // Admin deleting another user's account + contract.delete_user(env.clone(), admin_address, user_to_delete); + ``` + + # Edge Cases + + * **Already inactive**: Will panic if trying to delete an already inactive user + * **Permission denied**: Non-admin users can only delete their own accounts + * **Data preservation**: User data is preserved + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(user_id), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: list_all_users + Docs: Lists all registered users with pagination and filtering (admin-only) + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be admin) + * `page` - Zero-based page index + * `page_size` - Number of items per page (must be > 0) + * `role_filter` - Optional role filter + * `country_filter` - Optional country filter + * `status_filter` - Optional status filter + + # Returns + * `Vec` - Filtered and paginated lightweight user profiles + + # Panics + * If caller is not an admin + * If page_size is 0 or exceeds maximum allowed + * If system is not initialized + + # Examples + + ```rust + // Get first page with 10 users + let users = contract.list_all_users( + env.clone(), + admin_address, + 0, // page 0 + 10, // page size + None, None, None // no filters + ); + + // Filter by role and country + let students = contract.list_all_users( + env.clone(), + admin_address, + 0, 20, + Some(UserRole::Student), + Some("US".try_into().unwrap()), + None + ); + ``` + + # Edge Cases + + * **Empty results**: Returns empty vector if no users match filter + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(page), + type_: U32, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(page_size), + type_: U32, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(role_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(country_filter), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(status_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserStatus), + }, + ), + }, + ), + }, + ], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(LightProfile), + }, + ), + }, + ), + ], + ) + + • Function: list_all_users_advanced + Docs: Lists all registered users with advanced filtering including text search (admin-only). + + This is the new version that supports text search functionality. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be admin) + * `page` - Zero-based page index + * `page_size` - Number of items per page + * `role_filter` - Optional role filter + * `country_filter` - Optional country filter + * `status_filter` - Optional status filter + * `search_text` - Optional text search in name and profession + + # Returns + * `Vec` - Filtered and paginated lightweight user profiles + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(page), + type_: U32, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(page_size), + type_: U32, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(role_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(country_filter), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(status_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserStatus), + }, + ), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(search_text), + type_: Option( + ScSpecTypeOption { + value_type: String, + }, + ), + }, + ], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Udt( + ScSpecTypeUdt { + name: StringM(LightProfile), + }, + ), + }, + ), + ], + ) + + • Function: list_all_users_cursor + Docs: Lists all registered users with cursor-based pagination and filtering (admin-only) + + This function implements efficient cursor-based pagination to avoid gas limit issues + when dealing with large datasets. It returns a PaginatedResult with metadata for + efficient navigation. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be admin) + * `pagination` - Pagination parameters including cursor and limit + * `role_filter` - Optional filter for user role + * `status_filter` - Optional filter for user status + + # Returns + * `PaginatedLightProfiles` - Paginated results with navigation metadata + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(pagination), + type_: Udt( + ScSpecTypeUdt { + name: StringM(PaginationParams), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(role_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserRole), + }, + ), + }, + ), + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(status_filter), + type_: Option( + ScSpecTypeOption { + value_type: Udt( + ScSpecTypeUdt { + name: StringM(UserStatus), + }, + ), + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(PaginatedLightProfiles), + }, + ), + ], + ) + + • Function: initialize_system + Docs: Initialize the admin system (one-time only) + + # Arguments + * `env` - Soroban environment + * `initializer` - Address performing the initialization + * `super_admin` - Address that will become the super admin + * `max_page_size` - Optional maximum page size (default: 100, max: 1000) + + # Returns + * `AdminConfig` - The created admin configuration + + # Panics + * If system has already been initialized + * If max_page_size exceeds 1000 + + # Examples + + ```rust + // Initialize with default settings + let config = contract.initialize_system( + env.clone(), + deployer_address, + super_admin_address, + None + ); + + // Initialize with custom page size + let config = contract.initialize_system( + env.clone(), + deployer_address, + super_admin_address, + Some(500) + ); + ``` + + # Edge Cases + + * **Double initialization**: Will panic if called more than once + * **Invalid page size**: Will panic if max_page_size > 1000 + * **Super admin privileges**: Super admin cannot be removed after initialization + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(initializer), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(super_admin), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(max_page_size), + type_: Option( + ScSpecTypeOption { + value_type: U32, + }, + ), + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(AdminConfig), + }, + ), + ], + ) + + • Function: add_admin + Docs: Add a new admin (super admin only) + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be super admin) + * `new_admin` - Address to be added as admin + + # Panics + * If caller is not the super admin + * If system is not initialized + * If new_admin is already an admin + + # Examples + + ```rust + // Super admin adding a new admin + contract.add_admin(env.clone(), super_admin_address, new_admin_address); + ``` + + # Edge Cases + + * **Already admin**: Will panic if trying to add an existing admin + * **Self-promotion**: Super admin cannot add themselves (redundant) + * **Non-existent user**: Can add admin privileges to any address + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(new_admin), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: remove_admin + Docs: Remove an admin (super admin only) + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be super admin) + * `admin_to_remove` - Address to be removed from admins + + # Panics + * If caller is not the super admin + * If system is not initialized + * If admin_to_remove is not an admin + * If trying to remove the super admin + + # Examples + + ```rust + // Super admin removing another admin + contract.remove_admin(env.clone(), super_admin_address, admin_to_remove); + ``` + + # Edge Cases + + * **Super admin protection**: Cannot remove the super admin + * **Non-admin**: Will panic if trying to remove a non-admin address + * **Self-removal**: Super admin cannot remove themselves + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(admin_to_remove), + type_: Address, + }, + ], + ) + Output: VecM( + [], + ) + + • Function: get_admins + Docs: Get list of all admins (admin only) + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the call (must be admin) + + # Returns + * `Vec
` - List of all admin addresses including super admin + + # Panics + * If caller is not an admin + * If system is not initialized + + # Examples + + ```rust + // Get all admin addresses + let admins = contract.get_admins(env.clone(), admin_address); + for admin in admins { + // Process each admin address + } + ``` + + # Edge Cases + + * **Empty list**: Returns vector with only super admin if no other admins exist + * **Admin only**: Only admins can view the admin list + * **Order**: Super admin is typically first in the list + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Vec( + ScSpecTypeVec { + element_type: Address, + }, + ), + ], + ) + + • Function: is_system_initialized + Docs: Check if the system is initialized + + # Arguments + * `env` - Soroban environment + + # Returns + * `bool` - True if system is initialized + + # Examples + + ```rust + // Check if admin system is ready + let is_initialized = contract.is_system_initialized(env.clone()); + if !is_initialized { + // Initialize the system first + contract.initialize_system(env, deployer, super_admin, None); + } + ``` + + # Edge Cases + + * **Fresh deployment**: Returns `false` for newly deployed contracts + * **Public access**: Anyone can check initialization status + * **One-time check**: Once initialized, always returns `true` + Inputs: VecM( + [], + ) + Output: VecM( + [ + Bool, + ], + ) + + • Function: get_contract_version + Docs: Get the current contract version + + Returns the semantic version of the current contract deployment. + This is useful for tracking contract upgrades and compatibility. + + # Arguments + * `_env` - The Soroban environment (unused) + + # Returns + * `String` - The current contract version + Inputs: VecM( + [], + ) + Output: VecM( + [ + String, + ], + ) + + • Function: export_user_data + Docs: Export all user data for backup purposes (admin only) + + This function exports all user profiles and administrative data + for backup and recovery purposes. Only admins can perform this operation. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the export (must be admin) + + # Returns + * `UserBackupData` - Complete backup data structure + + # Panics + * If caller is not an admin + * If system is not initialized + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ], + ) + Output: VecM( + [ + Udt( + ScSpecTypeUdt { + name: StringM(UserBackupData), + }, + ), + ], + ) + + • Function: import_user_data + Docs: Import user data from backup (admin only) + + This function imports user data from a backup structure. + Only admins can perform this operation. This will overwrite existing data. + + # Arguments + * `env` - Soroban environment + * `caller` - Address performing the import (must be admin) + * `backup_data` - Backup data structure to import + + # Returns + * `u32` - Number of users imported + + # Panics + * If caller is not an admin + * If backup data is invalid + * If import operation fails + Inputs: VecM( + [ + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(caller), + type_: Address, + }, + ScSpecFunctionInputV0 { + doc: StringM(), + name: StringM(backup_data), + type_: Udt( + ScSpecTypeUdt { + name: StringM(UserBackupData), + }, + ), + }, + ], + ) + Output: VecM( + [ + U32, + ], + ) + + diff --git a/schemas/user_management_schema.json b/schemas/user_management_schema.json new file mode 100644 index 0000000..e2ce0bc --- /dev/null +++ b/schemas/user_management_schema.json @@ -0,0 +1,1541 @@ +[ + { + "type": "enum", + "doc": "", + "name": "Error", + "cases": [ + { + "doc": "", + "name": "AlreadInitialized", + "value": 1 + }, + { + "doc": "", + "name": "InvalidMaxPageSize", + "value": 2 + }, + { + "doc": "", + "name": "SystemNotInitialized", + "value": 3 + }, + { + "doc": "", + "name": "AccessDenied", + "value": 4 + }, + { + "doc": "", + "name": "SuperAdminNotRegular", + "value": 5 + }, + { + "doc": "", + "name": "OperationFailed", + "value": 6 + }, + { + "doc": "", + "name": "MaxAdminsReached", + "value": 7 + }, + { + "doc": "", + "name": "CannotRemoveSuperAdmin", + "value": 8 + }, + { + "doc": "", + "name": "UserProfileExists", + "value": 9 + }, + { + "doc": "", + "name": "NameRequired", + "value": 10 + }, + { + "doc": "", + "name": "EmailRequired", + "value": 11 + }, + { + "doc": "", + "name": "CountryRequired", + "value": 12 + }, + { + "doc": "", + "name": "InvalidEmailFormat", + "value": 15 + }, + { + "doc": "", + "name": "EmailAlreadyExists", + "value": 16 + }, + { + "doc": "", + "name": "InvalidField", + "value": 17 + }, + { + "doc": "", + "name": "InvalidProfilePicURL", + "value": 19 + }, + { + "doc": "", + "name": "UserNotFound", + "value": 20 + }, + { + "doc": "", + "name": "UserProfileNotFound", + "value": 21 + }, + { + "doc": "", + "name": "InactiveUser", + "value": 22 + }, + { + "doc": "", + "name": "PageParamTooLarge", + "value": 23 + }, + { + "doc": "", + "name": "InvalidTitleLength", + "value": 24 + }, + { + "doc": "", + "name": "PasswordMismatch", + "value": 25 + }, + { + "doc": "", + "name": "RateLimitExceeded", + "value": 26 + }, + { + "doc": "", + "name": "RateLimitNotConfigured", + "value": 27 + }, + { + "doc": "", + "name": "PasswordTooShort", + "value": 28 + }, + { + "doc": "", + "name": "PasswordTooLong", + "value": 29 + }, + { + "doc": "", + "name": "PasswordMissingUppercase", + "value": 30 + }, + { + "doc": "", + "name": "PasswordMissingLowercase", + "value": 31 + }, + { + "doc": "", + "name": "PasswordMissingDigit", + "value": 32 + }, + { + "doc": "", + "name": "PasswordMissingSpecialChar", + "value": 33 + }, + { + "doc": "", + "name": "RequiredFieldMissing", + "value": 34 + }, + { + "doc": "", + "name": "Unauthorized", + "value": 35 + } + ] + }, + { + "type": "enum", + "doc": "Errors that can occur during contract versioning operations", + "name": "VersioningError", + "cases": [ + { + "doc": "Invalid version format", + "name": "InvalidVersion", + "value": 1 + }, + { + "doc": "Version not found in history", + "name": "VersionNotFound", + "value": 2 + }, + { + "doc": "Migration not compatible", + "name": "MigrationNotCompatible", + "value": 3 + }, + { + "doc": "Migration already completed", + "name": "MigrationAlreadyCompleted", + "value": 4 + }, + { + "doc": "Unauthorized migration attempt", + "name": "UnauthorizedMigration", + "value": 5 + }, + { + "doc": "Migration failed", + "name": "MigrationFailed", + "value": 6 + } + ] + }, + { + "type": "struct", + "doc": "", + "name": "UserProfile", + "fields": [ + { + "doc": "User's contact email address (required, must be unique)", + "name": "contact_email", + "value": { + "type": "string" + } + }, + { + "doc": "User's country of residence (optional)", + "name": "country", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's full name (required)", + "name": "full_name", + "value": { + "type": "string" + } + }, + { + "doc": "User's profession or job title (optional)", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's profile picture URL (optional)", + "name": "profile_picture_url", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's learning goals or purpose (optional)", + "name": "purpose", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "union", + "doc": "Data keys for contract storage\n\nCurrently includes only UserProfile keyed by user Address", + "name": "DataKey", + "cases": [ + { + "doc": "", + "name": "UserProfile", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "", + "name": "EmailIndex", + "values": [ + { + "type": "string" + } + ] + } + ] + }, + { + "type": "struct", + "doc": "User profile information matching UI definition.\n\nThis struct contains user profile data with required and optional fields\nas defined by the user interface requirements.", + "name": "UserProfile", + "fields": [ + { + "doc": "User's contact email address (required, must be unique)", + "name": "contact_email", + "value": { + "type": "string" + } + }, + { + "doc": "User's country of residence (optional)", + "name": "country", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's full name (required)", + "name": "full_name", + "value": { + "type": "string" + } + }, + { + "doc": "User's profession or job title (optional)", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's profile picture URL (optional)", + "name": "profile_picture_url", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's learning goals or purpose (optional)", + "name": "purpose", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "struct", + "doc": "Struct for profile update parameters\nOnly includes fields that can be updated", + "name": "ProfileUpdateParams", + "fields": [ + { + "doc": "User's country of residence", + "name": "country", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's full name (optional update)", + "name": "full_name", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's profession or job title", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's profile picture URL", + "name": "profile_picture_url", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's learning goals or purpose", + "name": "purpose", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ] + }, + { + "type": "union", + "doc": "User roles in the SkillCert platform.\n\nDefines the different types of users and their permission levels.", + "name": "UserRole", + "cases": [ + { + "doc": "Regular platform user who can enroll in courses", + "name": "Student", + "values": [] + }, + { + "doc": "User who can create and manage courses", + "name": "Instructor", + "values": [] + }, + { + "doc": "Platform administrator with elevated privileges", + "name": "Admin", + "values": [] + }, + { + "doc": "Super administrator with full system access", + "name": "SuperAdmin", + "values": [] + }, + { + "doc": "Content moderator with course content permissions", + "name": "Moderator", + "values": [] + }, + { + "doc": "Support staff with user assistance permissions", + "name": "Support", + "values": [] + } + ] + }, + { + "type": "union", + "doc": "Granular permissions for RBAC system.\n\nDefines specific actions that can be granted or denied to users.", + "name": "Permission", + "cases": [ + { + "doc": "Can view user profiles", + "name": "ViewUsers", + "values": [] + }, + { + "doc": "Can edit user profiles (own or others)", + "name": "EditUsers", + "values": [] + }, + { + "doc": "Can delete/deactivate users", + "name": "DeleteUsers", + "values": [] + }, + { + "doc": "Can create new user accounts", + "name": "CreateUsers", + "values": [] + }, + { + "doc": "Can view course details", + "name": "ViewCourses", + "values": [] + }, + { + "doc": "Can create new courses", + "name": "CreateCourses", + "values": [] + }, + { + "doc": "Can edit course content", + "name": "EditCourses", + "values": [] + }, + { + "doc": "Can delete courses", + "name": "DeleteCourses", + "values": [] + }, + { + "doc": "Can manage course access (grant/revoke)", + "name": "ManageCourseAccess", + "values": [] + }, + { + "doc": "Can manage system configuration", + "name": "ManageSystem", + "values": [] + }, + { + "doc": "Can manage admin roles", + "name": "ManageAdmins", + "values": [] + }, + { + "doc": "Can view system analytics", + "name": "ViewAnalytics", + "values": [] + }, + { + "doc": "Can moderate content", + "name": "ModerateContent", + "values": [] + }, + { + "doc": "Can provide user support", + "name": "ProvideSupport", + "values": [] + }, + { + "doc": "Can view support tickets", + "name": "ViewSupport", + "values": [] + } + ] + }, + { + "type": "struct", + "doc": "Role-based permissions mapping.\n\nDefines which permissions are granted to each role by default.", + "name": "RolePermissions", + "fields": [ + { + "doc": "List of permissions granted to this role", + "name": "permissions", + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "Permission" + } + } + }, + { + "doc": "The role this permission set applies to", + "name": "role", + "value": { + "type": "custom", + "name": "UserRole" + } + } + ] + }, + { + "type": "struct", + "doc": "User-specific permission overrides.\n\nAllows granting or revoking specific permissions to individual users.", + "name": "UserPermissions", + "fields": [ + { + "doc": "Additional permissions granted beyond role defaults", + "name": "granted_permissions", + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "Permission" + } + } + }, + { + "doc": "Permissions explicitly revoked from role defaults", + "name": "revoked_permissions", + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "Permission" + } + } + }, + { + "doc": "The user address", + "name": "user", + "value": { + "type": "address" + } + } + ] + }, + { + "type": "union", + "doc": "User account status.\n\nRepresents the current state of a user's account.", + "name": "UserStatus", + "cases": [ + { + "doc": "User account is active and functional", + "name": "Active", + "values": [] + }, + { + "doc": "User account is deactivated", + "name": "Inactive", + "values": [] + }, + { + "doc": "User account is temporarily suspended", + "name": "Suspended", + "values": [] + } + ] + }, + { + "type": "struct", + "doc": "Lightweight user profile for listing operations.\n\nContains essential user information for efficient querying and display in user lists.", + "name": "LightProfile", + "fields": [ + { + "doc": "User's country of residence", + "name": "country", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's full name", + "name": "full_name", + "value": { + "type": "string" + } + }, + { + "doc": "User's profession or job title", + "name": "profession", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "User's role in the platform", + "name": "role", + "value": { + "type": "custom", + "name": "UserRole" + } + }, + { + "doc": "User's account status", + "name": "status", + "value": { + "type": "custom", + "name": "UserStatus" + } + }, + { + "doc": "User's blockchain address", + "name": "user_address", + "value": { + "type": "address" + } + } + ] + }, + { + "type": "struct", + "doc": "Rate limiting configuration for user operations.\n\nTracks rate limiting settings and current usage for spam protection.", + "name": "RateLimitConfig", + "fields": [ + { + "doc": "Maximum operations allowed per window", + "name": "max_operations_per_window", + "value": { + "type": "u32" + } + }, + { + "doc": "Time window for rate limiting in seconds", + "name": "window_seconds", + "value": { + "type": "u64" + } + } + ] + }, + { + "type": "struct", + "doc": "Rate limiting tracking data for a specific address.\n\nStores the current usage count and window start time for rate limiting.", + "name": "RateLimitData", + "fields": [ + { + "doc": "Current count of operations in this window", + "name": "count", + "value": { + "type": "u32" + } + }, + { + "doc": "Timestamp when the current window started", + "name": "window_start", + "value": { + "type": "u64" + } + } + ] + }, + { + "type": "struct", + "doc": "Administrative configuration for the user management system.\n\nContains system-wide settings and administrative information.", + "name": "AdminConfig", + "fields": [ + { + "doc": "Whether the system has been initialized", + "name": "initialized", + "value": { + "type": "bool" + } + }, + { + "doc": "Maximum allowed page size for queries", + "name": "max_page_size", + "value": { + "type": "u32" + } + }, + { + "doc": "Rate limiting configuration for user creation", + "name": "rate_limit_config", + "value": { + "type": "custom", + "name": "RateLimitConfig" + } + }, + { + "doc": "Address of the super administrator", + "name": "super_admin", + "value": { + "type": "address" + } + }, + { + "doc": "Total number of registered users", + "name": "total_user_count", + "value": { + "type": "u32" + } + } + ] + }, + { + "type": "struct", + "doc": "Backup data structure for user management system.\n\nContains all user data and system configuration for backup and recovery operations.", + "name": "UserBackupData", + "fields": [ + { + "doc": "Administrative configuration", + "name": "admin_config", + "value": { + "type": "custom", + "name": "AdminConfig" + } + }, + { + "doc": "List of admin addresses", + "name": "admins", + "value": { + "type": "vec", + "element": { + "type": "address" + } + } + }, + { + "doc": "Backup timestamp", + "name": "backup_timestamp", + "value": { + "type": "u64" + } + }, + { + "doc": "Backup version for compatibility", + "name": "backup_version", + "value": { + "type": "string" + } + }, + { + "doc": "Email to address mapping for uniqueness", + "name": "email_mappings", + "value": { + "type": "map", + "key": { + "type": "string" + }, + "value": { + "type": "address" + } + } + }, + { + "doc": "All lightweight profiles for efficient queries", + "name": "light_profiles", + "value": { + "type": "map", + "key": { + "type": "address" + }, + "value": { + "type": "custom", + "name": "LightProfile" + } + } + }, + { + "doc": "All user profiles in the system", + "name": "user_profiles", + "value": { + "type": "map", + "key": { + "type": "address" + }, + "value": { + "type": "custom", + "name": "UserProfile" + } + } + }, + { + "doc": "List of all registered user addresses", + "name": "users_index", + "value": { + "type": "vec", + "element": { + "type": "address" + } + } + } + ] + }, + { + "type": "struct", + "doc": "Pagination parameters for cursor-based pagination.\n\nUsed to implement efficient pagination that avoids gas limit issues\nwith large datasets by using cursor-based navigation.", + "name": "PaginationParams", + "fields": [ + { + "doc": "Cursor for pagination (address of the last item from previous page)", + "name": "cursor", + "value": { + "type": "option", + "value": { + "type": "address" + } + } + }, + { + "doc": "Maximum number of items to return per page", + "name": "limit", + "value": { + "type": "u32" + } + } + ] + }, + { + "type": "struct", + "doc": "Pagination result with metadata for efficient navigation.\n\nContains the paginated data along with pagination metadata\nto enable cursor-based navigation.", + "name": "PaginatedLightProfiles", + "fields": [ + { + "doc": "The paginated data items", + "name": "data", + "value": { + "type": "vec", + "element": { + "type": "custom", + "name": "LightProfile" + } + } + }, + { + "doc": "Whether there are more pages available", + "name": "has_more", + "value": { + "type": "bool" + } + }, + { + "doc": "Cursor for the next page (None if this is the last page)", + "name": "next_cursor", + "value": { + "type": "option", + "value": { + "type": "address" + } + } + }, + { + "doc": "Total count of items matching the filter (optional, may be expensive to compute)", + "name": "total_count", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + } + ] + }, + { + "type": "union", + "doc": "Storage keys for different data types in the user management contract.\n\nThis enum defines the various keys used to store and retrieve\nuser data from the contract's persistent storage.", + "name": "DataKey", + "cases": [ + { + "doc": "Key for storing complete user profiles: user_address -> UserProfile", + "name": "UserProfile", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing admin flags: address -> bool", + "name": "Admin", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing lightweight user profiles: user_address -> LightProfile", + "name": "UserProfileLight", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing the list of all registered user addresses", + "name": "UsersIndex", + "values": [] + }, + { + "doc": "Key for email to address mapping to ensure email uniqueness: email -> Address", + "name": "EmailIndex", + "values": [ + { + "type": "string" + } + ] + }, + { + "doc": "Key for storing the list of admin addresses", + "name": "Admins", + "values": [] + }, + { + "doc": "Key for storing user role assignments: user_address -> UserRole", + "name": "UserRole", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing administrative configuration", + "name": "AdminConfig", + "values": [] + }, + { + "doc": "Key for storing rate limiting data per address: address -> RateLimitData", + "name": "RateLimit", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing role-based permissions: role -> RolePermissions", + "name": "RolePermissions", + "values": [ + { + "type": "custom", + "name": "UserRole" + } + ] + }, + { + "doc": "Key for storing user-specific permission overrides: user_address -> UserPermissions", + "name": "UserPermissions", + "values": [ + { + "type": "address" + } + ] + }, + { + "doc": "Key for storing default role permissions configuration", + "name": "DefaultRolePermissions", + "values": [] + } + ] + }, + { + "type": "function", + "doc": "Retrieve a user profile for the authenticated user.\n\nThis function fetches the complete user profile associated with the provided\nblockchain address. The user must be authenticated; otherwise, the function\nwill panic.\n\n### Arguments\n\n* `env` - The Soroban environment.\n* `user` - The address of the user whose profile is being requested.\n\n### Returns\n\nReturns the `UserProfile` corresponding to the authenticated user.\n\n### Panics\n\n* If the user is not authenticated (`require_auth` fails).\n* If the user profile does not exist (`UserNotFound` error).\n\n### Examples\n\n```rust\n// Assuming the user is authenticated in the environment\nlet profile = contract.get_user_profile(env.clone(), my_address);\nprintln!(\"User full name: {}\", profile.full_name);\n```\n\n### Notes\n\n* Only the user themselves can fetch their profile; there is no admin override\nin this function.\n* If the profile is not found in storage, the function will panic with\n`UserNotFound`.", + "name": "get_user_profile", + "inputs": [ + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "result", + "value": { + "type": "custom", + "name": "UserProfile" + }, + "error": { + "type": "error" + } + } + ] + }, + { + "type": "function", + "doc": "Retrieve a user profile by their address.\n\nThis function fetches a complete user profile using the user's blockchain address.\nAccess may be restricted based on the requester's permissions.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `requester` - The address of the user requesting the profile\n* `user_id` - The address of the user whose profile is being requested\n\n# Returns\n\nReturns the requested `UserProfile`.\n\n# Panics\n\n* If the user profile doesn't exist\n* If the requester doesn't have permission to view the profile\n* If the requester is not the user themselves or an admin\n\n# Examples\n\n```rust\n// Get your own profile\nlet my_profile = contract.get_user_by_id(env.clone(), my_address, my_address);\n\n// Admin getting any user's profile\nlet user_profile = contract.get_user_by_id(env.clone(), admin_address, user_address);\n```\n\n# Edge Cases\n\n* **Non-existent user**: Will panic with appropriate error message\n* **Inactive user**: Returns profile but status will be `UserStatus::Inactive`\n* **Permission denied**:", + "name": "get_user_by_id", + "inputs": [ + { + "doc": "", + "name": "requester", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "user_id", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "UserProfile" + } + ] + }, + { + "type": "function", + "doc": "Create a new user profile\n\nCreates a new user profile using a UserProfile struct.\nValidates mandatory fields (full_name and contact_email) and saves the profile.\n\n# Arguments\n* `env` - Soroban environment\n* `user` - Address of the user whose profile is being created\n* `profile` - UserProfile struct containing all profile data\n\n# Returns\n* `UserProfile` - The created user profile\n\n# Panics\n* If mandatory fields (full_name, contact_email) are missing\n* If user profile already exists\n* If email format is invalid\n* If validation rules are violated\n\n# Events\nEmits a user creation event upon successful creation\n\n# Examples\n\n```rust\nlet profile = UserProfile {\nfull_name: \"John Doe\".try_into().unwrap(),\ncontact_email: \"john@example.com\".try_into().unwrap(),\nrole: UserRole::Student,\nstatus: UserStatus::Active,\ncountry: Some(\"US\".try_into().unwrap()),\n..Default::default()\n};\n\nlet created_profile = contract.create_user_profile(env, user_address, profile);\n```\n\n# Edge Cases\n\n* **Duplicate profile**: Will panic if user al", + "name": "create_user_profile", + "inputs": [ + { + "doc": "", + "name": "user", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "profile", + "value": { + "type": "custom", + "name": "UserProfile" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "UserProfile" + } + ] + }, + { + "type": "function", + "doc": "Edit an existing user profile\n\nUpdates an existing user profile with new values for allowed fields.\nOnly the user themselves or administrators can perform updates.\nEmail and role fields cannot be updated through this function.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address of the user performing the update\n* `user_id` - Address of the user whose profile is being updated\n* `updates` - ProfileUpdateParams containing fields to update\n\n# Returns\n* `UserProfile` - The updated user profile\n\n# Panics\n* If caller authentication fails\n* If user profile doesn't exist\n* If caller lacks permission to edit\n* If any field validation fails\n* If user is inactive\n\n# Events\nEmits a user update event upon successful profile update\n\n# Examples\n\n```rust\nlet updates = ProfileUpdateParams {\nfull_name: Some(\"Jane Doe\".try_into().unwrap()),\ncountry: Some(\"CA\".try_into().unwrap()),\nbio: Some(\"Updated bio\".try_into().unwrap()),\n..Default::default()\n};\n\nlet updated_profile = contract.edit_user_profile(env, caller_addres", + "name": "edit_user_profile", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "user_id", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "updates", + "value": { + "type": "custom", + "name": "ProfileUpdateParams" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "UserProfile" + } + ] + }, + { + "type": "function", + "doc": "Check if an address has admin privileges.\n\nThis function is used by other contracts to verify admin status\nfor cross-contract authorization checks.\n\n# Arguments\n\n* `env` - The Soroban environment\n* `who` - The address to check for admin privileges\n\n# Returns\n\nReturns `true` if the address has admin privileges, `false` otherwise.\n\n# Examples\n\n```rust\n// Check if user is admin\nlet is_admin = contract.is_admin(env.clone(), user_address);\nif is_admin {\n// Perform admin operations\n}\n\n// Cross-contract admin check\nlet can_perform_action = contract.is_admin(env.clone(), caller_address);\n```\n\n# Edge Cases\n\n* **System not initialized**: Returns `false` if admin system hasn't been set up\n* **Non-existent user**: Returns `false` for addresses that don't exist\n* **Regular users**: Always returns `false` for non-admin users", + "name": "is_admin", + "inputs": [ + { + "doc": "", + "name": "who", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Delete (deactivate) a user account\n\nPerforms a soft delete by marking the user as inactive instead of permanent deletion.\nOnly admins or the user themselves can trigger deletion.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the deletion (must be admin or the user themselves)\n* `user_id` - Address of the user to be deactivated\n\n# Panics\n* If caller authentication fails\n* If user doesn't exist\n* If caller is neither admin nor the user themselves\n* If user is already inactive\n\n# Events\nEmits a user deactivation event upon successful deletion\n\n# Examples\n\n```rust\n// User deleting their own account\ncontract.delete_user(env.clone(), user_address, user_address);\n\n// Admin deleting another user's account\ncontract.delete_user(env.clone(), admin_address, user_to_delete);\n```\n\n# Edge Cases\n\n* **Already inactive**: Will panic if trying to delete an already inactive user\n* **Permission denied**: Non-admin users can only delete their own accounts\n* **Data preservation**: User data is preserved", + "name": "delete_user", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "user_id", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Lists all registered users with pagination and filtering (admin-only)\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be admin)\n* `page` - Zero-based page index\n* `page_size` - Number of items per page (must be > 0)\n* `role_filter` - Optional role filter\n* `country_filter` - Optional country filter\n* `status_filter` - Optional status filter\n\n# Returns\n* `Vec` - Filtered and paginated lightweight user profiles\n\n# Panics\n* If caller is not an admin\n* If page_size is 0 or exceeds maximum allowed\n* If system is not initialized\n\n# Examples\n\n```rust\n// Get first page with 10 users\nlet users = contract.list_all_users(\nenv.clone(),\nadmin_address,\n0, // page 0\n10, // page size\nNone, None, None // no filters\n);\n\n// Filter by role and country\nlet students = contract.list_all_users(\nenv.clone(),\nadmin_address,\n0, 20,\nSome(UserRole::Student),\nSome(\"US\".try_into().unwrap()),\nNone\n);\n```\n\n# Edge Cases\n\n* **Empty results**: Returns empty vector if no users match filter", + "name": "list_all_users", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "page", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "page_size", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "role_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserRole" + } + } + }, + { + "doc": "", + "name": "country_filter", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "status_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserStatus" + } + } + } + ], + "outputs": [ + { + "type": "vec", + "element": { + "type": "custom", + "name": "LightProfile" + } + } + ] + }, + { + "type": "function", + "doc": "Lists all registered users with advanced filtering including text search (admin-only).\n\nThis is the new version that supports text search functionality.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be admin)\n* `page` - Zero-based page index\n* `page_size` - Number of items per page\n* `role_filter` - Optional role filter\n* `country_filter` - Optional country filter\n* `status_filter` - Optional status filter\n* `search_text` - Optional text search in name and profession\n\n# Returns\n* `Vec` - Filtered and paginated lightweight user profiles", + "name": "list_all_users_advanced", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "page", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "page_size", + "value": { + "type": "u32" + } + }, + { + "doc": "", + "name": "role_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserRole" + } + } + }, + { + "doc": "", + "name": "country_filter", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + }, + { + "doc": "", + "name": "status_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserStatus" + } + } + }, + { + "doc": "", + "name": "search_text", + "value": { + "type": "option", + "value": { + "type": "string" + } + } + } + ], + "outputs": [ + { + "type": "vec", + "element": { + "type": "custom", + "name": "LightProfile" + } + } + ] + }, + { + "type": "function", + "doc": "Lists all registered users with cursor-based pagination and filtering (admin-only)\n\nThis function implements efficient cursor-based pagination to avoid gas limit issues\nwhen dealing with large datasets. It returns a PaginatedResult with metadata for\nefficient navigation.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be admin)\n* `pagination` - Pagination parameters including cursor and limit\n* `role_filter` - Optional filter for user role\n* `status_filter` - Optional filter for user status\n\n# Returns\n* `PaginatedLightProfiles` - Paginated results with navigation metadata", + "name": "list_all_users_cursor", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "pagination", + "value": { + "type": "custom", + "name": "PaginationParams" + } + }, + { + "doc": "", + "name": "role_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserRole" + } + } + }, + { + "doc": "", + "name": "status_filter", + "value": { + "type": "option", + "value": { + "type": "custom", + "name": "UserStatus" + } + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "PaginatedLightProfiles" + } + ] + }, + { + "type": "function", + "doc": "Initialize the admin system (one-time only)\n\n# Arguments\n* `env` - Soroban environment\n* `initializer` - Address performing the initialization\n* `super_admin` - Address that will become the super admin\n* `max_page_size` - Optional maximum page size (default: 100, max: 1000)\n\n# Returns\n* `AdminConfig` - The created admin configuration\n\n# Panics\n* If system has already been initialized\n* If max_page_size exceeds 1000\n\n# Examples\n\n```rust\n// Initialize with default settings\nlet config = contract.initialize_system(\nenv.clone(),\ndeployer_address,\nsuper_admin_address,\nNone\n);\n\n// Initialize with custom page size\nlet config = contract.initialize_system(\nenv.clone(),\ndeployer_address,\nsuper_admin_address,\nSome(500)\n);\n```\n\n# Edge Cases\n\n* **Double initialization**: Will panic if called more than once\n* **Invalid page size**: Will panic if max_page_size > 1000\n* **Super admin privileges**: Super admin cannot be removed after initialization", + "name": "initialize_system", + "inputs": [ + { + "doc": "", + "name": "initializer", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "super_admin", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "max_page_size", + "value": { + "type": "option", + "value": { + "type": "u32" + } + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "AdminConfig" + } + ] + }, + { + "type": "function", + "doc": "Add a new admin (super admin only)\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be super admin)\n* `new_admin` - Address to be added as admin\n\n# Panics\n* If caller is not the super admin\n* If system is not initialized\n* If new_admin is already an admin\n\n# Examples\n\n```rust\n// Super admin adding a new admin\ncontract.add_admin(env.clone(), super_admin_address, new_admin_address);\n```\n\n# Edge Cases\n\n* **Already admin**: Will panic if trying to add an existing admin\n* **Self-promotion**: Super admin cannot add themselves (redundant)\n* **Non-existent user**: Can add admin privileges to any address", + "name": "add_admin", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "new_admin", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Remove an admin (super admin only)\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be super admin)\n* `admin_to_remove` - Address to be removed from admins\n\n# Panics\n* If caller is not the super admin\n* If system is not initialized\n* If admin_to_remove is not an admin\n* If trying to remove the super admin\n\n# Examples\n\n```rust\n// Super admin removing another admin\ncontract.remove_admin(env.clone(), super_admin_address, admin_to_remove);\n```\n\n# Edge Cases\n\n* **Super admin protection**: Cannot remove the super admin\n* **Non-admin**: Will panic if trying to remove a non-admin address\n* **Self-removal**: Super admin cannot remove themselves", + "name": "remove_admin", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "admin_to_remove", + "value": { + "type": "address" + } + } + ], + "outputs": [] + }, + { + "type": "function", + "doc": "Get list of all admins (admin only)\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the call (must be admin)\n\n# Returns\n* `Vec
` - List of all admin addresses including super admin\n\n# Panics\n* If caller is not an admin\n* If system is not initialized\n\n# Examples\n\n```rust\n// Get all admin addresses\nlet admins = contract.get_admins(env.clone(), admin_address);\nfor admin in admins {\n// Process each admin address\n}\n```\n\n# Edge Cases\n\n* **Empty list**: Returns vector with only super admin if no other admins exist\n* **Admin only**: Only admins can view the admin list\n* **Order**: Super admin is typically first in the list", + "name": "get_admins", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "vec", + "element": { + "type": "address" + } + } + ] + }, + { + "type": "function", + "doc": "Check if the system is initialized\n\n# Arguments\n* `env` - Soroban environment\n\n# Returns\n* `bool` - True if system is initialized\n\n# Examples\n\n```rust\n// Check if admin system is ready\nlet is_initialized = contract.is_system_initialized(env.clone());\nif !is_initialized {\n// Initialize the system first\ncontract.initialize_system(env, deployer, super_admin, None);\n}\n```\n\n# Edge Cases\n\n* **Fresh deployment**: Returns `false` for newly deployed contracts\n* **Public access**: Anyone can check initialization status\n* **One-time check**: Once initialized, always returns `true`", + "name": "is_system_initialized", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "type": "function", + "doc": "Get the current contract version\n\nReturns the semantic version of the current contract deployment.\nThis is useful for tracking contract upgrades and compatibility.\n\n# Arguments\n* `_env` - The Soroban environment (unused)\n\n# Returns\n* `String` - The current contract version", + "name": "get_contract_version", + "inputs": [], + "outputs": [ + { + "type": "string" + } + ] + }, + { + "type": "function", + "doc": "Export all user data for backup purposes (admin only)\n\nThis function exports all user profiles and administrative data\nfor backup and recovery purposes. Only admins can perform this operation.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the export (must be admin)\n\n# Returns\n* `UserBackupData` - Complete backup data structure\n\n# Panics\n* If caller is not an admin\n* If system is not initialized", + "name": "export_user_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + } + ], + "outputs": [ + { + "type": "custom", + "name": "UserBackupData" + } + ] + }, + { + "type": "function", + "doc": "Import user data from backup (admin only)\n\nThis function imports user data from a backup structure.\nOnly admins can perform this operation. This will overwrite existing data.\n\n# Arguments\n* `env` - Soroban environment\n* `caller` - Address performing the import (must be admin)\n* `backup_data` - Backup data structure to import\n\n# Returns\n* `u32` - Number of users imported\n\n# Panics\n* If caller is not an admin\n* If backup data is invalid\n* If import operation fails", + "name": "import_user_data", + "inputs": [ + { + "doc": "", + "name": "caller", + "value": { + "type": "address" + } + }, + { + "doc": "", + "name": "backup_data", + "value": { + "type": "custom", + "name": "UserBackupData" + } + } + ], + "outputs": [ + { + "type": "u32" + } + ] + } +] diff --git a/schemas/user_management_spec.json b/schemas/user_management_spec.json new file mode 100644 index 0000000..e69de29 diff --git a/scripts/export_schemas.sh b/scripts/export_schemas.sh new file mode 100755 index 0000000..7881214 --- /dev/null +++ b/scripts/export_schemas.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e + +echo "📋 Exporting Contract Schemas" +echo "==================================" + +# Create schemas directory +SCHEMA_DIR="schemas" +mkdir -p $SCHEMA_DIR + +# Fix the WASM paths to match your build target +WASM_DIR="target/wasm32v1-none/release" + +# Function to export contract bindings +export_contract_schemas() { + local contract_name=$1 + local wasm_path=$2 + + echo "📦 Exporting schema for $contract_name..." + + # Generate TypeScript bindings + stellar contract bindings typescript \ + --wasm "$wasm_path" \ + --output-dir "$SCHEMA_DIR/$contract_name-ts" \ + --network local || { + echo "⚠️ Warning: Could not generate TypeScript bindings for $contract_name" + } + + # Generate JSON bindings + stellar contract bindings json \ + --wasm "$wasm_path" > "$SCHEMA_DIR/${contract_name}_schema.json" || { + echo "⚠️ Warning: Could not generate JSON bindings for $contract_name" + } + + # Export human-readable documentation + stellar contract inspect \ + --wasm "$wasm_path" \ + --output docs > "$SCHEMA_DIR/${contract_name}_docs.md" || { + echo "⚠️ Warning: Could not export docs for $contract_name" + } +} + +# Export schemas for each contract +export_contract_schemas "course_access" "$WASM_DIR/course_access.wasm" +export_contract_schemas "course_registry" "$WASM_DIR/course_registry.wasm" +export_contract_schemas "user_management" "$WASM_DIR/user_management.wasm" + +# Create a combined metadata file +echo "" +echo "📄 Creating combined metadata file..." +cat > "$SCHEMA_DIR/contracts_metadata.json" << EOF +{ + "contracts": { + "course_access": { + "wasm": "$WASM_DIR/course_access.wasm", + "schema": "schemas/course_access_schema.json", + "docs": "schemas/course_access_docs.md", + "typescript": "schemas/course_access-ts/" + }, + "course_registry": { + "wasm": "$WASM_DIR/course_registry.wasm", + "schema": "schemas/course_registry_schema.json", + "docs": "schemas/course_registry_docs.md", + "typescript": "schemas/course_registry-ts/" + }, + "user_management": { + "wasm": "$WASM_DIR/user_management.wasm", + "schema": "schemas/user_management_schema.json", + "docs": "schemas/user_management_docs.md", + "typescript": "schemas/user_management-ts/" + } + }, + "generated_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" +} +EOF + +echo "✅ Schemas exported to $SCHEMA_DIR/" +echo "" +echo "📝 Generated files:" +echo " • TypeScript bindings in schemas/*/ts/" +echo " • JSON schemas in schemas/*_schema.json" +echo " • Documentation in schemas/*_docs.md" +echo " • Metadata in schemas/contracts_metadata.json" \ No newline at end of file