diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 46039fdc..2fab32c0 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -5,10 +5,12 @@ on: branches: [ "main", "master" ] paths: - 'apps/backend/**' + - 'apps/contract-bindings/**' pull_request: branches: [ "main", "master" ] paths: - 'apps/backend/**' + - 'apps/contract-bindings/**' jobs: build: @@ -27,6 +29,12 @@ jobs: cache: 'npm' cache-dependency-path: apps/backend/package-lock.json + - name: Build contract bindings package + run: | + cd ../contract-bindings + npm ci + npm run build + - name: Install dependencies run: npm ci diff --git a/.github/workflows/contract-ci.yml b/.github/workflows/contract-ci.yml index dba86c00..57460f72 100644 --- a/.github/workflows/contract-ci.yml +++ b/.github/workflows/contract-ci.yml @@ -41,3 +41,52 @@ jobs: - name: Run tests run: cargo test + + bindings: + name: Check Bindings + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: apps/contract-bindings/package-lock.json + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: wasm32-unknown-unknown + + - name: Install Stellar CLI + uses: stellar/stellar-cli@v26.0.0 + + - name: Build Contract + run: | + cd apps/onchain + cargo build --target wasm32-unknown-unknown --release + + - name: Generate Bindings + run: | + stellar contract bindings typescript --wasm apps/onchain/target/wasm32-unknown-unknown/release/onchain.wasm --output-dir apps/contract-bindings --overwrite + + - name: Build Bindings Package + run: | + cd apps/contract-bindings + npm ci + npm run build + + - name: Check for drift + run: | + if [ -n "$(git status --porcelain apps/contract-bindings)" ]; then + echo "Error: Contract bindings are out of sync with contract source." + echo "Please run 'npm run generate-bindings' and commit the changes." + git status apps/contract-bindings + git diff apps/contract-bindings + exit 1 + fi diff --git a/apps/backend/eslint.config.mjs b/apps/backend/eslint.config.mjs index d207409b..ade46a2f 100644 --- a/apps/backend/eslint.config.mjs +++ b/apps/backend/eslint.config.mjs @@ -116,9 +116,13 @@ export default tseslint.config( }, }, { - files: ['src/services/stellar/escrow-operations.ts'], + files: ['src/services/stellar/escrow-operations.ts', 'src/services/stellar/soroban-client.service.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-return': 'off', }, }, { diff --git a/apps/backend/package-lock.json b/apps/backend/package-lock.json index da435666..6b17877f 100644 --- a/apps/backend/package-lock.json +++ b/apps/backend/package-lock.json @@ -27,6 +27,7 @@ "bcrypt": "^6.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.3", + "contract-bindings": "file:../contract-bindings", "dotenv": "^17.3.1", "nodemailer": "^8.0.3", "passport": "^0.7.0", @@ -65,6 +66,16 @@ "typescript-eslint": "^8.20.0" } }, + "../contract-bindings": { + "version": "0.0.0", + "dependencies": { + "@stellar/stellar-sdk": "^14.5.0", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } + }, "node_modules/@angular-devkit/core": { "version": "19.2.19", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", @@ -240,7 +251,6 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -2159,7 +2169,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.14.tgz", "integrity": "sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==", "license": "MIT", - "peer": true, "dependencies": { "file-type": "21.3.0", "iterare": "1.2.1", @@ -2219,7 +2228,6 @@ "integrity": "sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==", "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -2303,7 +2311,6 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.12.tgz", "integrity": "sha512-GYK/vHI0SGz5m8mxr7v3Urx8b9t78Cf/dj5aJMZlGd9/1D9OI1hAl00BaphjEXINUJ/BQLxIlF2zUjrYsd6enQ==", "license": "MIT", - "peer": true, "dependencies": { "cors": "2.8.5", "express": "5.2.1", @@ -2952,7 +2959,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3084,7 +3090,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3258,7 +3263,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -3947,7 +3951,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4037,7 +4040,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4582,7 +4584,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4902,7 +4903,6 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -4959,15 +4959,13 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/class-validator": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", "license": "MIT", - "peer": true, "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", @@ -5230,6 +5228,10 @@ "node": ">= 0.6" } }, + "node_modules/contract-bindings": { + "resolved": "../contract-bindings", + "link": true + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5788,7 +5790,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5849,7 +5850,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6100,7 +6100,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -7372,7 +7371,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -9458,7 +9456,6 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", "license": "MIT", - "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -9731,7 +9728,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9971,8 +9967,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/require-addon": { "version": "1.2.0", @@ -10130,7 +10125,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -11063,7 +11057,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -11411,7 +11404,6 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -11590,7 +11582,6 @@ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", "license": "MIT", - "peer": true, "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^4.2.0", @@ -11796,7 +11787,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12102,7 +12092,6 @@ "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -12172,7 +12161,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", diff --git a/apps/backend/package.json b/apps/backend/package.json index c5a499c2..884176be 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -44,6 +44,7 @@ "bcrypt": "^6.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.3", + "contract-bindings": "file:../contract-bindings", "dotenv": "^17.3.1", "nodemailer": "^8.0.3", "passport": "^0.7.0", diff --git a/apps/backend/src/modules/admin/services/consistency-checker.service.ts b/apps/backend/src/modules/admin/services/consistency-checker.service.ts index 5a403a84..3935838f 100644 --- a/apps/backend/src/modules/admin/services/consistency-checker.service.ts +++ b/apps/backend/src/modules/admin/services/consistency-checker.service.ts @@ -180,7 +180,8 @@ export class ConsistencyCheckerService { Completed: 'completed', Cancelled: 'cancelled', Disputed: 'disputed', - ArbiterResolved: 'completed', + Resolved: 'completed', + Expired: 'cancelled', }; return statusMap[contractStatus] || contractStatus.toLowerCase(); } diff --git a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts b/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts index 1a425083..2cb94bda 100644 --- a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts +++ b/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts @@ -76,7 +76,7 @@ export class EscrowStellarIntegrationService { // Create operations for escrow initialization const operations = - this.escrowOperationsService.createEscrowInitializationOps( + await this.escrowOperationsService.createEscrowInitializationOps( escrowId, depositor.user.walletAddress, // User's Stellar wallet address recipient.user.walletAddress, // User's Stellar wallet address @@ -136,7 +136,7 @@ export class EscrowStellarIntegrationService { // Create funding operations const operations = - this.escrowOperationsService.createFundingOps(escrowId); + await this.escrowOperationsService.createFundingOps(escrowId); // Build the transaction const transaction = await this.stellarService.buildTransaction( @@ -184,10 +184,11 @@ export class EscrowStellarIntegrationService { ); // Create milestone release operations - const operations = this.escrowOperationsService.createMilestoneReleaseOps( - escrowId, - milestoneId, - ); + const operations = + await this.escrowOperationsService.createMilestoneReleaseOps( + escrowId, + milestoneId, + ); // Build the transaction const transaction = await this.stellarService.buildTransaction( @@ -229,11 +230,12 @@ export class EscrowStellarIntegrationService { ); // Create confirmation operations - const operations = this.escrowOperationsService.createConfirmationOps( - escrowId, - confirmerPublicKey, - milestoneId, - ); + const operations = + await this.escrowOperationsService.createConfirmationOps( + escrowId, + confirmerPublicKey, + milestoneId, + ); // Build the transaction const transaction = await this.stellarService.buildTransaction( @@ -274,7 +276,8 @@ export class EscrowStellarIntegrationService { this.logger.log(`Canceling on-chain escrow ${escrowId}`); // Create cancel operations - const operations = this.escrowOperationsService.createCancelOps(escrowId); + const operations = + await this.escrowOperationsService.createCancelOps(escrowId); // Build the transaction const transaction = await this.stellarService.buildTransaction( @@ -313,7 +316,7 @@ export class EscrowStellarIntegrationService { // Create completion operations const operations = - this.escrowOperationsService.createCompletionOps(escrowId); + await this.escrowOperationsService.createCompletionOps(escrowId); // Build the transaction const transaction = await this.stellarService.buildTransaction( diff --git a/apps/backend/src/services/stellar/escrow-operations.ts b/apps/backend/src/services/stellar/escrow-operations.ts index 280ad33a..6b6b663f 100644 --- a/apps/backend/src/services/stellar/escrow-operations.ts +++ b/apps/backend/src/services/stellar/escrow-operations.ts @@ -1,77 +1,67 @@ import * as StellarSdk from '@stellar/stellar-sdk'; import { Injectable, Logger } from '@nestjs/common'; +import { Client as ContractClient, MilestoneStatus } from 'contract-bindings'; @Injectable() export class EscrowOperationsService { private readonly logger = new Logger(EscrowOperationsService.name); - private readonly contractId: string; + private readonly networkPassphrase: string; + private readonly rpcUrl: string; + private client: ContractClient; constructor() { this.contractId = process.env.STELLAR_CONTRACT_ID || ''; + this.networkPassphrase = + process.env.STELLAR_NETWORK_PASSPHRASE || + 'Test SDF Network ; September 2015'; + this.rpcUrl = + process.env.STELLAR_RPC_URL || 'https://soroban-testnet.stellar.org'; + + this.client = new ContractClient({ + rpcUrl: this.rpcUrl, + networkPassphrase: this.networkPassphrase, + contractId: this.contractId, + allowHttp: true, + }); } /** * Creates operations for initializing an escrow contract */ - createEscrowInitializationOps( + async createEscrowInitializationOps( escrowId: string, depositorPublicKey: string, recipientPublicKey: string, tokenAddress: string, milestones: Array<{ id: number; amount: string; description: string }>, deadline: number, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log( `Creating escrow initialization ops for escrow ID: ${escrowId}`, ); - const contract = new StellarSdk.Contract(this.contractId); - - const milestoneVec = StellarSdk.xdr.ScVal.scvVec( - milestones.map((m) => - StellarSdk.xdr.ScVal.scvMap([ - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('amount'), - val: StellarSdk.xdr.ScVal.scvI128( - new StellarSdk.xdr.Int128Parts({ - lo: new StellarSdk.xdr.Uint64(m.amount), - hi: new StellarSdk.xdr.Int64('0'), - }), - ), - }), - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('description'), - val: StellarSdk.xdr.ScVal.scvSymbol( - m.description.replace(/\s+/g, '_'), - ), - }), - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('status'), - val: StellarSdk.xdr.ScVal.scvSymbol('Pending'), - }), - ]), - ), - ); + const milestoneList = milestones.map((m) => ({ + amount: BigInt(m.amount), + description: m.description.replace(/\s+/g, '_'), + status: { tag: 'Pending', values: undefined } as MilestoneStatus, + })); - const op = contract.call( - 'create_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(depositorPublicKey).toScVal(), - new StellarSdk.Address(recipientPublicKey).toScVal(), - new StellarSdk.Address( + const tx = await this.client.create_escrow({ + escrow_id: BigInt(escrowId), + depositor: depositorPublicKey, + recipient: recipientPublicKey, + token_address: tokenAddress === 'native' - ? 'CDLZFC3SYJYDZT7K67VZ75YJFCGSN5W4B77T2YI2EHCWH6I6D6LNCU6B' /* Native XLM Token Contract in Testnet */ + ? 'CDLZFC3SYJYDZT7K67VZ75YJFCGSN5W4B77T2YI2EHCWH6I6D6LNCU6B' : tokenAddress, - ).toScVal(), - milestoneVec, - StellarSdk.xdr.ScVal.scvU64( - new StellarSdk.xdr.Uint64(deadline.toString()), - ), - ); + milestones: milestoneList, + deadline: BigInt(deadline), + metadata_hash: Buffer.alloc(32), // Placeholder if not provided + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create escrow initialization ops: ${this.getErrorMessage(error)}`, @@ -83,21 +73,17 @@ export class EscrowOperationsService { /** * Creates operations for funding an escrow */ - createFundingOps( + async createFundingOps( escrowId: string, - // amount: string, // Not used in contract call directly as it's part of escrow creation - // asset: StellarSdk.Asset, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log(`Creating funding ops for escrow ID: ${escrowId}`); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'deposit_funds', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); + const tx = await this.client.deposit_funds({ + escrow_id: BigInt(escrowId), + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create funding ops: ${this.getErrorMessage(error)}`, @@ -109,27 +95,21 @@ export class EscrowOperationsService { /** * Creates operations for releasing a milestone payment */ - createMilestoneReleaseOps( + async createMilestoneReleaseOps( escrowId: string, milestoneId: number, - // releaserPublicKey: string, - // recipientPublicKey: string, - // amount: string, - // asset: StellarSdk.Asset, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log( `Creating milestone release ops for escrow ID: ${escrowId}, milestone: ${milestoneId}`, ); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'release_milestone', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - StellarSdk.xdr.ScVal.scvU32(milestoneId), - ); + const tx = await this.client.release_milestone({ + escrow_id: BigInt(escrowId), + milestone_index: milestoneId, + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create milestone release ops: ${this.getErrorMessage(error)}`, @@ -141,25 +121,23 @@ export class EscrowOperationsService { /** * Creates operations for confirming delivery/acceptance */ - createConfirmationOps( + async createConfirmationOps( escrowId: string, confirmerPublicKey: string, milestoneId: number, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log( `Creating confirmation ops for escrow ID: ${escrowId}, milestone: ${milestoneId}`, ); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'confirm_delivery', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - StellarSdk.xdr.ScVal.scvU32(milestoneId), - new StellarSdk.Address(confirmerPublicKey).toScVal(), - ); + const tx = await this.client.confirm_delivery({ + escrow_id: BigInt(escrowId), + milestone_index: milestoneId, + buyer: confirmerPublicKey, + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create confirmation ops: ${this.getErrorMessage(error)}`, @@ -171,20 +149,15 @@ export class EscrowOperationsService { /** * Creates operations for canceling an escrow */ - createCancelOps( - escrowId: string, - // cancellerPublicKey: string, - ): StellarSdk.xdr.Operation[] { + async createCancelOps(escrowId: string): Promise { try { this.logger.log(`Creating cancel ops for escrow ID: ${escrowId}`); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'cancel_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); + const tx = await this.client.cancel_escrow({ + escrow_id: BigInt(escrowId), + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create cancel ops: ${this.getErrorMessage(error)}`, @@ -196,20 +169,17 @@ export class EscrowOperationsService { /** * Creates operations for completing an escrow */ - createCompletionOps( + async createCompletionOps( escrowId: string, - // completerPublicKey: string, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log(`Creating completion ops for escrow ID: ${escrowId}`); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'complete_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); + const tx = await this.client.complete_escrow({ + escrow_id: BigInt(escrowId), + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create completion ops: ${this.getErrorMessage(error)}`, @@ -221,21 +191,19 @@ export class EscrowOperationsService { /** * Creates operations for raising a dispute */ - createDisputeOps( + async createDisputeOps( escrowId: string, callerPublicKey: string, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log(`Creating dispute ops for escrow ID: ${escrowId}`); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'raise_dispute', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(callerPublicKey).toScVal(), - ); + const tx = await this.client.raise_dispute({ + escrow_id: BigInt(escrowId), + caller: callerPublicKey, + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create dispute ops: ${this.getErrorMessage(error)}`, @@ -247,34 +215,25 @@ export class EscrowOperationsService { /** * Creates operations for resolving a dispute */ - createResolveDisputeOps( + async createResolveDisputeOps( escrowId: string, winnerPublicKey: string, splitWinnerAmount?: string, - ): StellarSdk.xdr.Operation[] { + ): Promise { try { this.logger.log( `Creating resolve dispute ops for escrow ID: ${escrowId}`, ); - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'resolve_dispute', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(winnerPublicKey).toScVal(), - splitWinnerAmount - ? StellarSdk.xdr.ScVal.scvVec([ - StellarSdk.xdr.ScVal.scvI128( - new StellarSdk.xdr.Int128Parts({ - lo: new StellarSdk.xdr.Uint64(splitWinnerAmount), - hi: new StellarSdk.xdr.Int64('0'), - }), - ), - ]) - : StellarSdk.xdr.ScVal.scvVec([]), // Option::None - ); + const tx = await this.client.resolve_dispute({ + escrow_id: BigInt(escrowId), + winner: winnerPublicKey, + split_winner_amount: splitWinnerAmount + ? BigInt(splitWinnerAmount) + : undefined, + }); - return [op]; + return [tx.asOperation()]; } catch (error) { this.logger.error( `Failed to create resolve dispute ops: ${this.getErrorMessage(error)}`, @@ -291,8 +250,7 @@ export class EscrowOperationsService { return error.message; } if (typeof error === 'object' && error !== null && 'message' in error) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return String((error as any).message); + return String((error as Record).message); } return 'Unknown error'; } diff --git a/apps/backend/src/services/stellar/soroban-client.service.ts b/apps/backend/src/services/stellar/soroban-client.service.ts index b20525d5..4115f707 100644 --- a/apps/backend/src/services/stellar/soroban-client.service.ts +++ b/apps/backend/src/services/stellar/soroban-client.service.ts @@ -1,13 +1,26 @@ import { Injectable, Logger, Inject } from '@nestjs/common'; import { ConfigType } from '@nestjs/config'; import * as StellarSdk from '@stellar/stellar-sdk'; +import { + Client as ContractClient, + Escrow as OnchainEscrowRaw, + Errors as ContractErrors, +} from 'contract-bindings'; import stellarConfig from '../../config/stellar.config'; +export interface OnchainMilestone { + amount: string; + description: string; + status: string; +} + export interface OnchainEscrow { status: string; amount: string; depositor: string; recipient: string; + milestones?: OnchainMilestone[]; + totalReleased?: string; } @Injectable() @@ -16,6 +29,7 @@ export class SorobanClientService { private rpcServer: StellarSdk.rpc.Server; private networkPassphrase: string; private contractId: string; + private client: ContractClient; constructor( @Inject(stellarConfig.KEY) @@ -27,6 +41,13 @@ export class SorobanClientService { this.networkPassphrase = this.config.networkPassphrase; this.contractId = process.env.STELLAR_CONTRACT_ID || ''; + this.client = new ContractClient({ + rpcUrl, + networkPassphrase: this.networkPassphrase, + contractId: this.contractId, + allowHttp: true, + }); + this.logger.log(`Initialized Soroban client with RPC: ${rpcUrl}`); } @@ -37,95 +58,37 @@ export class SorobanClientService { try { this.logger.debug(`Fetching escrow ${escrowId} from contract`); - const contract = new StellarSdk.Contract(this.contractId); - - // Get storage key for escrow: (Symbol("escrow"), u64) - const key = StellarSdk.xdr.ScVal.scvVec([ - StellarSdk.xdr.ScVal.scvSymbol('escrow'), - StellarSdk.xdr.ScVal.scvU64( - new StellarSdk.xdr.Uint64(escrowId.toString()), - ), - ]); - - const ledgerKey = StellarSdk.xdr.LedgerKey.contractData( - new StellarSdk.xdr.LedgerKeyContractData({ - contract: contract.address().toScAddress(), - key, - durability: StellarSdk.xdr.ContractDataDurability.persistent(), - }), - ); + const tx = await this.client.get_escrow({ escrow_id: BigInt(escrowId) }); + const result = await tx.simulate(); - const response = await this.rpcServer.getLedgerEntries(ledgerKey); - - if (!response.entries || response.entries.length === 0) { + if (!result || !result.result || !result.result.unwrap) { + this.logger.warn(`Escrow ${escrowId} not found or simulation failed`); return null; } - const entry = response.entries[0]; - const scVal = StellarSdk.xdr.ScVal.fromXDR(entry.val.toXDR()); - - // Basic decoding (simplified for this task) - // In a real app, use a proper ScVal decoder or the contract-client - return this.decodeEscrowScVal(scVal); - } catch (error: unknown) { - if (error instanceof Error) { - this.logger.error(`Error fetching escrow: ${error.message}`); - } - return null; - } - } - - /** - * Decodes an ScVal representing the Escrow struct - */ - private decodeEscrowScVal(scVal: StellarSdk.xdr.ScVal): OnchainEscrow | null { - if (scVal.switch() !== StellarSdk.xdr.ScValType.scvMap()) { - return null; - } - - const map = scVal.map(); - if (!map) { + const rawEscrow = result.result.unwrap() as OnchainEscrowRaw; + return this.mapOnchainEscrow(rawEscrow); + } catch (error) { + this.logger.error( + `Error fetching escrow ${escrowId}: ${(error as Error).message}`, + ); return null; } - - const result: Partial = {}; - - map.forEach((entry) => { - const key = entry.key().sym().toString(); - const val = entry.val(); - - // Simplified decoding for status and amount - if (key === 'status') { - result.status = this.decodeStatus(val); - } else if (key === 'total_amount') { - result.amount = val.i128().lo().toString(); // Simplified i128 handle - } else if (key === 'depositor') { - result.depositor = StellarSdk.Address.fromScAddress( - val.address(), - ).toString(); - } else if (key === 'recipient') { - result.recipient = StellarSdk.Address.fromScAddress( - val.address(), - ).toString(); - } - }); - - return result as OnchainEscrow; } - private decodeStatus(val: StellarSdk.xdr.ScVal): string { - // EscrowStatus enum decoding - if (val.switch() === StellarSdk.xdr.ScValType.scvVec()) { - const vec = val.vec(); - if (vec && vec.length > 0) { - return vec[0].sym().toString(); - } - } - // If it's just a symbol (some SDK versions/encodings) - if (val.switch() === StellarSdk.xdr.ScValType.scvSymbol()) { - return val.sym().toString(); - } - return 'Unknown'; + private mapOnchainEscrow(raw: OnchainEscrowRaw): OnchainEscrow { + return { + status: raw.status.tag, + amount: raw.total_amount.toString(), + depositor: raw.depositor, + recipient: raw.recipient, + milestones: raw.milestones.map((m) => ({ + amount: m.amount.toString(), + description: m.description, + status: m.status.tag, + })), + totalReleased: raw.total_released.toString(), + }; } getContractId(): string { @@ -140,27 +103,8 @@ export class SorobanClientService { * Decodes contract-specific error codes from Soroban XDR */ decodeContractError(errorCode: number): string { - const errorMap: Record = { - 1: 'EscrowNotFound', - 2: 'EscrowAlreadyExists', - 3: 'AlreadyFunded', - 4: 'NotFunded', - 5: 'InvalidMilestone', - 6: 'MilestoneAlreadyReleased', - 7: 'MilestoneNotReady', - 8: 'NotDepositor', - 9: 'NotArbiter', - 10: 'UnauthorizedAccess', - 11: 'InsufficientFunds', - 12: 'DeadlinePassed', - 13: 'DeadlineNotPassed', - 14: 'EscrowCompleted', - 15: 'EscrowCancelled', - 16: 'MilestoneNotReleased', - 17: 'EscrowNotActive', - 18: 'InvalidAmount', - }; - - return errorMap[errorCode] || `UnknownError(${errorCode})`; + const errors = ContractErrors as Record; + const errorEntry = errors[errorCode]; + return errorEntry ? errorEntry.message : `UnknownError(${errorCode})`; } } diff --git a/apps/contract-bindings/.gitignore b/apps/contract-bindings/.gitignore new file mode 100644 index 00000000..3191ead1 --- /dev/null +++ b/apps/contract-bindings/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +out/ +dist/ diff --git a/apps/contract-bindings/README.md b/apps/contract-bindings/README.md new file mode 100644 index 00000000..3ec7d90e --- /dev/null +++ b/apps/contract-bindings/README.md @@ -0,0 +1,54 @@ +# contract-bindings JS + +JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `contract-bindings` 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/contract-bindings +``` + +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": { + "contract-bindings": "./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 contract-bindings" +} +``` + +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 "contract-bindings" + +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/apps/contract-bindings/package-lock.json b/apps/contract-bindings/package-lock.json new file mode 100644 index 00000000..427f5c0e --- /dev/null +++ b/apps/contract-bindings/package-lock.json @@ -0,0 +1,777 @@ +{ + "name": "contract-bindings", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "contract-bindings", + "version": "0.0.0", + "dependencies": { + "@stellar/stellar-sdk": "^14.5.0", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@stellar/js-xdr": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", + "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", + "license": "Apache-2.0" + }, + "node_modules/@stellar/stellar-base": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.1.0.tgz", + "integrity": "sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.9.6", + "@stellar/js-xdr": "^3.1.2", + "base32.js": "^0.1.0", + "bignumber.js": "^9.3.1", + "buffer": "^6.0.3", + "sha.js": "^2.4.12" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@stellar/stellar-sdk": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-14.6.1.tgz", + "integrity": "sha512-A1rQWDLdUasXkMXnYSuhgep+3ZZzyuXJKdt5/KAIc0gkmSp906HTvUpbT4pu+bVr41tu0+J4Ugz9J4BQAGGytg==", + "license": "Apache-2.0", + "dependencies": { + "@stellar/stellar-base": "^14.1.0", + "axios": "^1.13.3", + "bignumber.js": "^9.3.1", + "commander": "^14.0.2", + "eventsource": "^2.0.2", + "feaxios": "^0.0.23", + "randombytes": "^2.1.0", + "toml": "^3.0.0", + "urijs": "^1.19.1" + }, + "bin": { + "stellar-js": "bin/stellar-js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/base32.js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/feaxios": { + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.23.tgz", + "integrity": "sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==", + "license": "MIT", + "dependencies": { + "is-retry-allowed": "^3.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-retry-allowed": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", + "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "license": "MIT" + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "license": "MIT" + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + } + } +} diff --git a/apps/contract-bindings/package.json b/apps/contract-bindings/package.json new file mode 100644 index 00000000..e1bd15a5 --- /dev/null +++ b/apps/contract-bindings/package.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.0", + "name": "contract-bindings", + "type": "module", + "exports": "./dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@stellar/stellar-sdk": "^14.5.0", + "buffer": "6.0.3" + }, + "devDependencies": { + "typescript": "^5.6.2" + } +} diff --git a/apps/contract-bindings/src/index.ts b/apps/contract-bindings/src/index.ts new file mode 100644 index 00000000..fb5f3cda --- /dev/null +++ b/apps/contract-bindings/src/index.ts @@ -0,0 +1,339 @@ +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, + Timepoint, + 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"; +import { xdr } from "@stellar/stellar-sdk"; + +declare module "@stellar/stellar-sdk/contract" { + interface AssembledTransaction { + asOperation(): xdr.Operation; + } +} + +(AssembledTransaction.prototype as any).asOperation = function() { + if (!this.built) { + throw new Error("Transaction must be built before calling asOperation()"); + } + return this.built.operations[0]; +}; + +if (typeof window !== "undefined") { + //@ts-ignore Buffer exists + window.Buffer = window.Buffer || Buffer; +} + + + + +export const Errors = { + 1: {message:"EscrowNotFound"}, + 2: {message:"EscrowAlreadyExists"}, + 3: {message:"MilestoneNotFound"}, + 4: {message:"MilestoneAlreadyReleased"}, + 5: {message:"UnauthorizedAccess"}, + 6: {message:"InvalidMilestoneAmount"}, + 7: {message:"TotalAmountMismatch"}, + 8: {message:"InsufficientBalance"}, + 9: {message:"EscrowNotActive"}, + 10: {message:"VectorTooLarge"}, + 11: {message:"ZeroAmount"}, + 12: {message:"InvalidDeadline"}, + 13: {message:"SelfDealing"}, + 14: {message:"EscrowAlreadyFunded"}, + 15: {message:"TokenTransferFailed"}, + 16: {message:"TreasuryNotInitialized"}, + 17: {message:"InvalidFeeConfiguration"}, + 18: {message:"AdminNotInitialized"}, + 19: {message:"AlreadyInitialized"}, + 20: {message:"InvalidEscrowStatus"}, + 21: {message:"AlreadyInDispute"}, + 22: {message:"InvalidWinner"}, + 23: {message:"ContractPaused"}, + 24: {message:"DeadlineNotReached"}, + 25: {message:"InvalidStatusForRefund"}, + 26: {message:"NoFundsToRefund"}, + 27: {message:"Unauthorized"}, + 28: {message:"OperatorNotInitialized"}, + 29: {message:"ArbitratorNotInitialized"} +} + + +export interface Escrow { + collected_signatures: Array; + deadline: u64; + depositor: string; + metadata_hash: Buffer; + milestones: Array; + recipient: string; + required_signatures: u32; + resolution: Resolution; + status: EscrowStatus; + threshold_amount: i128; + token_address: string; + total_amount: i128; + total_released: i128; +} + + +export interface Milestone { + amount: i128; + description: string; + status: MilestoneStatus; +} + +export type Resolution = {tag: "None", values: void} | {tag: "Depositor", values: void} | {tag: "Recipient", values: void} | {tag: "Split", values: void}; + +export type EscrowStatus = {tag: "Created", values: void} | {tag: "Active", values: void} | {tag: "Completed", values: void} | {tag: "Cancelled", values: void} | {tag: "Disputed", values: void} | {tag: "Resolved", values: void} | {tag: "Expired", values: void}; + +export type ContractState = {tag: "Active", values: void} | {tag: "Paused", values: void}; + +export type MilestoneStatus = {tag: "Pending", values: void} | {tag: "Released", values: void} | {tag: "Disputed", values: void}; + + +export interface CreateEscrowRequest { + deadline: u64; + depositor: string; + escrow_id: u64; + metadata_hash: Buffer; + milestones: Array; + recipient: string; + token_address: string; +} + + +export interface EscrowCreatedBatchItem { + deadline: u64; + depositor: string; + escrow_id: u64; + recipient: string; + token_address: string; + total_amount: i128; +} + +export interface Client { + /** + * Construct and simulate a init 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. + */ + init: ({admin, operator, arbitrator}: {admin: string, operator: string, arbitrator: string}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a get_state 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_state: ({escrow_id}: {escrow_id: u64}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a get_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. + */ + get_config: (options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a get_escrow 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_escrow: ({escrow_id}: {escrow_id: u64}, options?: MethodOptions) => Promise>> + + /** + * 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. + */ + initialize: ({treasury, fee_bps}: {treasury: string, fee_bps: Option}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a set_paused 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. + */ + set_paused: ({paused}: {paused: boolean}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a update_fee 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. + */ + update_fee: ({new_fee_bps}: {new_fee_bps: i128}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a cancel_escrow 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. + */ + cancel_escrow: ({escrow_id}: {escrow_id: u64}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a create_escrow 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_escrow: ({escrow_id, depositor, recipient, token_address, milestones, deadline, metadata_hash}: {escrow_id: u64, depositor: string, recipient: string, token_address: string, milestones: Array, deadline: u64, metadata_hash: Buffer}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a deposit_funds 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. + */ + deposit_funds: ({escrow_id}: {escrow_id: u64}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a raise_dispute 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. + */ + raise_dispute: ({escrow_id, caller}: {escrow_id: u64, caller: string}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a set_token_fee 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. + * Set fee override for a specific token. + * Only treasury (admin) can call this function. + * + * # Arguments + * * `env` - Soroban environment reference + * * `token_address` - Address of the token to set fee for + * * `fee_bps` - Fee in basis points (must be in range [0, BPS_DENOMINATOR]) + * + * # Returns + * Ok(()) on success, or Error if validation fails + */ + set_token_fee: ({token_address, fee_bps}: {token_address: string, fee_bps: i128}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a refund_expired 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. + */ + refund_expired: ({escrow_id, caller}: {escrow_id: u64, caller: string}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a set_escrow_fee 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. + * Set fee override for a specific escrow. + * Only treasury (admin) can call this function. + * + * # Arguments + * * `env` - Soroban environment reference + * * `escrow_id` - ID of the escrow to set fee for + * * `fee_bps` - Fee in basis points (must be in range [0, BPS_DENOMINATOR]) + * + * # Returns + * Ok(()) on success, or Error if validation fails + */ + set_escrow_fee: ({escrow_id, fee_bps}: {escrow_id: u64, fee_bps: i128}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a complete_escrow 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. + */ + complete_escrow: ({escrow_id}: {escrow_id: u64}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a resolve_dispute 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. + */ + resolve_dispute: ({escrow_id, winner, split_winner_amount}: {escrow_id: u64, winner: string, split_winner_amount: Option}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a confirm_delivery 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. + */ + confirm_delivery: ({escrow_id, milestone_index, buyer}: {escrow_id: u64, milestone_index: u32, buyer: string}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a collect_signature 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. + * Collect a signature for releasing funds + * The signature can come from either the depositor or a designated third party + */ + collect_signature: ({escrow_id, signer}: {escrow_id: u64, signer: string}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a release_milestone 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. + */ + release_milestone: ({escrow_id, milestone_index}: {escrow_id: u64, milestone_index: u32}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a configure_multisig 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 the threshold amount and required signatures for an escrow + * Only the depositor can call this function + */ + configure_multisig: ({escrow_id, threshold_amount, required_signatures}: {escrow_id: u64, threshold_amount: i128, required_signatures: u32}, options?: MethodOptions) => Promise>> + + /** + * Construct and simulate a create_escrows_batch 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_escrows_batch: ({requests}: {requests: Array}, options?: MethodOptions) => 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([ "AAAAAAAAAAAAAAAEaW5pdAAAAAMAAAAAAAAABWFkbWluAAAAAAAAEwAAAAAAAAAIb3BlcmF0b3IAAAATAAAAAAAAAAphcmJpdHJhdG9yAAAAAAATAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAAHQAAAAAAAAAORXNjcm93Tm90Rm91bmQAAAAAAAEAAAAAAAAAE0VzY3Jvd0FscmVhZHlFeGlzdHMAAAAAAgAAAAAAAAARTWlsZXN0b25lTm90Rm91bmQAAAAAAAADAAAAAAAAABhNaWxlc3RvbmVBbHJlYWR5UmVsZWFzZWQAAAAEAAAAAAAAABJVbmF1dGhvcml6ZWRBY2Nlc3MAAAAAAAUAAAAAAAAAFkludmFsaWRNaWxlc3RvbmVBbW91bnQAAAAAAAYAAAAAAAAAE1RvdGFsQW1vdW50TWlzbWF0Y2gAAAAABwAAAAAAAAATSW5zdWZmaWNpZW50QmFsYW5jZQAAAAAIAAAAAAAAAA9Fc2Nyb3dOb3RBY3RpdmUAAAAACQAAAAAAAAAOVmVjdG9yVG9vTGFyZ2UAAAAAAAoAAAAAAAAAClplcm9BbW91bnQAAAAAAAsAAAAAAAAAD0ludmFsaWREZWFkbGluZQAAAAAMAAAAAAAAAAtTZWxmRGVhbGluZwAAAAANAAAAAAAAABNFc2Nyb3dBbHJlYWR5RnVuZGVkAAAAAA4AAAAAAAAAE1Rva2VuVHJhbnNmZXJGYWlsZWQAAAAADwAAAAAAAAAWVHJlYXN1cnlOb3RJbml0aWFsaXplZAAAAAAAEAAAAAAAAAAXSW52YWxpZEZlZUNvbmZpZ3VyYXRpb24AAAAAEQAAAAAAAAATQWRtaW5Ob3RJbml0aWFsaXplZAAAAAASAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAABMAAAAAAAAAE0ludmFsaWRFc2Nyb3dTdGF0dXMAAAAAFAAAAAAAAAAQQWxyZWFkeUluRGlzcHV0ZQAAABUAAAAAAAAADUludmFsaWRXaW5uZXIAAAAAAAAWAAAAAAAAAA5Db250cmFjdFBhdXNlZAAAAAAAFwAAAAAAAAASRGVhZGxpbmVOb3RSZWFjaGVkAAAAAAAYAAAAAAAAABZJbnZhbGlkU3RhdHVzRm9yUmVmdW5kAAAAAAAZAAAAAAAAAA9Ob0Z1bmRzVG9SZWZ1bmQAAAAAGgAAAAAAAAAMVW5hdXRob3JpemVkAAAAGwAAAAAAAAAWT3BlcmF0b3JOb3RJbml0aWFsaXplZAAAAAAAHAAAAAAAAAAYQXJiaXRyYXRvck5vdEluaXRpYWxpemVkAAAAHQ==", + "AAAAAQAAAAAAAAAAAAAABkVzY3JvdwAAAAAADQAAAAAAAAAUY29sbGVjdGVkX3NpZ25hdHVyZXMAAAPqAAAAEwAAAAAAAAAIZGVhZGxpbmUAAAAGAAAAAAAAAAlkZXBvc2l0b3IAAAAAAAATAAAAAAAAAA1tZXRhZGF0YV9oYXNoAAAAAAAD7gAAACAAAAAAAAAACm1pbGVzdG9uZXMAAAAAA+oAAAfQAAAACU1pbGVzdG9uZQAAAAAAAAAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAAAAAAATcmVxdWlyZWRfc2lnbmF0dXJlcwAAAAAEAAAAAAAAAApyZXNvbHV0aW9uAAAAAAfQAAAAClJlc29sdXRpb24AAAAAAAAAAAAGc3RhdHVzAAAAAAfQAAAADEVzY3Jvd1N0YXR1cwAAAAAAAAAQdGhyZXNob2xkX2Ftb3VudAAAAAsAAAAAAAAADXRva2VuX2FkZHJlc3MAAAAAAAATAAAAAAAAAAx0b3RhbF9hbW91bnQAAAALAAAAAAAAAA50b3RhbF9yZWxlYXNlZAAAAAAACw==", + "AAAAAAAAAAAAAAAJZ2V0X3N0YXRlAAAAAAAAAQAAAAAAAAAJZXNjcm93X2lkAAAAAAAABgAAAAEAAAPpAAAH0AAAAAxFc2Nyb3dTdGF0dXMAAAAD", + "AAAAAAAAAAAAAAAKZ2V0X2NvbmZpZwAAAAAAAAAAAAEAAAPpAAAD7QAAAAIAAAATAAAACwAAAAM=", + "AAAAAAAAAAAAAAAKZ2V0X2VzY3JvdwAAAAAAAQAAAAAAAAAJZXNjcm93X2lkAAAAAAAABgAAAAEAAAPpAAAH0AAAAAZFc2Nyb3cAAAAAAAM=", + "AAAAAAAAAAAAAAAKaW5pdGlhbGl6ZQAAAAAAAgAAAAAAAAAIdHJlYXN1cnkAAAATAAAAAAAAAAdmZWVfYnBzAAAAA+gAAAALAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAAAAAAAAAAAKc2V0X3BhdXNlZAAAAAAAAQAAAAAAAAAGcGF1c2VkAAAAAAABAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAAAAAAAAAAAKdXBkYXRlX2ZlZQAAAAAAAQAAAAAAAAALbmV3X2ZlZV9icHMAAAAACwAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAQAAAAAAAAAAAAAACU1pbGVzdG9uZQAAAAAAAAMAAAAAAAAABmFtb3VudAAAAAAACwAAAAAAAAALZGVzY3JpcHRpb24AAAAAEQAAAAAAAAAGc3RhdHVzAAAAAAfQAAAAD01pbGVzdG9uZVN0YXR1cwA=", + "AAAAAgAAAAAAAAAAAAAAClJlc29sdXRpb24AAAAAAAQAAAAAAAAAAAAAAAROb25lAAAAAAAAAAAAAAAJRGVwb3NpdG9yAAAAAAAAAAAAAAAAAAAJUmVjaXBpZW50AAAAAAAAAAAAAAAAAAAFU3BsaXQAAAA=", + "AAAAAAAAAAAAAAANY2FuY2VsX2VzY3JvdwAAAAAAAAEAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAABAAAD6QAAA+0AAAAAAAAAAw==", + "AAAAAAAAAAAAAAANY3JlYXRlX2VzY3JvdwAAAAAAAAcAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAACWRlcG9zaXRvcgAAAAAAABMAAAAAAAAACXJlY2lwaWVudAAAAAAAABMAAAAAAAAADXRva2VuX2FkZHJlc3MAAAAAAAATAAAAAAAAAAptaWxlc3RvbmVzAAAAAAPqAAAH0AAAAAlNaWxlc3RvbmUAAAAAAAAAAAAACGRlYWRsaW5lAAAABgAAAAAAAAANbWV0YWRhdGFfaGFzaAAAAAAAA+4AAAAgAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAAAAAAAAAAANZGVwb3NpdF9mdW5kcwAAAAAAAAEAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAABAAAD6QAAA+0AAAAAAAAAAw==", + "AAAAAAAAAAAAAAANcmFpc2VfZGlzcHV0ZQAAAAAAAAIAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAAAAAUZTZXQgZmVlIG92ZXJyaWRlIGZvciBhIHNwZWNpZmljIHRva2VuLgpPbmx5IHRyZWFzdXJ5IChhZG1pbikgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbi4KCiMgQXJndW1lbnRzCiogYGVudmAgLSBTb3JvYmFuIGVudmlyb25tZW50IHJlZmVyZW5jZQoqIGB0b2tlbl9hZGRyZXNzYCAtIEFkZHJlc3Mgb2YgdGhlIHRva2VuIHRvIHNldCBmZWUgZm9yCiogYGZlZV9icHNgIC0gRmVlIGluIGJhc2lzIHBvaW50cyAobXVzdCBiZSBpbiByYW5nZSBbMCwgQlBTX0RFTk9NSU5BVE9SXSkKCiMgUmV0dXJucwpPaygoKSkgb24gc3VjY2Vzcywgb3IgRXJyb3IgaWYgdmFsaWRhdGlvbiBmYWlscwAAAAAADXNldF90b2tlbl9mZWUAAAAAAAACAAAAAAAAAA10b2tlbl9hZGRyZXNzAAAAAAAAEwAAAAAAAAAHZmVlX2JwcwAAAAALAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAAAAAAAAAAAOcmVmdW5kX2V4cGlyZWQAAAAAAAIAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAABmNhbGxlcgAAAAAAEwAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAAAAAT9TZXQgZmVlIG92ZXJyaWRlIGZvciBhIHNwZWNpZmljIGVzY3Jvdy4KT25seSB0cmVhc3VyeSAoYWRtaW4pIGNhbiBjYWxsIHRoaXMgZnVuY3Rpb24uCgojIEFyZ3VtZW50cwoqIGBlbnZgIC0gU29yb2JhbiBlbnZpcm9ubWVudCByZWZlcmVuY2UKKiBgZXNjcm93X2lkYCAtIElEIG9mIHRoZSBlc2Nyb3cgdG8gc2V0IGZlZSBmb3IKKiBgZmVlX2Jwc2AgLSBGZWUgaW4gYmFzaXMgcG9pbnRzIChtdXN0IGJlIGluIHJhbmdlIFswLCBCUFNfREVOT01JTkFUT1JdKQoKIyBSZXR1cm5zCk9rKCgpKSBvbiBzdWNjZXNzLCBvciBFcnJvciBpZiB2YWxpZGF0aW9uIGZhaWxzAAAAAA5zZXRfZXNjcm93X2ZlZQAAAAAAAgAAAAAAAAAJZXNjcm93X2lkAAAAAAAABgAAAAAAAAAHZmVlX2JwcwAAAAALAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAgAAAAAAAAAAAAAADEVzY3Jvd1N0YXR1cwAAAAcAAAAAAAAAAAAAAAdDcmVhdGVkAAAAAAAAAAAAAAAABkFjdGl2ZQAAAAAAAAAAAAAAAAAJQ29tcGxldGVkAAAAAAAAAAAAAAAAAAAJQ2FuY2VsbGVkAAAAAAAAAAAAAAAAAAAIRGlzcHV0ZWQAAAAAAAAAAAAAAAhSZXNvbHZlZAAAAAAAAAAAAAAAB0V4cGlyZWQA", + "AAAAAAAAAAAAAAAPY29tcGxldGVfZXNjcm93AAAAAAEAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAABAAAD6QAAA+0AAAAAAAAAAw==", + "AAAAAAAAAAAAAAAPcmVzb2x2ZV9kaXNwdXRlAAAAAAMAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAABndpbm5lcgAAAAAAEwAAAAAAAAATc3BsaXRfd2lubmVyX2Ftb3VudAAAAAPoAAAACwAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAgAAAAAAAAAAAAAADUNvbnRyYWN0U3RhdGUAAAAAAAACAAAAAAAAAAAAAAAGQWN0aXZlAAAAAAAAAAAAAAAAAAZQYXVzZWQAAA==", + "AAAAAAAAAAAAAAAQY29uZmlybV9kZWxpdmVyeQAAAAMAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAAD21pbGVzdG9uZV9pbmRleAAAAAAEAAAAAAAAAAVidXllcgAAAAAAABMAAAABAAAD6QAAA+0AAAAAAAAAAw==", + "AAAAAAAAAHRDb2xsZWN0IGEgc2lnbmF0dXJlIGZvciByZWxlYXNpbmcgZnVuZHMKVGhlIHNpZ25hdHVyZSBjYW4gY29tZSBmcm9tIGVpdGhlciB0aGUgZGVwb3NpdG9yIG9yIGEgZGVzaWduYXRlZCB0aGlyZCBwYXJ0eQAAABFjb2xsZWN0X3NpZ25hdHVyZQAAAAAAAAIAAAAAAAAACWVzY3Jvd19pZAAAAAAAAAYAAAAAAAAABnNpZ25lcgAAAAAAEwAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAAAAAAAAAAARcmVsZWFzZV9taWxlc3RvbmUAAAAAAAACAAAAAAAAAAllc2Nyb3dfaWQAAAAAAAAGAAAAAAAAAA9taWxlc3RvbmVfaW5kZXgAAAAABAAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAgAAAAAAAAAAAAAAD01pbGVzdG9uZVN0YXR1cwAAAAADAAAAAAAAAAAAAAAHUGVuZGluZwAAAAAAAAAAAAAAAAhSZWxlYXNlZAAAAAAAAAAAAAAACERpc3B1dGVk", + "AAAAAAAAAG5Db25maWd1cmUgdGhlIHRocmVzaG9sZCBhbW91bnQgYW5kIHJlcXVpcmVkIHNpZ25hdHVyZXMgZm9yIGFuIGVzY3JvdwpPbmx5IHRoZSBkZXBvc2l0b3IgY2FuIGNhbGwgdGhpcyBmdW5jdGlvbgAAAAAAEmNvbmZpZ3VyZV9tdWx0aXNpZwAAAAAAAwAAAAAAAAAJZXNjcm93X2lkAAAAAAAABgAAAAAAAAAQdGhyZXNob2xkX2Ftb3VudAAAAAsAAAAAAAAAE3JlcXVpcmVkX3NpZ25hdHVyZXMAAAAABAAAAAEAAAPpAAAD7QAAAAAAAAAD", + "AAAAAAAAAAAAAAAUY3JlYXRlX2VzY3Jvd3NfYmF0Y2gAAAABAAAAAAAAAAhyZXF1ZXN0cwAAA+oAAAfQAAAAE0NyZWF0ZUVzY3Jvd1JlcXVlc3QAAAAAAQAAA+kAAAPtAAAAAAAAAAM=", + "AAAAAQAAAAAAAAAAAAAAE0NyZWF0ZUVzY3Jvd1JlcXVlc3QAAAAABwAAAAAAAAAIZGVhZGxpbmUAAAAGAAAAAAAAAAlkZXBvc2l0b3IAAAAAAAATAAAAAAAAAAllc2Nyb3dfaWQAAAAAAAAGAAAAAAAAAA1tZXRhZGF0YV9oYXNoAAAAAAAD7gAAACAAAAAAAAAACm1pbGVzdG9uZXMAAAAAA+oAAAfQAAAACU1pbGVzdG9uZQAAAAAAAAAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAAAAAAANdG9rZW5fYWRkcmVzcwAAAAAAABM=", + "AAAAAQAAAAAAAAAAAAAAFkVzY3Jvd0NyZWF0ZWRCYXRjaEl0ZW0AAAAAAAYAAAAAAAAACGRlYWRsaW5lAAAABgAAAAAAAAAJZGVwb3NpdG9yAAAAAAAAEwAAAAAAAAAJZXNjcm93X2lkAAAAAAAABgAAAAAAAAAJcmVjaXBpZW50AAAAAAAAEwAAAAAAAAANdG9rZW5fYWRkcmVzcwAAAAAAABMAAAAAAAAADHRvdGFsX2Ftb3VudAAAAAs=" ]), + options + ) + } + public readonly fromJSON = { + init: (json: string) => (this as any).txFromJSON(json), + get_state: (json: string) => (this as any).txFromJSON(json), + get_config: (json: string) => (this as any).txFromJSON(json), + get_escrow: (json: string) => (this as any).txFromJSON(json), + initialize: (json: string) => (this as any).txFromJSON(json), + set_paused: (json: string) => (this as any).txFromJSON(json), + update_fee: (json: string) => (this as any).txFromJSON(json), + cancel_escrow: (json: string) => (this as any).txFromJSON(json), + create_escrow: (json: string) => (this as any).txFromJSON(json), + deposit_funds: (json: string) => (this as any).txFromJSON(json), + raise_dispute: (json: string) => (this as any).txFromJSON(json), + set_token_fee: (json: string) => (this as any).txFromJSON(json), + refund_expired: (json: string) => (this as any).txFromJSON(json), + set_escrow_fee: (json: string) => (this as any).txFromJSON(json), + complete_escrow: (json: string) => (this as any).txFromJSON(json), + resolve_dispute: (json: string) => (this as any).txFromJSON(json), + confirm_delivery: (json: string) => (this as any).txFromJSON(json), + collect_signature: (json: string) => (this as any).txFromJSON(json), + release_milestone: (json: string) => (this as any).txFromJSON(json), + configure_multisig: (json: string) => (this as any).txFromJSON(json), + create_escrows_batch: (json: string) => (this as any).txFromJSON(json) + } +} \ No newline at end of file diff --git a/apps/contract-bindings/tsconfig.json b/apps/contract-bindings/tsconfig.json new file mode 100644 index 00000000..acac1422 --- /dev/null +++ b/apps/contract-bindings/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/package.json b/package.json index c2fdb24d..6a482692 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "axios": "^1.13.5" }, "scripts": { - "lint": "eslint . --ext .ts,.tsx" + "lint": "eslint . --ext .ts,.tsx", + "generate-bindings": "powershell ./scripts/generate-bindings.ps1" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^8.56.1", diff --git a/scripts/generate-bindings.ps1 b/scripts/generate-bindings.ps1 new file mode 100644 index 00000000..3f99a40a --- /dev/null +++ b/scripts/generate-bindings.ps1 @@ -0,0 +1,61 @@ +# scripts/generate-bindings.ps1 +# Requires soroban-cli to be installed + +$RepoRoot = Resolve-Path "$PSScriptRoot\.." +Set-Location -Path $RepoRoot + +# Build contract +Write-Host "Building contract..." -ForegroundColor Cyan +Set-Location -Path "apps/onchain" +cargo build --target wasm32-unknown-unknown --release +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to build contract" -ForegroundColor Red + exit $LASTEXITCODE +} +Set-Location -Path $RepoRoot + +# Generate bindings +Write-Host "Generating TypeScript bindings..." -ForegroundColor Cyan +$WasmPath = "apps/onchain/target/wasm32-unknown-unknown/release/onchain.wasm" +$OutputDir = "apps/contract-bindings" + +# Ensure output directory exists +if (-not (Test-Path $OutputDir)) { + New-Item -ItemType Directory -Path $OutputDir +} + +# Use full path to stellar.exe if not in PATH +$StellarExe = "C:\Program Files (x86)\Stellar CLI\stellar.exe" +if (-not (Test-Path $StellarExe)) { + $StellarExe = "stellar" # Fallback to PATH +} + +& $StellarExe contract bindings typescript ` + --wasm $WasmPath ` + --output-dir $OutputDir ` + --overwrite + +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to generate bindings" -ForegroundColor Red + exit $LASTEXITCODE +} + +Write-Host "Building contract-bindings package..." -ForegroundColor Cyan +Set-Location -Path "apps/contract-bindings" + +if (-not (Test-Path "node_modules")) { + npm ci + if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to install contract-bindings dependencies" -ForegroundColor Red + exit $LASTEXITCODE + } +} + +npm run build +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to build contract-bindings package" -ForegroundColor Red + exit $LASTEXITCODE +} + +Set-Location -Path $RepoRoot +Write-Host "Bindings generated successfully in $OutputDir" -ForegroundColor Green