From 866b7d183cff9c00f4da488e04af92551f4fce53 Mon Sep 17 00:00:00 2001 From: Fabricius Zatti Date: Tue, 4 Jun 2024 13:27:00 +0000 Subject: [PATCH] build(connector-stellar): add a run soroban transaction endpoint - Add a run soroban transaction endpoint to the Stellar Connector plugin. - Add a Soroban hellow_world contract to the demo-contract folder. **Remarks:** The `runSorobanTransaction` endpoint can be used to make smart contract invocations on the Soroban platform. The endpoint accepts a flag called `readOnly` to indicate when the transaction should not alter ledger state. When `true`, the transaction will only be simulated based on the current ledger state and provide an up-to-date output without registering the transaction to the ledger, ensuring no fees are consumed. When `false`, the transaction will be executed and registered to the ledger even if it doesn't alter the ledger state, incurring fees as usual. More details can be found in the `README.md` file under the connector root directory. Closes #3238 Signed-off-by: Fabricius Zatti --- .../README.md | 76 +- .../src/main/json/openapi.json | 93 ++ .../src/main/json/openapi.tpl.json | 98 ++ .../main/typescript/core/contract-engine.ts | 91 + .../generated/openapi/typescript-axios/api.ts | 123 ++ .../plugin-ledger-connector-stellar.ts | 44 +- .../run-soroban-transaction-endpoint.ts | 104 ++ .../src/test/rust/demo-contract/.gitignore | 8 + .../src/test/rust/demo-contract/Cargo.lock | 1485 +++++++++++++++++ .../src/test/rust/demo-contract/Cargo.toml | 23 + .../src/test/rust/demo-contract/README.md | 70 + .../contracts/hello_world/Cargo.toml | 15 + .../contracts/hello_world/src/lib.rs | 134 ++ .../contracts/hello_world/src/test.rs | 120 ++ .../test_snapshots/test/test.1.json | 1460 ++++++++++++++++ .../test/rust/demo-contract/hello_world.wasm | Bin 0 -> 2886 bytes .../src/test/rust/demo-contract/spec.ts | 36 + .../soroban_token_contract.wasm | Bin 7456 -> 0 bytes .../deploy-contract/index.test.ts | 4 +- .../run-soroban-transaction/index.test.ts | 320 ++++ 20 files changed, 4290 insertions(+), 14 deletions(-) create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/run-soroban-transaction-endpoint.ts create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/.gitignore create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.lock create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.toml create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/README.md create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/Cargo.toml create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/lib.rs create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/test.rs create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/test_snapshots/test/test.1.json create mode 100755 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/hello_world.wasm create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/spec.ts delete mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm create mode 100644 packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/run-soroban-transaction/index.test.ts diff --git a/packages/cacti-plugin-ledger-connector-stellar/README.md b/packages/cacti-plugin-ledger-connector-stellar/README.md index 73e50b7f7a..fdc7034ee1 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/README.md +++ b/packages/cacti-plugin-ledger-connector-stellar/README.md @@ -3,8 +3,8 @@ This plugin provides `Cacti` a way to interact with Stellar networks. Using this we can perform: - Deploy Smart-contracts over the network. -- Build and sign transactions. (WIP) -- Invoke smart-contract functions that we have deployed on the network. (WIP) +- Build and sign transactions. +- Invoke smart-contract functions that we have deployed on the network. ## Summary @@ -51,11 +51,28 @@ This endpoint is responsible for deploying smart contracts to Soroban (Stellar's **Core Aspects**: - **Input**: Accepts either a compiled WASM buffer or a WASM hash. - - **WASM Buffer**: Uploads compiled code to Stellar, generates a on-chain WASM hash, then deploys the contract. Refer to the [Stellar documentation](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/contracts) for further detail on this process. - - **WASM Hash**: Directly deploys the contract using the existing WASM hash. + + - `wasmBuffer`: Uploads compiled code to Stellar, generates a on-chain WASM hash, then deploys the contract. Refer to the [Stellar documentation](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/contracts) for further detail on this process. + - `wasmHash`: Directly deploys the contract using the existing WASM hash. + - `transactionInvocation`: An object containing data about how the transaction should be assembled and executed. + - **Output**: An object containing the on-chain WASM hash and a unique contract ID of the newly deployed instance. -- **Transaction Invocation**: Provides data for assembling and executing the transactions. +#### `run-soroban-transaction` endpoint + +This endpoint is responsible for invoking smart contracts on Soroban (Stellar's smart contract platform) to either change or read a ledger state. + +**Core Aspects**: + +- **Input**: Accepts either a compiled WASM buffer or a WASM hash. + - `contractId`: The unique contract id of the contract instance to be invoked. + - `method`: The name of the contract method being invoked. + - `methodArgs`: An object containing the arguments accepted by the method. + - `specXdr`: An array containing the contract specification in XDR format. + - `transactionInvocation`: An object containing data about how the transaction should be assembled and executed. + - `readOnly`: A flag to indicate when the transaction should not alter ledger state. When `true`, the transaction will only be simulated based on the current ledger state and provide an up-to-date output without registering the transaction to the ledger, ensuring no fees are consumed. +- **Output**: An object containing the response of the transaction invocation. + - `result`: The direct output of the invoked contract method. ### Usage @@ -111,6 +128,28 @@ const deployOut = await connector.deployContract({ }); ``` +Call example to invoke a contract and return the output value: + +```typescript +const res = await connector.runSorobanTransaction({ + contractId, + method: "balance", + methodArgs: { + id: adminAccount.getPublicKey(), + }, + specXdr: tokenSpec, + readOnly: true, + transactionInvocation: { + header: { + source: adminAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [adminAccount.getSecretKey()], + }, +}); +``` + ### Building/running the container image locally In the Cacti project root say: @@ -168,9 +207,30 @@ docker run \ #### Testing API calls with the container -Don't have a Stellar network on hand to test with? Test or develop against our Stellar All-In-One container! +Don't have a Stellar network on hand to test with? Test or develop against our Stellar All-In-One container by importing the `StellarTestLedger` from `cacti-test-tooling`. It will deploy and manage a docker image based on [Stellar quickstart](https://github.com/stellar/quickstart). + +**Usage Example**(refer to the integration tests for further examples): + +```typescript +import { StellarTestLedger } from "@hyperledger/cactus-test-tooling"; +import { Network } from "stellar-plus/lib/stellar-plus"; + +const logLevel: LogLevelDesc = "TRACE"; +const stellarTestLedger = new StellarTestLedger({ logLevel }); + +await stellarTestLedger.start(); +const networkConfig = Network.CustomNet( + await stellarTestLedger.getNetworkConfiguration(), +); + +// Here use the networkConfig object to connect to +// your test ledger and run your tests. + +await stellarTestLedger.stop(); +await stellarTestLedger.destroy(); +``` -TODO (WIP) +In this example, the `StellarTestLedger` is used to pull up a fresh new ledger with no history and all of its required services to interact with the network. In conjunction with the `stellar-plus` library, the method `getNetworkConfiguration` is used to get all services data and instantiate an object that can be used with all tools in Stellar Plus to directly interact with the test ledger. ## Prometheus Exporter @@ -184,7 +244,7 @@ You can also initialize the prometheus exporter object seperately and then pass `getPrometheusMetricsV1` function returns the prometheus exporter metrics, currently displaying the total transaction count, which currently increments everytime a Stellar transaction is executed through the connector internal methods. This includes the methods - `deployContract` - - `runSorobanTransaction()` (_Soon_) +- `runSorobanTransaction()` ### Prometheus Integration diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json index f1fadfec61..0704de4549 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.json @@ -85,6 +85,65 @@ } } }, + "RunSorobanTransactionRequest": { + "type": "object", + "additionalProperties": false, + "required": [ + "contractId", + "method", + "specXdr", + "transactionInvocation", + "readOnly" + ], + "properties": { + "contractId": { + "type": "string", + "pattern": "^C[a-zA-Z0-9]{55}$", + "nullable": false, + "minLength": 56, + "maxLength": 56, + "description": "The ID of the contract that was deployed." + }, + "specXdr": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": false, + "description": "Array of strings containing the XDR of the contract specification." + }, + "method": { + "type": "string", + "nullable": false, + "description": "The method to be called on the contract." + }, + "methodArgs": { + "type": "object", + "nullable": true, + "description": "The arguments to pass to the method." + }, + "transactionInvocation": { + "$ref": "#/components/schemas/TransactionInvocation", + "nullable": false, + "description": "The transaction invocation that will be used to run the contract." + }, + "readOnly": { + "type": "boolean", + "nullable": false, + "description": "Flag indicating if the transaction should be read-only." + } + } + }, + "RunSorobanTransactionResponse": { + "type": "object", + "properties": { + "result": { + "type": "object", + "description": "The result of the invoked contract method." + } + }, + "description": "Response object containing the result of a contract invocation or error information if it failed." + }, "DeployContractV1Request": { "type": "object", "additionalProperties": false, @@ -209,6 +268,40 @@ } } }, + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction" + } + }, + "operationId": "runSorobanTransactionV1", + "summary": "Executes a Soroban transaction on a stellar ledger", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunSorobanTransactionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunSorobanTransactionResponse" + } + } + } + } + } + } + }, "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/get-prometheus-exporter-metrics": { "get": { "x-hyperledger-cacti": { diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.tpl.json b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.tpl.json index f1fadfec61..931a18691f 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.tpl.json +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/json/openapi.tpl.json @@ -85,6 +85,68 @@ } } }, + + "RunSorobanTransactionRequest": { + "type": "object", + "additionalProperties": false, + "required": [ + "contractId", + "method", + "specXdr", + "transactionInvocation", + "readOnly" + ], + "properties": { + "contractId": { + "type": "string", + "pattern": "^C[a-zA-Z0-9]{55}$", + "nullable": false, + "minLength": 56, + "maxLength": 56, + "description": "The ID of the contract that was deployed." + }, + "specXdr": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": false, + "description": "Array of strings containing the XDR of the contract specification." + }, + "method": { + "type": "string", + "nullable": false, + "description": "The method to be called on the contract." + }, + "methodArgs": { + "type": "object", + "nullable": true, + "description": "The arguments to pass to the method." + }, + "transactionInvocation": { + "$ref": "#/components/schemas/TransactionInvocation", + "nullable": false, + "description": "The transaction invocation that will be used to run the contract." + }, + "readOnly": { + "type": "boolean", + "nullable": false, + "description": "Flag indicating if the transaction should be read-only." + } + } + }, + + "RunSorobanTransactionResponse": { + "type": "object", + "properties": { + "result": { + "type": "object", + "description": "The result of the invoked contract method." + } + }, + "description": "Response object containing the result of a contract invocation or error information if it failed." + }, + "DeployContractV1Request": { "type": "object", "additionalProperties": false, @@ -209,6 +271,42 @@ } } }, + + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction": { + "post": { + "x-hyperledger-cacti": { + "http": { + "verbLowerCase": "post", + "path": "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction" + } + }, + "operationId": "runSorobanTransactionV1", + "summary": "Executes a Soroban transaction on a stellar ledger", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunSorobanTransactionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunSorobanTransactionResponse" + } + } + } + } + } + } + }, + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/get-prometheus-exporter-metrics": { "get": { "x-hyperledger-cacti": { diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts index b1a0f5291e..c661d8c133 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/core/contract-engine.ts @@ -2,6 +2,7 @@ import { ContractEngine } from "stellar-plus/lib/stellar-plus/core/contract-engi import { SorobanTransactionPipelineOptions } from "stellar-plus/lib/stellar-plus/core/pipelines/soroban-transaction/types"; import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; import { TransactionInvocation } from "stellar-plus/lib/stellar-plus/types"; +import { Core as SPCore } from "stellar-plus/lib/stellar-plus/core/index"; export interface DeployContractWithWasmOptions extends BaseContractEngineInvocation { @@ -25,6 +26,21 @@ interface BaseContractEngineInvocation { networkConfig: NetworkConfig; } +export interface InvokeContractOptions extends BaseInvokeContractOptions { + readOnly: false; +} +export interface ReadContractOptions extends BaseInvokeContractOptions { + readOnly: true; +} + +export interface BaseInvokeContractOptions + extends BaseContractEngineInvocation { + contractId: string; + method: string; + methodArgs: T; + specXdr: string[]; +} + /** * Deploys a contract to the Stellar network. Accepts either a WebAssembly binary buffer or a hash of the WebAssembly binary code in the ledger. * @@ -76,3 +92,78 @@ export const deployContract = async ( return output; }; + +/** + * + * Invokes a contract on the Stellar network. Can perform a full contract invocation or a read-only contract invocation. + * Read-only should be used when the contract does not modify the ledger state. In these cases, the contract invocation is not submitted to the network. + * Only a simulation is performed to determine the result of the contract invocation, therefore no fees are incurred. + * + * @param {InvokeContractOptions | ReadContractOptions} options - Options for invoking a contract. + * @param {TransactionInvocation} options.txInvocation - Transaction invocation object containing the parameters to configure the transaction envelope. + * @param {SorobanTransactionPipelineOptions} [options.pipelineOptions] - Options for the Soroban transaction pipeline. + * @param {string} options.fnLogPrefix - Prefix for log messages. + * @param {NetworkConfig} options.networkConfig - Network configuration object. Contains the details of the services available to interact with the Stellar network. + * @param {string} options.contractId - Contract ID of the contract to invoke. + * @param {string} options.method - Method to invoke on the contract. + * @param {T} options.methodArgs - Object containing the arguments to pass to the method. + * @param {string[]} options.specXdr - Array of strings containing the XDR of the contract specification. + * @param {boolean} options.readOnly - Flag to indicate if the contract invocation is read-only. + * + * @returns {Promise} - Returns the result of the contract invocation. + * + * @throws {Error} - Throws an error if the contract invocation fails. + * @throws {Error} - Throws an error if the contract read fails. + * + * + * @returns + */ +export const invokeContract = async ( + options: InvokeContractOptions | ReadContractOptions, +): Promise => { + const { + networkConfig, + txInvocation, + fnLogPrefix, + pipelineOptions, + contractId, + method, + methodArgs, + specXdr, + } = options; + + const fnTag = `${fnLogPrefix}#invokeContract()`; + + const contractEngine = new ContractEngine({ + networkConfig, + contractParameters: { + contractId: contractId, + spec: new SPCore.ContractSpec(specXdr), + }, + options: { + sorobanTransactionPipeline: pipelineOptions, + }, + }); + + if (options.readOnly) { + return await contractEngine + .readFromContract({ + method, + methodArgs: methodArgs as object, + ...txInvocation, + }) + .catch((error) => { + throw new Error(`${fnTag} Failed to read contract. ` + error); + }); + } + + return await contractEngine + .invokeContract({ + method, + methodArgs: methodArgs as object, + ...txInvocation, + }) + .catch((error) => { + throw new Error(`${fnTag} Failed to invoke contract. ` + error); + }); +}; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts index e2ea2441af..0cab5ac99e 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -67,6 +67,62 @@ export interface DeployContractV1Response { */ 'wasmHash': string | null; } +/** + * + * @export + * @interface RunSorobanTransactionRequest + */ +export interface RunSorobanTransactionRequest { + /** + * The ID of the contract that was deployed. + * @type {string} + * @memberof RunSorobanTransactionRequest + */ + 'contractId': string; + /** + * Array of strings containing the XDR of the contract specification. + * @type {Array} + * @memberof RunSorobanTransactionRequest + */ + 'specXdr': Array; + /** + * The method to be called on the contract. + * @type {string} + * @memberof RunSorobanTransactionRequest + */ + 'method': string; + /** + * The arguments to pass to the method. + * @type {object} + * @memberof RunSorobanTransactionRequest + */ + 'methodArgs'?: object | null; + /** + * + * @type {TransactionInvocation} + * @memberof RunSorobanTransactionRequest + */ + 'transactionInvocation': TransactionInvocation; + /** + * Flag indicating if the transaction should be read-only. + * @type {boolean} + * @memberof RunSorobanTransactionRequest + */ + 'readOnly': boolean; +} +/** + * Response object containing the result of a contract invocation or error information if it failed. + * @export + * @interface RunSorobanTransactionResponse + */ +export interface RunSorobanTransactionResponse { + /** + * The result of the invoked contract method. + * @type {object} + * @memberof RunSorobanTransactionResponse + */ + 'result'?: object; +} /** * * @export @@ -237,6 +293,40 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Executes a Soroban transaction on a stellar ledger + * @param {RunSorobanTransactionRequest} [runSorobanTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + runSorobanTransactionV1: async (runSorobanTransactionRequest?: RunSorobanTransactionRequest, options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(runSorobanTransactionRequest, localVarRequestOptions, configuration) + return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, @@ -283,6 +373,17 @@ export const DefaultApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.getPrometheusMetricsV1(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary Executes a Soroban transaction on a stellar ledger + * @param {RunSorobanTransactionRequest} [runSorobanTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async runSorobanTransactionV1(runSorobanTransactionRequest?: RunSorobanTransactionRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.runSorobanTransactionV1(runSorobanTransactionRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, } }; @@ -321,6 +422,16 @@ export const DefaultApiFactory = function (configuration?: Configuration, basePa getPrometheusMetricsV1(options?: any): AxiosPromise { return localVarFp.getPrometheusMetricsV1(options).then((request) => request(axios, basePath)); }, + /** + * + * @summary Executes a Soroban transaction on a stellar ledger + * @param {RunSorobanTransactionRequest} [runSorobanTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + runSorobanTransactionV1(runSorobanTransactionRequest?: RunSorobanTransactionRequest, options?: any): AxiosPromise { + return localVarFp.runSorobanTransactionV1(runSorobanTransactionRequest, options).then((request) => request(axios, basePath)); + }, }; }; @@ -364,6 +475,18 @@ export class DefaultApi extends BaseAPI { public getPrometheusMetricsV1(options?: AxiosRequestConfig) { return DefaultApiFp(this.configuration).getPrometheusMetricsV1(options).then((request) => request(this.axios, this.basePath)); } + + /** + * + * @summary Executes a Soroban transaction on a stellar ledger + * @param {RunSorobanTransactionRequest} [runSorobanTransactionRequest] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public runSorobanTransactionV1(runSorobanTransactionRequest?: RunSorobanTransactionRequest, options?: AxiosRequestConfig) { + return DefaultApiFp(this.configuration).runSorobanTransactionV1(runSorobanTransactionRequest, options).then((request) => request(this.axios, this.basePath)); + } } diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts index e23bed8e67..efa6245c5b 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/plugin-ledger-connector-stellar.ts @@ -24,6 +24,8 @@ import { import { DeployContractV1Request, DeployContractV1Response, + RunSorobanTransactionRequest, + RunSorobanTransactionResponse, WatchBlocksV1, } from "./generated/openapi/typescript-axios"; @@ -35,13 +37,14 @@ import { } from "./web-services/get-open-api-spec-v1-endpoint"; import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; import { PluginRegistry } from "@hyperledger/cactus-core"; -import { deployContract } from "./core/contract-engine"; +import { deployContract, invokeContract } from "./core/contract-engine"; import { convertApiTransactionInvocationToStellarPlus } from "./utils"; import { GetPrometheusExporterMetricsEndpointV1, IGetPrometheusExporterMetricsEndpointV1Options, } from "./web-services/get-prometheus-exporter-metrics-endpoint-v1"; import { DeployContractEndpoint } from "./web-services/deploy-contract-endpoint"; +import { RunSorobanTransactionEndpoint } from "./web-services/run-soroban-transaction-endpoint"; export const E_KEYCHAIN_NOT_FOUND = "cacti.connector.stellar.keychain_not_found"; @@ -59,8 +62,8 @@ export class PluginLedgerConnectorStellar IPluginLedgerConnector< DeployContractV1Request, DeployContractV1Response, - unknown, - unknown + RunSorobanTransactionRequest, + RunSorobanTransactionResponse >, ICactusPlugin, IPluginWebService @@ -129,8 +132,32 @@ export class PluginLedgerConnectorStellar return response; } - public transact(): Promise { - throw new InternalServerError("Method not implemented."); + public async runSorobanTransaction( + req: RunSorobanTransactionRequest, + ): Promise { + const invokeArgs = { + ...req, + methodArgs: req.methodArgs ?? {}, + networkConfig: this.networkConfig, + readOnly: !!req.readOnly, + fnLogPrefix: this.className, + txInvocation: convertApiTransactionInvocationToStellarPlus( + req.transactionInvocation, + this.networkConfig, + ), + }; + + const result = + await invokeContract(invokeArgs); + + this.prometheusExporter.addCurrentTransaction(); + return { result } as RunSorobanTransactionResponse; + } + + public async transact( + req: RunSorobanTransactionRequest, + ): Promise { + return this.runSorobanTransaction(req); } public async getConsensusAlgorithmFamily(): Promise { @@ -198,6 +225,13 @@ export class PluginLedgerConnectorStellar }); endpoints.push(endpoint); } + { + const endpoint = new RunSorobanTransactionEndpoint({ + connector: this, + logLevel: this.options.logLevel, + }); + endpoints.push(endpoint); + } { const opts: IGetPrometheusExporterMetricsEndpointV1Options = { connector: this, diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/run-soroban-transaction-endpoint.ts b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/run-soroban-transaction-endpoint.ts new file mode 100644 index 0000000000..25d0a5816d --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/main/typescript/web-services/run-soroban-transaction-endpoint.ts @@ -0,0 +1,104 @@ +import type { Express, Request, Response } from "express"; + +import { + IWebServiceEndpoint, + IExpressRequestHandler, + IEndpointAuthzOptions, +} from "@hyperledger/cactus-core-api"; + +import { + Logger, + Checks, + LogLevelDesc, + LoggerProvider, + IAsyncProvider, +} from "@hyperledger/cactus-common"; + +import { + handleRestEndpointException, + registerWebServiceEndpoint, +} from "@hyperledger/cactus-core"; + +import { PluginLedgerConnectorStellar } from "../plugin-ledger-connector-stellar"; +import OAS from "../../json/openapi.json"; +import { RunSorobanTransactionRequest } from "../generated/openapi/typescript-axios"; + +export interface IRunSorobanTransactionOptions { + logLevel?: LogLevelDesc; + connector: PluginLedgerConnectorStellar; +} + +export class RunSorobanTransactionEndpoint implements IWebServiceEndpoint { + public static readonly CLASS_NAME = "RunSorobanTransactionEndpoint"; + + private readonly log: Logger; + + public get className(): string { + return RunSorobanTransactionEndpoint.CLASS_NAME; + } + + constructor(public readonly options: IRunSorobanTransactionOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(options, `${fnTag} arg options`); + Checks.truthy(options.connector, `${fnTag} arg options.connector`); + + const level = this.options.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + } + + public get oasPath(): (typeof OAS.paths)["/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction"] { + return OAS.paths[ + "/api/v1/plugins/@hyperledger/cacti-plugin-ledger-connector-stellar/run-soroban-transaction" + ]; + } + + public getPath(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.path; + } + + public getVerbLowerCase(): string { + return this.oasPath.post["x-hyperledger-cacti"].http.verbLowerCase; + } + + public getOperationId(): string { + return this.oasPath.post.operationId; + } + + getAuthorizationOptionsProvider(): IAsyncProvider { + // TODO: make this an injectable dependency in the constructor + return { + get: async () => ({ + isProtected: true, + requiredRoles: [], + }), + }; + } + + public async registerExpress( + expressApp: Express, + ): Promise { + await registerWebServiceEndpoint(expressApp, this); + return this; + } + + public getExpressRequestHandler(): IExpressRequestHandler { + return this.handleRequest.bind(this); + } + + public async handleRequest(req: Request, res: Response): Promise { + const { log } = this; + const fnTag = `${this.className}#handleRequest()`; + const reqTag = `${this.getVerbLowerCase()} - ${this.getPath()}`; + this.log.debug(reqTag); + const reqBody: RunSorobanTransactionRequest = req.body; + try { + const resBody = + await this.options.connector.runSorobanTransaction(reqBody); + res.json(resBody); + } catch (ex) { + const errorMsg = `${reqTag} ${fnTag} Failed to deploy contract:`; + await handleRestEndpointException({ errorMsg, log, error: ex, res }); + } + } +} diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/.gitignore b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/.gitignore new file mode 100644 index 0000000000..e5a98e3db1 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/.gitignore @@ -0,0 +1,8 @@ +# Rust's output directory +target + +# Local Soroban settings +.soroban + +# bindings +bindings \ No newline at end of file diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.lock b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.lock new file mode 100644 index 0000000000..54b8732bab --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.lock @@ -0,0 +1,1485 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes-lit" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hello-world" +version = "0.0.0" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "20.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc32c6e817f3ca269764ec0d7d14da6210b74a5bf14d4e745aa3ee860558900" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "soroban-env-common" +version = "20.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c14e18d879c520ff82612eaae0590acaf6a7f3b977407e1abb1c9e31f94c7814" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", +] + +[[package]] +name = "soroban-env-guest" +version = "20.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5122ca2abd5ebcc1e876a96b9b44f87ce0a0e06df8f7c09772ddb58b159b7454" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "20.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114a0fa0d0cc39d0be16b1ee35b6e5f4ee0592ddcf459bde69391c02b03cf520" +dependencies = [ + "backtrace", + "curve25519-dalek", + "ed25519-dalek", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey", +] + +[[package]] +name = "soroban-env-macros" +version = "20.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13e3f8c86f812e0669e78fcb3eae40c385c6a9dd1a4886a1de733230b4fcf27" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "20.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a54708f44890e0546180db6b4f530e2a88d83b05a9b38a131caa21d005e25a" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "20.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fc8be9068dd4e0212d8b13ad61089ea87e69ac212c262914503a961c8dc3a3" +dependencies = [ + "arbitrary", + "bytes-lit", + "ctor", + "ed25519-dalek", + "rand", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey", +] + +[[package]] +name = "soroban-sdk-macros" +version = "20.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db20def4ead836663633f58d817d0ed8e1af052c9650a04adf730525af85b964" +dependencies = [ + "crate-git-revision", + "darling", + "itertools", + "proc-macro2", + "quote", + "rustc_version", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn", +] + +[[package]] +name = "soroban-spec" +version = "20.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eefeb5d373b43f6828145d00f0c5cc35e96db56a6671ae9614f84beb2711cab" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "20.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3152bca4737ef734ac37fe47b225ee58765c9095970c481a18516a2b287c7a33" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-strkey" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" +dependencies = [ + "base32", + "crate-git-revision", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e59cdf3eb4467fb5a4b00b52e7de6dca72f67fac6f9b700f55c95a5d86f09c9d" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "escape-bytes", + "hex", + "serde", + "serde_with", + "stellar-strkey", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.88.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" +dependencies = [ + "indexmap 1.9.3", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.toml b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.toml new file mode 100644 index 0000000000..0c3a329803 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/Cargo.toml @@ -0,0 +1,23 @@ +[workspace] +resolver = "2" +members = [ + "contracts/*", +] + +[workspace.dependencies] +soroban-sdk = "20.3.2" + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true + +# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile +[profile.release-with-logs] +inherits = "release" +debug-assertions = true diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/README.md b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/README.md new file mode 100644 index 0000000000..4b8cb577cb --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/README.md @@ -0,0 +1,70 @@ +# Hello World Soroban Project + +A demo project containing the source cofr for an implementation of a soroban smart contract and tests. + +## Requirements + +- Rust 1.74.0 or newer +- Soroban CLI 21.0.0-rc.1 or newer + +See the full setup at [Soroban's official documentation](https://developers.stellar.org/docs/smart-contracts/getting-started/setup). + +## Project initialization + +This project was initialized using the soroban-cli on version 21.0.0-rc.1 with the command below: + +```bash +soroban contract init demo-contract +``` + +For further details on how to start a new soroban smart contract projects, refer to the [official documentation](https://developers.stellar.org/docs/smart-contracts/getting-started). + +## Project Structure + +This repository uses the recommended structure for a Soroban project: + +```text +. +├── contracts +│   └── hello_world +│   ├── src +│   │   ├── lib.rs +│   │   └── test.rs +│   └── Cargo.toml +├── Cargo.toml +└── README.md +``` + +- New Soroban contracts can be put in `contracts`, each in their own directory. There is already a `hello_world` contract in there to get you started. +- If you initialized this project with any other example contracts via `--with-example`, those contracts will be in the `contracts` directory as well. +- Contracts should have their own `Cargo.toml` files that rely on the top-level `Cargo.toml` workspace for their dependencies. +- Frontend libraries can be added to the top-level directory as well. If you initialized this project with a frontend template via `--frontend-template` you will have those files already included. + +## Building the Contract + +To build the contract, run the following command: + +```bash +soroban contract build +``` + +The compiled file will be saved to `target/wasm32-unknown-unknown/release/hello_world.wasm`. + +## Testing + +The tests are present in the `src/test.rs` file and can be executed by running the following command: + +```bash +cargo test +``` + +## Ready-to-use assets + +Within this project's root directory one may find the following assets: + +- **hello_world.wasm** : A compiled WASM file of the code, ready to be uploaded to a Stellar network. +- **spec.ts** : A typescript file exporting useful objects for clients to interact with this contract. + - `spec`: spec array containing the contract specification encoded in XDR format. This spec can be extracted from compiled WASM contracts or by generating the contract bindings using the `soroban-cli`. + - `methods`: enum object containing the method names for the functions in the contract as they should be invoked. + - `Args`: Multiple types for the 'args' object to be structured to invoke that method. + - `Response`: Multiple types for the responses received from the contract invocations to that method. diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/Cargo.toml b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/Cargo.toml new file mode 100644 index 0000000000..2d8b3ac4ea --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "hello-world" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/lib.rs b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/lib.rs new file mode 100644 index 0000000000..0a366afa1d --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/lib.rs @@ -0,0 +1,134 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, token, vec, Address, Env, Symbol, Vec}; + +// +// Trait that defines the contract interface +// +pub trait HelloContractTrait { + // Function that returns a Symbol with the string "Hello, World!" + fn say_hello(env: Env) -> Symbol; + // Function that returns a Vec with the strings "Hello", and the + // name of the recipient provided in the argument `to`. + fn say_hello_to(env: Env, to: Symbol) -> Vec; + // Function that returns a Symbol with the string "CaptainCacti" + fn set_name(env: Env, name: Symbol); + // Function that sets the name provided in the argument `name` + // in the storage in a Vec with the key DataKey::Names + fn get_name(env: Env) -> Symbol; + // Function that returns the name at the index provided in the + // argument `index` from the Vec in the storage with the key DataKey::Names + fn get_name_by_index(env: Env, index: u32) -> Symbol; + // Function that performs a deposit of the asset_id provided in the argument `asset_id` + // with the amount provided in the argument `amount` from the address provided in the argument `from` + // to the contract address. + fn deposit(env: Env, from: Address, asset_id: Address, amount: i128); +} + + +// +// Enum that defines the storage keys +// +#[contracttype] +pub enum DataKey { + Names, // Vec of names + Balance(Address) // u64 balance for an address +} + +// +// Contract implementation +// +#[contract] +pub struct HelloContract; + +#[contractimpl] +impl HelloContractTrait for HelloContract { + + fn say_hello(env: Env) -> Symbol{ + // Return a new Symbol with the string "Hello, World!" + return Symbol::new(&env,"HelloWorld"); + } + + fn say_hello_to(env: Env, to: Symbol) -> Vec { + // Return a new Vec with the strings "Hello", and the + // name of the recipient provided in the argument `to`. + vec![&env, symbol_short!("Hello"), to] + } + + + fn get_name(env: Env) -> Symbol { + // Return a new Symbol with the string "CaptainCacti" + return Symbol::new(&env,"CaptainCacti"); + } + + fn set_name(env: Env, name: Symbol) { + // Attempts to read the current value for the + // Vec from the storage and unwrap it. + // If the value does not exist, it will return an empty Vec. + let mut names = env + .storage() + .instance() + .get::<_,Vec>(&DataKey::Names) + .unwrap_or(Vec::new(&env)); + + // Push the new name to the back of the Vec + names.push_back(name); + + // Set the new Vec in the storage for the key DataKey::Names + env.storage().instance().set(&DataKey::Names, &names); + } + + fn get_name_by_index(env: Env, index: u32) -> Symbol { + // Attempts to read the current value for the + // Vec from the storage and unwrap it. + // If the value does not exist, it will return an empty Vec. + let names = env + .storage() + .instance() + .get::<_,Vec>(&DataKey::Names) + .unwrap_or(Vec::new(&env)); + + // Attempt to get the name at the index provided in the argument `index`. + // If the index is out of bounds, it will return None. + let name = names.try_get(index).unwrap(); + + // If the name is None, return a new Symbol with the string "No name found" + if name.is_none() { + return Symbol::new(&env,"NotFound"); + } + // Since we know the name is not None, + // we can unwrap it safely and return it. + name.unwrap() + } + + fn deposit(env: Env, from: Address, asset_id: Address, amount: i128) { + + // Ensure the address provided in the argument `from` + // has authorized the contract to perform the deposit. + from.require_auth(); + + // Get the balance for the address provided in the argument `from` + // from the storage for the key DataKey::Balance + // If the balance does not exist, it will return 0. + let balance = env + .storage() + .instance() + .get::<_,i128>(&DataKey::Balance(from.clone())) + .unwrap_or(0); + + // Add the amount provided in the argument `amount` to the balance + let new_balance = balance + amount; + + // Set the new balance in the storage for the key DataKey::Balance + env.storage().instance().set(&DataKey::Balance(from.clone()), &new_balance); + + // Invoke the transfer function of the token contract with the id + // provided in the argument `asset_id`. If the transfer fails, it will panic + // and revert the transaction. + token::Client::new(&env, &asset_id).transfer( &from, &env.current_contract_address(), &amount) + + } + + +} + +mod test; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/test.rs b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/test.rs new file mode 100644 index 0000000000..560320dda8 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/src/test.rs @@ -0,0 +1,120 @@ +#![cfg(test)] + + +use super::*; +use soroban_sdk::{ testutils::Address, Env}; +use token::StellarAssetClient; + +#[test] +fn test() { + // Create a new environment + let env = Env::default(); + + // Register the contract with the environment + let contract_id = env.register_contract(None, HelloContract); + + // Instantiate a new client for the contract + let client = HelloContractClient::new(&env, &contract_id); + + // Mock all authorizations for the subsequent calls + // This ensures we don't have to individually mock each authorization + env.mock_all_auths(); + + // ============================== + // Test the say_hello function + // ============================ + + // Use the client to call the say_hello function and store the result in the variable words + let output_say_hello = client.say_hello(); + + // Assert that the result is equal to the Symbol "HelloWorld" + assert_eq!(output_say_hello, Symbol::new(&env, "HelloWorld")); + + + // ============================== + // Test the say_hello_to function + // =============================== + + // Use the client to call the say_hello_to function with the argument "Alice" and store the result in the variable words + let output_say_hello_to = client.say_hello_to(&Symbol::new(&env, "Alice")); + + // Assert that the result is equal to the Vec ["Hello", "Alice"] + assert_eq!(output_say_hello_to, vec![&env, symbol_short!("Hello"), Symbol::new(&env, "Alice")]); + + // ============================== + // Test the get_name function + // =============================== + + // Use the client to call the get_name function and store the result in the variable name + let output_get_name = client.get_name(); + + // Assert that the result is equal to the Symbol "CaptainCacti" + assert_eq!(output_get_name, Symbol::new(&env, "CaptainCacti")); + + + // ============================== + // Test the set_name and get_name_by_index functions + // =============================== + + // Use the client to call the set_name function with the argument "Bob" + client.set_name(&Symbol::new(&env, "Bob")); + + // Use the client to call the get_name_by_index function with the argument 0 and store the result in the variable name + let output_get_name_by_index = client.get_name_by_index(&0); + + // Assert that the result is equal to the Symbol "Bob" + assert_eq!(output_get_name_by_index, Symbol::new(&env, "Bob")); + + // Use the client to call the set_name function with the argument "Charlie" + client.set_name(&Symbol::new(&env, "Charlie")); + + // Use the client to call the get_name_by_index function with the argument 1 and store the result in the variable name + let output_get_name_by_index = client.get_name_by_index(&1); + + // Assert that the result is equal to the Symbol "Charlie" + assert_eq!(output_get_name_by_index, Symbol::new(&env, "Charlie")); + + // Attempts to read a non-existent name at index 2 + // This should return an 'NotFound' Symbol + let output_get_name_by_index = client.get_name_by_index(&2); + + // Assert that the result is equal to the Symbol "NotFound" + assert_eq!(output_get_name_by_index, Symbol::new(&env, "NotFound")); + + + // ============================== + // Test the deposit function + // =============================== + + // Create a new account for the token contract admin + let admin = ::generate(&env); + + // instantiate a new token contract + let token_contract_id = env.register_stellar_asset_contract(admin); + + // instantiate a new token contract admin client for the token contract + // This client will be used to mint tokens for Alice + let admin_token_contract = StellarAssetClient::new(&env, &token_contract_id); + + // instantiate a new token contract client for the token contract + // This client will be used to check the balance of accounts + let token_contract = token::TokenClient::new(&env, &token_contract_id); + + // Create a new account for Alice + let alice = ::generate(&env); + + // Mint 1000 tokens for Alice + admin_token_contract.mint(&alice, &1000); + + // Use the client to call the deposit function with the + // arguments alice, token_contract_id, and 100. + // This will deposit 100 tokens from Alice to the contract. + client.deposit(&alice, &token_contract_id, &100); + + // Assert that the balance of the contract is 100 + assert_eq!(token_contract.balance(&contract_id), 100); + + // Assert that the balance of Alice is 900 + assert_eq!(token_contract.balance(&alice), 900); + +} diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/test_snapshots/test/test.1.json b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/test_snapshots/test/test.1.json new file mode 100644 index 0000000000..6eb0832c29 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/contracts/hello_world/test_snapshots/test/test.1.json @@ -0,0 +1,1460 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [], + [], + [], + [ + [ + "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V", + { + "function": { + "contract_fn": { + "contract_address": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "function_name": "set_admin", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "function_name": "mint", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "deposit", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [], + [] + ], + "ledger": { + "protocol_version": 20, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "account": { + "account_id": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V", + "balance": 0, + "seq_num": 0, + "num_sub_entries": 0, + "inflation_dest": null, + "flags": 0, + "home_domain": "", + "thresholds": "01010101", + "signers": [], + "ext": "v0" + } + }, + "ext": "v0" + }, + null + ] + ], + [ + { + "contract_data": { + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 15 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Names" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "Bob" + }, + { + "symbol": "Charlie" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 5541220902715666415 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 15 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 15 + ] + ], + [ + { + "contract_data": { + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 100 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "amount" + }, + "val": { + "i128": { + "hi": 0, + "lo": 900 + } + } + }, + { + "key": { + "symbol": "authorized" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "clawback" + }, + "val": { + "bool": false + } + } + ] + } + } + }, + "ext": "v0" + }, + 518400 + ] + ], + [ + { + "contract_data": { + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": "stellar_asset", + "storage": [ + { + "key": { + "symbol": "METADATA" + }, + "val": { + "map": [ + { + "key": { + "symbol": "decimal" + }, + "val": { + "u32": 7 + } + }, + { + "key": { + "symbol": "name" + }, + "val": { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + } + }, + { + "key": { + "symbol": "symbol" + }, + "val": { + "string": "aaa" + } + } + ] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "AssetInfo" + } + ] + }, + "val": { + "vec": [ + { + "symbol": "AlphaNum4" + }, + { + "map": [ + { + "key": { + "symbol": "asset_code" + }, + "val": { + "string": "aaa\\0" + } + }, + { + "key": { + "symbol": "issuer" + }, + "val": { + "bytes": "0000000000000000000000000000000000000000000000000000000000000003" + } + } + ] + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 120960 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "say_hello" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "say_hello" + } + ], + "data": { + "symbol": "HelloWorld" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "say_hello_to" + } + ], + "data": { + "symbol": "Alice" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "say_hello_to" + } + ], + "data": { + "vec": [ + { + "symbol": "Hello" + }, + { + "symbol": "Alice" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_name" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_name" + } + ], + "data": { + "symbol": "CaptainCacti" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_name" + } + ], + "data": { + "symbol": "Bob" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_name" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "u32": 0 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "symbol": "Bob" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_name" + } + ], + "data": { + "symbol": "Charlie" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_name" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "u32": 1 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "symbol": "Charlie" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "u32": 2 + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "get_name_by_index" + } + ], + "data": { + "symbol": "NotFound" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "init_asset" + } + ], + "data": { + "bytes": "0000000161616100000000000000000000000000000000000000000000000000000000000000000000000003" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "init_asset" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "set_admin" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "set_admin" + }, + { + "address": "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_admin" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "mint" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "i128": { + "hi": 0, + "lo": 1000 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "mint" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 1000 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "mint" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "deposit" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CBUSYNQKASUYFWYC3M2GUEDMX4AIVWPALDBYJPNK6554BREHTGZ2IUNF" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "transfer" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "i128": { + "hi": 0, + "lo": 100 + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "transfer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "string": "aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGO6V" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "transfer" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "deposit" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "balance" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "balance" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 100 + } + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4" + }, + { + "symbol": "balance" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "692c360a04a982db02db346a106cbf008ad9e058c384bdaaf77bc0c48799b3a4", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "balance" + } + ], + "data": { + "i128": { + "hi": 0, + "lo": 900 + } + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/hello_world.wasm b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/hello_world.wasm new file mode 100755 index 0000000000000000000000000000000000000000..b101e330e9e428f7c66f459df753c194ec283dac GIT binary patch literal 2886 zcmZuz+iz4w82@JG?CxpLE}RA;1q_)}Fe!;buM~Nq=U@RtLZC5X;=}IQ?g6`OcgyZl zX;QljZ2@n9MuIUKL`@nW`sfQU5)*yU@a~(h#wYy?@b}HxON*X%XTJGv^Id0#!rGi5 zB5^X4Q;kMLG;$=Cb8>kZqu@a;$8dvq-N;EWfx)oK#xgN48Cl8^$+x$Q1(79zaf~F! zQBoL(0f%A;@HH_3Y5_bJ@Rxv}6-R-e1)j$vz~n^{V+s5`vTTX@RsXJpOsYgGWs8*R z5vo@?R*y`l(^f*v3#AfDP+xMlY6!pCd_qox{0Gn2*Lst4(OjjvD5<9wE@t0} zN~H?zN_XIFy+XU~OHn;r4(B4;YuDP~i+ycuHousiEf=C|v@cbN<}0kwq2)%S`Nq)ax#FVd#~AS(tvb7;dv@U zf$)Tu?xzN=f@fkS_>InIkS0fyRujs`BqkI&*Mw;72V0YT_ws-|U_d!3CpDeRkbl4V z1eBEf8FP4~B}>_u2*O(W(!C#3(!#;=o?%bK>@kOEb1>a4#v5pIwv(nnx*xH4AkI7K zc>OW29ZAkFLW*Up0Yik}a|SMvhs`~u$+P67n$&74BYfc{z+t^YG?4WFppzNGGU=Zy z`p?7_zxVal`o~6$*aHN}4_nLKXEF&ar;1)8Ege8-7=kBBe;N=h4w5%@VuJ&?itUx( zD8d5E7e&uXlfw-5&0yQ=c7$~!&^H5XRf`tjerR0T1Hb-5x@AcFkb^Xwlg<$c|f!RD>S)ZFgM~L4k}uQ!I(DU1hwGfI0qJi$1-4)5)RhfLu>0Q|_tP!+-|IT8 zH#yA4ZSmP%M+aaR_Pd|`cJFdPIJ~BY5DgayVB}s%UT4rleE~Log!!vj0Pe+M4Wy}8 zgiyM7n6`)OQ{o(J^-ebfaqpOxv!*%tet)rRKtD$q0f%Y2W5j@RZ?Mup-$qa9+iw{$ zyQop@!73>8EA9$2#F;huT+b+;U5`+&{tuh$#ADaDqyoY4e_|Hk4;T4lgRY0Hk(?fK zzl))#N{NPr6T0h@irt67WITFG#-&ZLf_pe=9rMP~30WjyS>Y8pEFS@0td*<2v(_ha5A~2`SRgw#@sJcgv}`ciN7bb6g+Ev^d{`U z-x|orZ)vf%8kp)LK2jY%Qn8!+0i-q;sYP%N*Msv&;65~W^aDWg$Fs!mENqO-Hq>rTVdBSIh5K z!}-C3IbA91u&i@a)oM;Jgr%zy^gqAfiAs4Ps@7&JWw2GM^=eqIU5u*qKy90;l(C$t z*XE;{g`t*xn9P@Y4VV3yXpt9ecl%2i2jW$GuT;w)#xib4Og7`Dmq-W8Jbj%N(bgAA z*9VFB3V+NC8TD5SW*mRrr%b$lUT^n()75F2r_a{?S^9q-Kt?I+;c0BB`#xJhf!X{iYoI{f9MgpyYZ(mM!H50B;Z^e^;=P-OrB literal 0 HcmV?d00001 diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/spec.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/spec.ts new file mode 100644 index 0000000000..057e1856e9 --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/spec.ts @@ -0,0 +1,36 @@ +/* eslint-disable */ + +// spell-checker:disable +export const spec = [ + "AAAAAgAAAAAAAAAAAAAAB0RhdGFLZXkAAAAAAgAAAAAAAAAAAAAABU5hbWVzAAAAAAAAAQAAAAAAAAAHQmFsYW5jZQAAAAABAAAAEw==", + "AAAAAAAAAAAAAAAJc2F5X2hlbGxvAAAAAAAAAAAAAAEAAAAR", + "AAAAAAAAAAAAAAAMc2F5X2hlbGxvX3RvAAAAAQAAAAAAAAACdG8AAAAAABEAAAABAAAD6gAAABE=", + "AAAAAAAAAAAAAAAIZ2V0X25hbWUAAAAAAAAAAQAAABE=", + "AAAAAAAAAAAAAAAIc2V0X25hbWUAAAABAAAAAAAAAARuYW1lAAAAEQAAAAA=", + "AAAAAAAAAAAAAAARZ2V0X25hbWVfYnlfaW5kZXgAAAAAAAABAAAAAAAAAAVpbmRleAAAAAAAAAQAAAABAAAAEQ==", + "AAAAAAAAAAAAAAAHZGVwb3NpdAAAAAADAAAAAAAAAARmcm9tAAAAEwAAAAAAAAAIYXNzZXRfaWQAAAATAAAAAAAAAAZhbW91bnQAAAAAAAsAAAAA", +]; +// spell-checker:enable + +export enum methods { + sayHello = "say_hello", + sayHelloTo = "say_hello_to", + getName = "get_name", + setName = "set_name", + getNameByIndex = "get_name_by_index", + deposit = "deposit", +} + +export type SayHelloArgs = {}; +export type SayHelloToArgs = { to: string }; +export type GetNameArgs = {}; +export type SetNameArgs = { name: string }; +export type GetNameByIndexArgs = { index: number }; +export type DepositArgs = { from: string; asset_id: string; amount: number }; + +export type SayHelloResponse = string; +export type SayHelloToResponse = string[]; +export type GetNameResponse = string; +export type SetNameResponse = null; +export type GetNameByIndexResponse = string; +export type DepositResponse = null; diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm b/packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm deleted file mode 100644 index 82f8e2ce0f7dd4781ead9066a8b52a4a62e341bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7456 zcmc&(eT-aH6~FhsH?y;SJKa}U&C(jYGsU;U^Q zGt2I@1+(o8bhlK6QjscxLPZ2MmiQHjG0{~+3{jHCgpl}$_=oZbi6&~|#~+HnbMC$G z&CV_$F~n*5-o5AEd+z!8opbKHD@u!1DTI(M`OXRdIDYc@gyN@XI+7>?|7BB5_|4-@ zykwJRHRL}M<6^>V9>+6-*+fxIbOgpTv;znNtf+~J{KRUZZtj(Has;x> z$z4L?IWCke$wA@CJD74r-U|A(90g?>GwC@D%A}mZvnuyuepQyh;SRZ5_~0Fcug6# zTB{;xis;PUZSSt{$t~GxrCMLOQ;AK}wduxmX>R&c74crbI$djsf$qghW4ttlj<~$L zG&fg2TB?;R;>xbl{QN@wNJZ@Ink>yR?W$a3p;TMErLrIf^R795%R;>>hWyFH3pKI3 zn_h;rCpT3oPghHGi(=TXm8umnnq9oJI$56+dttC^HjR%@l^Uh-N^Q!M!T9)XmD2q9 zWNESDsZGxfT+t`Ah@sRkFO6Eies_4L)oO|0Js^gkqo$Txosad&*k9J3ZG;$TiLJrE zr6Rv?=WZ0v7UgB!RAcEsEe4d>oDouaz6he#yH;Bw>JA?u4h047A_`}Acw)rMQD=vj z8}Woz(J7vY36TmvPm{w3&5|BJh@R)cF2jWvC8J&tUEtBoMx!c-UVQyg8DP8Lw2zZ4 zGLqq0vp0>0Ge$|Rf@h>Bn+hn8ph_$X9;xk(K@W9Z9*?N|y1a^I!B=A%I?Y@uL@^V+ zEUwE7ojK3sOtgO{+VqjUz?4k%ljcmc6&*AmL-Ue-`Hc4CYnKXsj|$M*M=eO{$2X!C zUM{pJGCbb7SglL zDVy8Tg6H65<@DE*oTfB}qMZ>_S_2>^4GBK?fr9ch8 z!t+(EPd!!i_Z73-3Xo7|G#UUY>`SNsAV_7Q{b3bi>8h|^=ndeNA*c@E_zr;D0WL`~ zNzw^F%hnD~m|SB`CZCF4leZUD^c)2fF^hr^0Tz@@V6YF|V>Xv@4y_yokBCoDY9rwg zk&;LKXDU9u9hq8BCS@IVJHq}WUOG>z#S<+JcWg+-GL?! zm*4^EQ@v@cIfzyNGTH``0sb27!-uI$>c9X+Sm;nOi$N|j#V+j~)OZ}&W>C}aC`v0N z9+n)VImdMN{4wnhsQ&O#Vu`eyM0{`H-1rFLQn?y9*p?n+U=3m5f*wI)3WQcR-u+mn z2~BY*gTh(O2BkuLa!@J&0p!W(McpXE1K4ygZL>>{udluZD&iAm7ujMhAt5eRPkCcYX zp-PICmt^XX6uD{)Q-!T!CLE}uA^8G5On#oD#A|E&rZ5ra4VYYQM8X!~m)L4H1obCD z!wG?04x3YYfzUCgwC1tPG9|>*4?XtE&;R+;!RQ}cDm)IR$TJ78wBn`sj}_-}XNX_C zP7rh`#u%4tu?-!m;K2O`;zciflEe?cK+mEdo}l;E@SF6~6rW!B1h@I%F+gR6V65WP zfBF7PJ)|XV6Ml>40MW`I+GR=JE)18MDFz*gtwP&GOhc`6f3S@+e-q(po*X=BD$sTs z7ZKt~8caeY99u&&EVpogwMSX1@ezdZj*}sxepD#xsxgndRKX>My`Y#A<`fJaA#p)) z9j8&a!Ja-b*@>%Rf4A0D|8&(2Qu2NlQ3vt@Y1x`nc9_#ZB|iPi(?5Lm-rv7CiuCzy zQV$?9CW=+?piJu=P`lyr4{CX=B|_C`>9LmRnv*cfvWH_39tc9pixhzNz^V}5Yfg{u zT%{$nq){w0TBfTSX(+)JRwy#_xlE2&Fug(<@OA0Z!oV>iEJ>W=;xA8=MPhq zqL(>h?Wa%ym5%Do0A8HjQLZ`e+arPtC9yuoo~DlnG+Z(m#Zt%tI1g$ns)LDZT+sOG zVa`U)WaYkl4FOpiSl(TC$*p8Hsv7deV2P)BBeFPt*J2p z%;a>k)-CbrKfQYP$ItxsukaGma_Sziri7Iq>p*~8YTdk`fr#p4nIF3yYPwR$} zm13D>#U3Q9mwJ*8^rncoxFL#>4xxwk4_Imn`peM5f!@@!kEXs$PZ7wD&;F|$+&MD> zm|y_+pJNv20a?yH=|ScH0a!@^3(1^II^k=SUI*CQ+mCOEEMZKd#?)AHFmT5UJ|nkN z&1v;nV+(j=e_m3eq-r;_N0DNP?`$M{54)U`c1IoK#Q zDiPh@+Ur_-=Sugj_OkUAyU!o-rhWfx`HCNH+FY*JSTV>}J~D_w_iMi?CK@fIU&h=N zbNga`3ejcvq^_H1E_?;HGCz0oOFc)kL&97{3!yAQHR{}Q{IkYFOhXW_`L*3%GhN!z z?po!jz13!l`M~K<6o-9k(^xy++vA{p-1(7cVG`*d#NS8uRqh1s zt>BY*&~J0X=cXj7IX)Zd_eS`0>xgexDwfEg?_}>z_M=%$@4A5ZM*6rlT{fB7`ALjX zKf@W0T4oQ2ZR%$@@lh)sGd+xPJ~TbI5B2hg>a&&F^_516{_bSP6HL729Cqmzmpm)A zBULa>@@F5jt|!TL&0uC>5#_^dp53)`GUOh52H8L_eTrTb2GdWbA+AID8UtASU diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts index a371872212..4084563ba3 100644 --- a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/deploy-contract/index.test.ts @@ -40,8 +40,10 @@ describe(testCaseName, () => { const wasmHashPattern = /^[a-f0-9]{64}$/; beforeAll(async () => { + // Load the WASM file for the + // `hello_world`example contract. wasmBuffer = await loadWasmFile( - "./packages/cacti-plugin-ledger-connector-stellar/src/test/rust/token-contract/soroban_token_contract.wasm", + "./packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/hello_world.wasm", ); expect(wasmBuffer).toBeDefined(); diff --git a/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/run-soroban-transaction/index.test.ts b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/run-soroban-transaction/index.test.ts new file mode 100644 index 0000000000..f4338040ec --- /dev/null +++ b/packages/cacti-plugin-ledger-connector-stellar/src/test/typescript/integration/plugin-ledger-connector-stellar/run-soroban-transaction/index.test.ts @@ -0,0 +1,320 @@ +import { AddressInfo } from "net"; +import { pluginName } from ".."; +import { + StellarApiClient, + StellarApiClientOptions, +} from "../../../../../main/typescript/api-client/stellar-api-client"; +import { + IListenOptions, + LogLevelDesc, + Servers, +} from "@hyperledger/cactus-common"; +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { PluginFactoryLedgerConnector } from "../../../../../main/typescript/plugin-factory-ledger-connector"; +import { loadWasmFile } from "../../../../../main/typescript/utils"; +import { StellarTestLedger } from "@hyperledger/cactus-test-tooling"; +import { NetworkConfig } from "stellar-plus/lib/stellar-plus/network"; +import { PluginLedgerConnectorStellar } from "../../../../../main/typescript/plugin-ledger-connector-stellar"; +import http from "http"; +import { Network } from "stellar-plus/lib/stellar-plus"; +import { Constants, PluginImportType } from "@hyperledger/cactus-core-api"; +import { v4 as uuidV4 } from "uuid"; +import express from "express"; +import { Server as SocketIoServer } from "socket.io"; +import bodyParser from "body-parser"; +import { DefaultAccountHandler } from "stellar-plus/lib/stellar-plus/account"; +import { K_CACTUS_STELLAR_TOTAL_TX_COUNT } from "../../../../../main/typescript/prometheus-exporter/metrics"; +import { + GetNameArgs, + GetNameByIndexArgs, + SayHelloArgs, + SayHelloToArgs, + SayHelloToResponse, + SetNameArgs, + spec as helloWorldSpec, + methods, +} from "../../../../rust/demo-contract/spec"; + +const testCaseName = pluginName + " / run soroban transactions"; +const runSorobanTransactionFnTag = `PluginLedgerConnectorStellar#invokeContract()`; + +describe(testCaseName, () => { + const logLevel: LogLevelDesc = "TRACE"; + const stellarTestLedger = new StellarTestLedger({ logLevel }); + let networkConfig: NetworkConfig; + let wasmBuffer: Buffer; + let connector: PluginLedgerConnectorStellar; + let server: http.Server; + let apiClient: StellarApiClient; + const contractIdPattern = /^C[A-Z0-9]{55}$/; + const wasmHashPattern = /^[a-f0-9]{64}$/; + + beforeAll(async () => { + // Load the WASM file for the + // `hello_world`example contract. + wasmBuffer = await loadWasmFile( + "./packages/cacti-plugin-ledger-connector-stellar/src/test/rust/demo-contract/hello_world.wasm", + ); + expect(wasmBuffer).toBeDefined(); + + await stellarTestLedger.start(); + networkConfig = Network.CustomNet( + await stellarTestLedger.getNetworkConfiguration(), + ); + + expect(networkConfig.horizonUrl).toBeDefined(); + expect(networkConfig.networkPassphrase).toBeDefined(); + expect(networkConfig.rpcUrl).toBeDefined(); + expect(networkConfig.friendbotUrl).toBeDefined(); + + const factory = new PluginFactoryLedgerConnector({ + pluginImportType: PluginImportType.Local, + }); + + connector = await factory.create({ + networkConfig, + pluginRegistry: new PluginRegistry({}), + instanceId: uuidV4(), + }); + + await connector.onPluginInit(); + + expect(connector).toBeInstanceOf(PluginLedgerConnectorStellar); + + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + server = http.createServer(expressApp); + + const wsApi = new SocketIoServer(server, { + path: Constants.SocketIoConnectionPathV1, + }); + + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + + const { address, port } = addressInfo; + const apiHost = `http://${address}:${port}`; + console.log( + `Metrics URL: ${apiHost}/api/v1/plugins/@hyperledger/cactus-plugin-ledger-connector-besu/get-prometheus-exporter-metrics`, + ); + const stellarApiClientOptions = new StellarApiClientOptions({ + basePath: apiHost, + }); + apiClient = new StellarApiClient(stellarApiClientOptions); + await connector.getOrCreateWebServices(); + await connector.registerWebServices(expressApp, wsApi); + }); + + afterAll(async () => { + await stellarTestLedger.stop(); + await stellarTestLedger.destroy(); + await Servers.shutdown(server); + }); + + describe("before deploy", () => { + it("should fail to invoke a contract Id that isn't deployed yet", async () => { + const invokerAccount = new DefaultAccountHandler({ networkConfig }); + await invokerAccount.initializeWithFriendbot(); + + await expect( + connector.runSorobanTransaction({ + contractId: + "CBHYOXSMPOW7PF7OGDCOSMZT45356INSNX4DGWDRUAMTAZMZ5DXZEAAU", + method: methods.sayHello, + methodArgs: {} as SayHelloArgs, + specXdr: helloWorldSpec, + readOnly: false, + transactionInvocation: { + header: { + source: invokerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [invokerAccount.getSecretKey()], + }, + }), + ).rejects.toThrow( + `${runSorobanTransactionFnTag} Failed to invoke contract. `, + ); + }); + + it("should fail to read from a contract Id that isn't deployed yet", async () => { + const invokerAccount = new DefaultAccountHandler({ networkConfig }); + await invokerAccount.initializeWithFriendbot(); + + await expect( + connector.runSorobanTransaction({ + contractId: + "CBHYOXSMPOW7PF7OGDCOSMZT45356INSNX4DGWDRUAMTAZMZ5DXZEAAU", + method: methods.sayHello, + methodArgs: {} as SayHelloArgs, + specXdr: helloWorldSpec, + readOnly: true, + transactionInvocation: { + header: { + source: invokerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [invokerAccount.getSecretKey()], + }, + }), + ).rejects.toThrow( + `${runSorobanTransactionFnTag} Failed to read contract. `, + ); + }); + }); + + describe("after deploy", () => { + let contractId: string; + let adminAccount: DefaultAccountHandler; + + beforeAll(async () => { + const deployerAccount = new DefaultAccountHandler({ networkConfig }); + await deployerAccount.initializeWithFriendbot(); + + const res = await connector.deployContract({ + wasmBuffer: wasmBuffer.toString("base64"), + transactionInvocation: { + header: { + source: deployerAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [deployerAccount.getSecretKey()], + }, + }); + + adminAccount = new DefaultAccountHandler({ networkConfig }); + + expect(res).toBeDefined(); + expect(res.contractId).toMatch(contractIdPattern); + expect(res.wasmHash).toMatch(wasmHashPattern); + await expect( + adminAccount.initializeWithFriendbot(), + ).resolves.toBeUndefined(); + + contractId = res.contractId as string; + }); + + it("should not submit a failing transaction", async () => { + // Try to invoke a method with the wrong args + await expect( + connector.runSorobanTransaction({ + contractId, + method: methods.sayHelloTo, + methodArgs: { + index: 0, + } as GetNameByIndexArgs, + specXdr: helloWorldSpec, + readOnly: false, + transactionInvocation: { + header: { + source: adminAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [adminAccount.getSecretKey()], + }, + }), + ).rejects.toThrow( + `${runSorobanTransactionFnTag} Failed to invoke contract. `, + ); + }); + + it("should invoke a contract function to alter the ledger state", async () => { + const res = await connector.runSorobanTransaction({ + contractId, + method: methods.setName, + methodArgs: { + name: "Fifo", + } as SetNameArgs, + specXdr: helloWorldSpec, + readOnly: false, + transactionInvocation: { + header: { + source: adminAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [adminAccount.getSecretKey()], + }, + }); + + expect(res).toBeDefined(); + expect(res).toHaveProperty("result"); + }); + + it("should invoke a contract function and return the output", async () => { + const res = await connector.runSorobanTransaction({ + contractId, + method: methods.getName, + methodArgs: {} as GetNameArgs, + specXdr: helloWorldSpec, + readOnly: false, + transactionInvocation: { + header: { + source: adminAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [adminAccount.getSecretKey()], + }, + }); + + expect(res).toBeDefined(); + expect(res).toHaveProperty("result"); + expect(res.result).toEqual("CaptainCacti"); + }); + + it("should simulate a contract function invocation to read a state from the ledger", async () => { + const expected = ["Hello", "Fifo"] as SayHelloToResponse; + + const res = await connector.runSorobanTransaction({ + contractId, + method: methods.sayHelloTo, + methodArgs: { + to: "Fifo", + } as SayHelloToArgs, + specXdr: helloWorldSpec, + readOnly: true, + transactionInvocation: { + header: { + source: adminAccount.getPublicKey(), + fee: 100, + timeout: 30, + }, + signers: [adminAccount.getSecretKey()], + }, + }); + expect(res).toBeDefined(); + expect(res).toHaveProperty("result"); + expect(res.result).toEqual(expected); + }); + }); + + describe("Prometheus", () => { + it("should provide transaction metrics", async () => { + const promMetricsOutput = + "# HELP " + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + " Total transactions executed\n" + + "# TYPE " + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + " gauge\n" + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + '{type="' + + K_CACTUS_STELLAR_TOTAL_TX_COUNT + + '"} 4'; + + const res = await apiClient.getPrometheusMetricsV1(); + + expect(res).toBeDefined(); + expect(res.data.includes(promMetricsOutput)).toBe(true); + }); + }); +});