diff --git a/bindings/core/src/method/client.rs b/bindings/core/src/method/client.rs index 782ea42956..2bac83685a 100644 --- a/bindings/core/src/method/client.rs +++ b/bindings/core/src/method/client.rs @@ -125,6 +125,15 @@ pub enum ClientMethod { /// Returns the unhealthy nodes. #[cfg(not(target_family = "wasm"))] UnhealthyNodes, + /// Extension method which provides request methods for plugins. + #[serde(rename_all = "camelCase")] + CallPluginRoute { + base_plugin_path: String, + method: String, + endpoint: String, + query_params: Vec, + request_object: Option, + }, /// Prepare a transaction for signing #[serde(rename_all = "camelCase")] PrepareTransaction { diff --git a/bindings/core/src/method/utils.rs b/bindings/core/src/method/utils.rs index 4dd0c3db95..0110708758 100644 --- a/bindings/core/src/method/utils.rs +++ b/bindings/core/src/method/utils.rs @@ -3,11 +3,14 @@ use derivative::Derivative; use iota_sdk::types::block::{ - address::{AliasAddress, Bech32Address, Hrp}, - output::{AliasId, NftId, OutputId}, + address::{Bech32Address, Hrp}, + output::{dto::OutputDto, AliasId, NftId, OutputId, RentStructure, TokenScheme}, payload::{ dto::MilestonePayloadDto, - transaction::dto::{TransactionEssenceDto, TransactionPayloadDto}, + transaction::{ + dto::{TransactionEssenceDto, TransactionPayloadDto}, + TransactionId, + }, }, signature::dto::Ed25519SignatureDto, BlockDto, @@ -97,27 +100,40 @@ pub enum UtilsMethod { /// Output ID output_id: OutputId, }, + /// Computes the Foundry ID + #[serde(rename_all = "camelCase")] + ComputeFoundryId { + /// Alias id + alias_id: AliasId, + /// Serial number + serial_number: u32, + /// Token scheme kind + token_scheme_kind: u8, + }, /// Computes the NFT ID #[serde(rename_all = "camelCase")] ComputeNftId { /// Output ID output_id: OutputId, }, - /// Computes the Foundry ID + /// Computes the output ID from transaction id and output index + ComputeOutputId { id: TransactionId, index: u16 }, + /// Computes a tokenId from the aliasId, serial number and token scheme type. #[serde(rename_all = "camelCase")] - ComputeFoundryId { - /// Alias address - alias_address: AliasAddress, - /// Serial number + ComputeTokenId { + alias_id: AliasId, serial_number: u32, - /// Token scheme kind - token_scheme_kind: u8, + token_scheme_type: TokenScheme, }, - /// Compute the hash of a transaction essence. + /// Computes the hash of a transaction essence. HashTransactionEssence { /// The transaction essence essence: TransactionEssenceDto, }, + /// Computes the input commitment from the output objects that are used as inputs to fund the transaction. + ComputeInputsCommitment { inputs: Vec }, + /// Computes the required storage deposit of an output. + ComputeStorageDeposit { output: OutputDto, rent: RentStructure }, /// Checks if the given mnemonic is valid. /// Expected response: [`Ok`](crate::Response::Ok) VerifyMnemonic { diff --git a/bindings/core/src/method_handler/client.rs b/bindings/core/src/method_handler/client.rs index 37e15673a0..dc2ceb840d 100644 --- a/bindings/core/src/method_handler/client.rs +++ b/bindings/core/src/method_handler/client.rs @@ -304,7 +304,7 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM ClientMethod::GetBlockMetadata { block_id } => { Response::BlockMetadata(client.get_block_metadata(&block_id).await?) } - ClientMethod::GetBlockRaw { block_id } => Response::BlockRaw(client.get_block_raw(&block_id).await?), + ClientMethod::GetBlockRaw { block_id } => Response::Raw(client.get_block_raw(&block_id).await?), ClientMethod::GetOutput { output_id } => Response::OutputWithMetadataResponse( client .get_output(&output_id) @@ -318,13 +318,13 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM &client.get_milestone_by_id(&milestone_id).await?, )), ClientMethod::GetMilestoneByIdRaw { milestone_id } => { - Response::MilestoneRaw(client.get_milestone_by_id_raw(&milestone_id).await?) + Response::Raw(client.get_milestone_by_id_raw(&milestone_id).await?) } ClientMethod::GetMilestoneByIndex { index } => { Response::Milestone(MilestonePayloadDto::from(&client.get_milestone_by_index(index).await?)) } ClientMethod::GetMilestoneByIndexRaw { index } => { - Response::MilestoneRaw(client.get_milestone_by_index_raw(index).await?) + Response::Raw(client.get_milestone_by_index_raw(index).await?) } ClientMethod::GetUtxoChangesById { milestone_id } => { Response::MilestoneUtxoChanges(client.get_utxo_changes_by_id(&milestone_id).await?) @@ -459,6 +459,18 @@ pub(crate) async fn call_client_method_internal(client: &Client, method: ClientM ClientMethod::RequestFundsFromFaucet { url, address } => { Response::Faucet(request_funds_from_faucet(&url, &address).await?) } + ClientMethod::CallPluginRoute { + base_plugin_path, + method, + endpoint, + query_params, + request_object, + } => { + let data: serde_json::Value = client + .call_plugin_route(&base_plugin_path, &method, &endpoint, query_params, request_object) + .await?; + Response::CustomJson(data) + } }; Ok(response) } diff --git a/bindings/core/src/method_handler/utils.rs b/bindings/core/src/method_handler/utils.rs index 8cf2072492..890a92bcf9 100644 --- a/bindings/core/src/method_handler/utils.rs +++ b/bindings/core/src/method_handler/utils.rs @@ -4,8 +4,8 @@ use iota_sdk::{ client::{hex_public_key_to_bech32_address, hex_to_bech32, verify_mnemonic, Client}, types::block::{ - address::{dto::AddressDto, Address, ToBech32Ext}, - output::{AliasId, FoundryId, NftId}, + address::{dto::AddressDto, Address, AliasAddress, ToBech32Ext}, + output::{AliasId, FoundryId, InputsCommitment, NftId, Output, OutputId, Rent, TokenId}, payload::{transaction::TransactionEssence, MilestonePayload, TransactionPayload}, signature::Ed25519Signature, Block, @@ -48,15 +48,39 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result Response::AliasId(AliasId::from(&output_id)), - UtilsMethod::ComputeNftId { output_id } => Response::NftId(NftId::from(&output_id)), UtilsMethod::ComputeFoundryId { - alias_address, + alias_id, + serial_number, + token_scheme_kind, + } => Response::FoundryId(FoundryId::build( + &AliasAddress::new(alias_id), serial_number, token_scheme_kind, - } => Response::FoundryId(FoundryId::build(&alias_address, serial_number, token_scheme_kind)), - UtilsMethod::HashTransactionEssence { essence } => Response::TransactionEssenceHash(prefix_hex::encode( + )), + UtilsMethod::ComputeNftId { output_id } => Response::NftId(NftId::from(&output_id)), + UtilsMethod::ComputeOutputId { id, index } => Response::OutputId(OutputId::new(id, index)?), + UtilsMethod::ComputeTokenId { + alias_id, + serial_number, + token_scheme_type, + } => { + let foundry_id = FoundryId::build(&AliasAddress::new(alias_id), serial_number, token_scheme_type.kind()); + Response::TokenId(TokenId::from(foundry_id)) + } + UtilsMethod::HashTransactionEssence { essence } => Response::Hash(prefix_hex::encode( TransactionEssence::try_from_dto_unverified(essence)?.hash(), )), + UtilsMethod::ComputeInputsCommitment { inputs } => { + let inputs = inputs + .into_iter() + .map(|o| Ok(Output::try_from_dto_unverified(o)?)) + .collect::>>()?; + Response::Hash(InputsCommitment::new(inputs.iter()).to_string()) + } + UtilsMethod::ComputeStorageDeposit { output, rent } => { + let out = Output::try_from_dto_unverified(output)?; + Response::MinimumRequiredStorageDeposit(out.rent_cost(&rent).to_string()) + } UtilsMethod::VerifyMnemonic { mut mnemonic } => { verify_mnemonic(&mnemonic)?; mnemonic.zeroize(); diff --git a/bindings/core/src/response.rs b/bindings/core/src/response.rs index adeb1d37a6..e04f93b997 100644 --- a/bindings/core/src/response.rs +++ b/bindings/core/src/response.rs @@ -26,7 +26,7 @@ use iota_sdk::{ input::dto::UtxoInputDto, output::{ dto::{OutputDto, OutputMetadataDto}, - AliasId, FoundryId, NftId, OutputId, + AliasId, FoundryId, NftId, OutputId, TokenId, }, payload::{ dto::{MilestonePayloadDto, PayloadDto}, @@ -135,7 +135,9 @@ pub enum Response { BlockMetadata(BlockMetadataResponse), /// Response for: /// - [`GetBlockRaw`](crate::method::ClientMethod::GetBlockRaw) - BlockRaw(Vec), + /// - [`GetMilestoneByIdRaw`](crate::method::ClientMethod::GetMilestoneByIdRaw) + /// - [`GetMilestoneByIndexRaw`](crate::method::ClientMethod::GetMilestoneByIndexRaw) + Raw(Vec), /// Response for: /// - [`GetOutput`](crate::method::ClientMethod::GetOutput) OutputWithMetadataResponse(OutputWithMetadataResponse), @@ -152,10 +154,6 @@ pub enum Response { /// - [`GetMilestoneByIndex`](crate::method::ClientMethod::GetMilestoneByIndex) Milestone(MilestonePayloadDto), /// Response for: - /// - [`GetMilestoneByIdRaw`](crate::method::ClientMethod::GetMilestoneByIdRaw) - /// - [`GetMilestoneByIndexRaw`](crate::method::ClientMethod::GetMilestoneByIndexRaw) - MilestoneRaw(Vec), - /// Response for: /// - [`GetUtxoChangesById`](crate::method::ClientMethod::GetUtxoChangesById) /// - [`GetUtxoChangesByIndex`](crate::method::ClientMethod::GetUtxoChangesByIndex) MilestoneUtxoChanges(MilestoneUTXOChanges), @@ -210,6 +208,9 @@ pub enum Response { /// - [`MilestoneId`](crate::method::UtilsMethod::MilestoneId) MilestoneId(MilestoneId), /// Response for: + /// - [`TokenId`](crate::method::UtilsMethod::TokenId) + TokenId(TokenId), + /// Response for: /// - [`TransactionId`](crate::method::UtilsMethod::TransactionId) TransactionId(TransactionId), /// Response for: @@ -223,11 +224,14 @@ pub enum Response { FoundryId(FoundryId), /// Response for: /// - [`HashTransactionEssence`](crate::method::UtilsMethod::HashTransactionEssence) - TransactionEssenceHash(String), + /// - [`ComputeInputsCommitment`](crate::method::UtilsMethod::ComputeInputsCommitment) + Hash(String), /// Response for [`GetNodeInfo`](crate::method::ClientMethod::GetNodeInfo) NodeInfoWrapper(NodeInfoWrapper), /// Response for [`Bech32ToHex`](crate::method::UtilsMethod::Bech32ToHex) HexAddress(String), + /// Response for [`CallPluginRoute`](crate::method::ClientMethod::CallPluginRoute) + CustomJson(serde_json::Value), // Responses in client and wallet /// Response for: @@ -308,6 +312,7 @@ pub enum Response { AddressesWithUnspentOutputs(Vec), /// Response for: /// - [`MinimumRequiredStorageDeposit`](crate::method::AccountMethod::MinimumRequiredStorageDeposit) + /// - [`ComputeStorageDeposit`](crate::method::UtilsMethod::ComputeStorageDeposit) MinimumRequiredStorageDeposit(String), /// Response for: /// - [`ClaimableOutputs`](crate::method::AccountMethod::ClaimableOutputs) diff --git a/bindings/nodejs/CHANGELOG.md b/bindings/nodejs/CHANGELOG.md index 241bc6d960..b9be9f866a 100644 --- a/bindings/nodejs/CHANGELOG.md +++ b/bindings/nodejs/CHANGELOG.md @@ -23,6 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `callPluginRoute` to Client to fetch data from custom node plugins; +- `computeTokenId `, `computeOutputId`, `computeInputsCommitment` and `computeStorageDeposit` to Utils; +- Type alias for Ids which were previously just `HexEncodedString`; +- List of `ConfictReason` explanations matching the enum; +- `units-helper` class for IOTA units conversion; - `Client::destroy` to close an open handle; ### Changed @@ -32,12 +37,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `MintTokenTransaction` to `CreateNativeTokenTransaction` and `PreparedMintTokenTransaction` to `PreparedCreateNativeTokenTransaction` (including their corresponding `Data` types); - Rename `SendAmountParams` to `SendParams`; - Rename `Account::sendAmount` to `send`, `Account::prepareSendAmount` to `prepareSend`; +- Rename `Response::MilestoneRaw` to `Raw`; ### Fixed - Moved `internal` field from `IGenerateAddressesOptions` to `IGenerateAddressOptions`; - Error handling in `Client`, `SecretManager` and `Wallet` constructors; - Deadlock in .sync() with incoming transactions; +- Renamed `Output.getNntId` to `Output.getNftId`; ## 1.0.0-rc.1 - 2023-06-19 diff --git a/bindings/nodejs/examples/client/10-mqtt.ts b/bindings/nodejs/examples/client/10-mqtt.ts index 156599ec3b..5f1ff087cd 100644 --- a/bindings/nodejs/examples/client/10-mqtt.ts +++ b/bindings/nodejs/examples/client/10-mqtt.ts @@ -1,7 +1,15 @@ // Copyright 2021-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Client, initLogger } from '@iota/sdk'; +import { + Block, + Client, + initLogger, + MilestonePayload, + parsePayload, +} from '@iota/sdk'; +import { plainToInstance } from 'class-transformer'; + require('dotenv').config({ path: '.env' }); // Run with command: @@ -24,14 +32,35 @@ async function run() { const topics = ['blocks']; const callback = function (error: Error, data: string) { - console.log(JSON.parse(data)); + if (error != null) { + console.log(error); + return; + } + + const parsed = JSON.parse(data); + if (parsed.topic == 'milestone') { + const payload = parsePayload( + JSON.parse(parsed.payload), + ) as MilestonePayload; + const index = payload.index; + const previousMilestone = payload.previousMilestoneId; + console.log( + 'New milestone index' + + index + + ', previous ID: ' + + previousMilestone, + ); + } else if (parsed.topic == 'blocks') { + const block = plainToInstance(Block, JSON.parse(parsed.payload)); + console.log('payload:', block.payload); + } }; await client.listen(topics, callback); // Clear listener after 10 seconds setTimeout(async () => { - await client.clearListeners(['blocks']); + await client.clearListeners(topics); console.log('Listener cleared'); // Exit the process setTimeout(async () => process.exit(0), 2000); diff --git a/bindings/nodejs/examples/client/16-custom-plugin.ts b/bindings/nodejs/examples/client/16-custom-plugin.ts new file mode 100644 index 0000000000..ecf441d9c8 --- /dev/null +++ b/bindings/nodejs/examples/client/16-custom-plugin.ts @@ -0,0 +1,43 @@ +// Copyright 2021-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Client, initLogger } from '@iota/sdk'; +require('dotenv').config({ path: '.env' }); + +// Run with command: +// yarn run-example ./client/16-custom-plugin.ts + +// In this example we will get output from a known nft by calling the node endpoint using a "custom plugin" call. +async function run() { + initLogger(); + if (!process.env.NODE_URL) { + throw new Error('.env NODE_URL is undefined, see .env.example'); + } + + const client = new Client({ + // Insert your node URL in the .env. + nodes: [process.env.NODE_URL], + localPow: true, + }); + + try { + // Get an NFT id from ./how_tos/nfts/mint_nft.ts + const nftId = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const route = 'outputs/nft/' + nftId; + + // Call our "custom" indexer plugin + const outputId = await client.callPluginRoute( + 'api/indexer/v1/', + 'GET', + route, + undefined, + undefined, + ); + console.log('Output id: ', outputId); + } catch (error) { + console.error('Error: ', error); + } +} + +run().then(() => process.exit()); diff --git a/bindings/nodejs/examples/package.json b/bindings/nodejs/examples/package.json index 1cdb83f6eb..67dcbc115a 100644 --- a/bindings/nodejs/examples/package.json +++ b/bindings/nodejs/examples/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@iota/sdk": "link:../", + "class-transformer": "^0.5.1", "dotenv": "^16.0.0", "ts-node": "^10.9.1" }, diff --git a/bindings/nodejs/examples/yarn.lock b/bindings/nodejs/examples/yarn.lock new file mode 100644 index 0000000000..933feffcb2 --- /dev/null +++ b/bindings/nodejs/examples/yarn.lock @@ -0,0 +1,389 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@iota/sdk@link:..": + version "0.0.0" + uid "" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/node@^18.15.12": + version "18.16.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" + integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.4.1: + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +cargo-cp-artifact@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz#353814f49f6aa76601a4bcb3ea5f3071180b90de" + integrity sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA== + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +class-transformer@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dotenv@^16.0.0: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +node-abi@^3.3.0: + version "3.45.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.45.0.tgz#f568f163a3bfca5aacfce1fbeee1fa2cc98441f5" + integrity sha512-iwXuFrMAcFVi/ZoZiqq8BzAdsLw9kxDfTC0HMyjXfSL/6CSDAGD5UmR7azrAgWV1zKYq7dUUMj4owusBWKLsiQ== + dependencies: + semver "^7.3.5" + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^7.3.5: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +typescript@^4.6.3, typescript@^4.9.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/bindings/nodejs/lib/client/client.ts b/bindings/nodejs/lib/client/client.ts index 71ba98451f..893e25a270 100644 --- a/bindings/nodejs/lib/client/client.ts +++ b/bindings/nodejs/lib/client/client.ts @@ -142,7 +142,9 @@ export class Client { options, }, }); - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -308,8 +310,9 @@ export class Client { payload, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -798,8 +801,9 @@ export class Client { blockId, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -820,8 +824,13 @@ export class Client { maxAttempts, }, }); + const parsed = JSON.parse(response) as Response<[BlockId, Block][]>; + const arr: [BlockId, Block][] = []; + parsed.payload.forEach((payload) => { + arr.push([payload[0], plainToInstance(Block, payload[1])]); + }); - return JSON.parse(response).payload; + return arr; } /** @@ -854,8 +863,9 @@ export class Client { blockId, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -868,8 +878,9 @@ export class Client { blockId, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -883,8 +894,9 @@ export class Client { blockId, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** * Promote a block without checking if it should be promoted @@ -896,8 +908,9 @@ export class Client { blockId, }, }); - - return JSON.parse(response).payload; + const parsed = JSON.parse(response) as Response<[BlockId, Block]>; + const block = plainToInstance(Block, parsed.payload[1]); + return [parsed.payload[0], block]; } /** @@ -1005,4 +1018,34 @@ export class Client { return JSON.parse(response).payload; } + + /** + * Extension method which provides request methods for plugins. + * @param basePluginPath The base path for the plugin eg indexer/v1/ . + * @param method The http method. + * @param endpoint The path for the plugin request. + * @param queryParams Additional query params for the request. + * @param request The request object. + * @returns The response json. + */ + async callPluginRoute( + basePluginPath: string, + method: 'GET' | 'POST', + endpoint: string, + queryParams?: string[], + request?: string, + ): Promise { + const response = await this.methodHandler.callMethod({ + name: 'callPluginRoute', + data: { + basePluginPath, + method, + endpoint, + queryParams: queryParams ?? [], + request, + }, + }); + + return JSON.parse(response).payload; + } } diff --git a/bindings/nodejs/lib/types/block/address.ts b/bindings/nodejs/lib/types/block/address.ts index 3b6761ccc4..f33bd58a9c 100644 --- a/bindings/nodejs/lib/types/block/address.ts +++ b/bindings/nodejs/lib/types/block/address.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { HexEncodedString } from '../utils'; +import { AliasId, NftId } from './id'; enum AddressType { Ed25519 = 0, @@ -47,15 +48,15 @@ class Ed25519Address extends Address { } class AliasAddress extends Address { - private aliasId: HexEncodedString; - constructor(address: HexEncodedString) { + private aliasId: AliasId; + constructor(address: AliasId) { super(AddressType.Alias); this.aliasId = address; } /** * The alias id. */ - getAliasId(): HexEncodedString { + getAliasId(): AliasId { return this.aliasId; } @@ -67,15 +68,15 @@ class AliasAddress extends Address { * NFT address. */ class NftAddress extends Address { - private nftId: HexEncodedString; - constructor(address: HexEncodedString) { + private nftId: NftId; + constructor(address: NftId) { super(AddressType.Nft); this.nftId = address; } /** * The NFT Id. */ - getNftId(): HexEncodedString { + getNftId(): NftId { return this.nftId; } diff --git a/bindings/nodejs/lib/types/block/block-id.ts b/bindings/nodejs/lib/types/block/block-id.ts deleted file mode 100644 index af3990917e..0000000000 --- a/bindings/nodejs/lib/types/block/block-id.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2021-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -/** A block identifier, the BLAKE2b-256 hash of the block bytes. - * See for more information. - */ -export type BlockId = string; diff --git a/bindings/nodejs/lib/types/block/id.ts b/bindings/nodejs/lib/types/block/id.ts new file mode 100644 index 0000000000..019c491ec4 --- /dev/null +++ b/bindings/nodejs/lib/types/block/id.ts @@ -0,0 +1,14 @@ +// Copyright 2021-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { HexEncodedString } from '../utils'; + +export type AliasId = HexEncodedString; + +export type NftId = HexEncodedString; + +export type BlockId = HexEncodedString; + +export type TokenId = HexEncodedString; + +export type FoundryId = HexEncodedString; diff --git a/bindings/nodejs/lib/types/block/index.ts b/bindings/nodejs/lib/types/block/index.ts index 6f395c811c..c0f349c55c 100644 --- a/bindings/nodejs/lib/types/block/index.ts +++ b/bindings/nodejs/lib/types/block/index.ts @@ -6,6 +6,6 @@ export * from './output/index'; export * from './payload/index'; export * from './address'; -export * from './block-id'; +export * from './id'; export * from './block'; export * from './signature'; diff --git a/bindings/nodejs/lib/types/block/output/output.ts b/bindings/nodejs/lib/types/block/output/output.ts index f024f7e44d..ceb97a4243 100644 --- a/bindings/nodejs/lib/types/block/output/output.ts +++ b/bindings/nodejs/lib/types/block/output/output.ts @@ -239,7 +239,7 @@ class NftOutput extends StateMetadataOutput /*implements INftOutput*/ { * Unique identifier of the NFT, which is the BLAKE2b-160 hash of the Output ID that created it. * Unless its newly minted, then the id is zeroed. */ - getNnftId(): HexEncodedString { + getNftId(): HexEncodedString { return this.nftId; } } diff --git a/bindings/nodejs/lib/types/block/payload/transaction/essence.ts b/bindings/nodejs/lib/types/block/payload/transaction/essence.ts index 160dd70f48..403948257c 100644 --- a/bindings/nodejs/lib/types/block/payload/transaction/essence.ts +++ b/bindings/nodejs/lib/types/block/payload/transaction/essence.ts @@ -1,8 +1,12 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -// Temp solution for not double parsing JSON +import { Type } from 'class-transformer'; +import { PayloadDiscriminator } from '..'; import { HexEncodedString } from '../../../utils'; +import { Input, InputDiscriminator } from '../../input'; +import { Output, OutputDiscriminator } from '../../output'; +import { Payload } from '../payload'; /** * All of the essence types. @@ -30,22 +34,38 @@ abstract class TransactionEssence { * RegularTransactionEssence transaction essence. */ class RegularTransactionEssence extends TransactionEssence { - /** - * The public key. - */ - publicKey: HexEncodedString; - /** - * The transactionessence. - */ - transactionEssence: HexEncodedString; + /// The unique value denoting whether the block was meant for mainnet, testnet, or a private network. + networkId: number; + inputsCommitment: HexEncodedString; + + @Type(() => Input, { + discriminator: InputDiscriminator, + }) + inputs: [Input]; + + @Type(() => Output, { + discriminator: OutputDiscriminator, + }) + outputs: [Output]; + + @Type(() => Payload, { + discriminator: PayloadDiscriminator, + }) + payload: Payload | undefined; constructor( - publicKey: HexEncodedString, - transactionEssence: HexEncodedString, + networkId: number, + inputsCommitment: HexEncodedString, + inputs: [Input], + outputs: [Output], + payload: Payload | undefined, ) { super(TransactionEssenceType.Regular); - this.publicKey = publicKey; - this.transactionEssence = transactionEssence; + this.networkId = networkId; + this.inputsCommitment = inputsCommitment; + this.inputs = inputs; + this.outputs = outputs; + this.payload = payload; } } diff --git a/bindings/nodejs/lib/types/block/payload/transaction/index.ts b/bindings/nodejs/lib/types/block/payload/transaction/index.ts index 06e2e6bcea..699f469d27 100644 --- a/bindings/nodejs/lib/types/block/payload/transaction/index.ts +++ b/bindings/nodejs/lib/types/block/payload/transaction/index.ts @@ -2,5 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 export * from './essence'; -export * from './transaction'; export * from './unlock'; +export * from './transaction'; diff --git a/bindings/nodejs/lib/types/block/payload/transaction/unlock.ts b/bindings/nodejs/lib/types/block/payload/transaction/unlock.ts index 62f87d55af..0bfefd6a54 100644 --- a/bindings/nodejs/lib/types/block/payload/transaction/unlock.ts +++ b/bindings/nodejs/lib/types/block/payload/transaction/unlock.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Type } from 'class-transformer'; -import { Ed25519Signature } from '../../..'; +import { Ed25519Signature } from '../../signature'; /** * All of the unlock types. diff --git a/bindings/nodejs/lib/types/client/bridge/client.ts b/bindings/nodejs/lib/types/client/bridge/client.ts index 76ed4d4a3b..d266072461 100644 --- a/bindings/nodejs/lib/types/client/bridge/client.ts +++ b/bindings/nodejs/lib/types/client/bridge/client.ts @@ -431,3 +431,14 @@ export type __RequestFundsFromFaucetMethod__ = { address: string; }; }; + +export type __CallPluginRouteMethod__ = { + name: 'callPluginRoute'; + data: { + basePluginPath: string; + method: 'GET' | 'POST'; + endpoint: string; + queryParams: string[]; + request?: string; + }; +}; diff --git a/bindings/nodejs/lib/types/client/bridge/index.ts b/bindings/nodejs/lib/types/client/bridge/index.ts index a4faa95e01..25ab5189dc 100644 --- a/bindings/nodejs/lib/types/client/bridge/index.ts +++ b/bindings/nodejs/lib/types/client/bridge/index.ts @@ -66,6 +66,7 @@ import type { __ClearListenersMethod__, __SignatureUnlockMethod__, __RequestFundsFromFaucetMethod__, + __CallPluginRouteMethod__, } from './client'; export type __ClientMethods__ = @@ -132,4 +133,5 @@ export type __ClientMethods__ = | __BuildFoundryOutputMethod__ | __BuildNftOutputMethod__ | __ClearListenersMethod__ - | __RequestFundsFromFaucetMethod__; + | __RequestFundsFromFaucetMethod__ + | __CallPluginRouteMethod__; diff --git a/bindings/nodejs/lib/types/client/generate-addresses-options.ts b/bindings/nodejs/lib/types/client/generate-addresses-options.ts index 7433512236..33e4eb2499 100644 --- a/bindings/nodejs/lib/types/client/generate-addresses-options.ts +++ b/bindings/nodejs/lib/types/client/generate-addresses-options.ts @@ -1,6 +1,6 @@ // Copyright 2021-2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import type { CoinType } from '../../client'; +import type { CoinType } from './constants'; import type { IRange } from './range'; /** diff --git a/bindings/nodejs/lib/types/client/index.ts b/bindings/nodejs/lib/types/client/index.ts index 5c7c06c0a3..13de558dcc 100644 --- a/bindings/nodejs/lib/types/client/index.ts +++ b/bindings/nodejs/lib/types/client/index.ts @@ -9,7 +9,6 @@ export * from './generate-addresses-options'; export * from './ledger-nano-status'; export * from './network'; export * from './nodeInfo'; -export * from './prepared-create-token-transaction-data'; export * from './prepared-transaction-data'; export * from './query-parameters'; export * from './range'; diff --git a/bindings/nodejs/lib/types/client/query-parameters.ts b/bindings/nodejs/lib/types/client/query-parameters.ts index 3e52f1a22d..9cba7d8f79 100644 --- a/bindings/nodejs/lib/types/client/query-parameters.ts +++ b/bindings/nodejs/lib/types/client/query-parameters.ts @@ -47,6 +47,7 @@ export type NftQueryParameter = | ExpiresBefore | ExpiresAfter | ExpirationReturnAddress + | Issuer | Sender | Tag | CommonQueryParameters; diff --git a/bindings/nodejs/lib/types/models/conflict-reason.ts b/bindings/nodejs/lib/types/models/conflict-reason.ts index fc894e10bd..63f248622e 100644 --- a/bindings/nodejs/lib/types/models/conflict-reason.ts +++ b/bindings/nodejs/lib/types/models/conflict-reason.ts @@ -4,7 +4,7 @@ /** * Reason for block conflicts. */ -export declare enum ConflictReason { +export enum ConflictReason { /** * The block has no conflict. */ @@ -62,3 +62,30 @@ export declare enum ConflictReason { */ semanticValidationFailed = 255, } + +/** + * Conflict reason strings. + */ +export const CONFLICT_REASON_STRINGS: { [key in ConflictReason]: string } = { + [ConflictReason.none]: 'The block has no conflict', + [ConflictReason.inputUTXOAlreadySpent]: + 'The referenced UTXO was already spent', + [ConflictReason.inputUTXOAlreadySpentInThisMilestone]: + 'The referenced UTXO was already spent while confirming this milestone', + [ConflictReason.inputUTXONotFound]: 'The referenced UTXO cannot be found', + [ConflictReason.inputOutputSumMismatch]: + 'The sum of the inputs and output values does not match', + [ConflictReason.invalidSignature]: 'The unlock block signature is invalid', + [ConflictReason.invalidTimelock]: + 'The configured timelock is not yet expired', + [ConflictReason.invalidNativeTokens]: 'The native tokens are invalid', + [ConflictReason.returnAmountMismatch]: + 'The return amount in a transaction is not fulfilled by the output side', + [ConflictReason.invalidInputUnlock]: 'The input unlock is invalid', + [ConflictReason.invalidInputsCommitment]: + 'The inputs commitment is invalid', + [ConflictReason.invalidSender]: + ' The output contains a Sender with an ident (address) which is not unlocked', + [ConflictReason.invalidChainState]: 'The chain state transition is invalid', + [ConflictReason.semanticValidationFailed]: 'The semantic validation failed', +}; diff --git a/bindings/nodejs/lib/types/utils/bridge/index.ts b/bindings/nodejs/lib/types/utils/bridge/index.ts index aaffdc9196..de6c40ba67 100644 --- a/bindings/nodejs/lib/types/utils/bridge/index.ts +++ b/bindings/nodejs/lib/types/utils/bridge/index.ts @@ -2,8 +2,12 @@ import type { __GenerateMnemonicMethod__, __MnemonicToHexSeedMethod__, __ComputeAliasIdMethod__, + __ComputeOutputIdMethod__, + __ComputeTokenIdMethod__, __ComputeNftIdMethod__, __ComputeFoundryIdMethod__, + __ComputeInputsCommitmentMethod__, + __ComputeStorageDepositMethod__, __ParseBech32AddressMethod__, __BlockIdMethod__, __MilestoneIdMethod__, @@ -27,6 +31,10 @@ export type __UtilsMethods__ = | __ComputeAliasIdMethod__ | __ComputeNftIdMethod__ | __ComputeFoundryIdMethod__ + | __ComputeOutputIdMethod__ + | __ComputeTokenIdMethod__ + | __ComputeInputsCommitmentMethod__ + | __ComputeStorageDepositMethod__ | __ParseBech32AddressMethod__ | __BlockIdMethod__ | __MilestoneIdMethod__ diff --git a/bindings/nodejs/lib/types/utils/bridge/utils.ts b/bindings/nodejs/lib/types/utils/bridge/utils.ts index 1a154b69ae..bb8e16a773 100644 --- a/bindings/nodejs/lib/types/utils/bridge/utils.ts +++ b/bindings/nodejs/lib/types/utils/bridge/utils.ts @@ -5,7 +5,12 @@ import { TransactionEssence, MilestonePayload, TransactionPayload, + TransactionId, + TokenSchemeType, + Output, + IRent, } from '../../'; +import { AliasId } from '../../block/id'; export interface __GenerateMnemonicMethod__ { name: 'generateMnemonic'; @@ -25,6 +30,22 @@ export interface __ComputeAliasIdMethod__ { }; } +export interface __ComputeFoundryIdMethod__ { + name: 'computeFoundryId'; + data: { + aliasId: AliasId; + serialNumber: number; + tokenSchemeKind: number; + }; +} + +export interface __ComputeInputsCommitmentMethod__ { + name: 'computeInputsCommitment'; + data: { + inputs: Output[]; + }; +} + export interface __ComputeNftIdMethod__ { name: 'computeNftId'; data: { @@ -32,12 +53,28 @@ export interface __ComputeNftIdMethod__ { }; } -export interface __ComputeFoundryIdMethod__ { - name: 'computeFoundryId'; +export interface __ComputeOutputIdMethod__ { + name: 'computeOutputId'; + data: { + id: TransactionId; + index: number; + }; +} + +export interface __ComputeStorageDepositMethod__ { + name: 'computeStorageDeposit'; data: { - aliasAddress: string; + output: Output; + rentStructure: IRent; + }; +} + +export interface __ComputeTokenIdMethod__ { + name: 'computeTokenId'; + data: { + aliasId: AliasId; serialNumber: number; - tokenSchemeKind: number; + tokenSchemeType: TokenSchemeType; }; } diff --git a/bindings/nodejs/lib/types/wallet/index.ts b/bindings/nodejs/lib/types/wallet/index.ts index fdce33c021..821e52e8d4 100644 --- a/bindings/nodejs/lib/types/wallet/index.ts +++ b/bindings/nodejs/lib/types/wallet/index.ts @@ -15,3 +15,4 @@ export * from './prepared-transaction'; export * from './signed-transaction-essence'; export * from './transaction'; export * from './transaction-options'; +export * from './prepared-create-token-transaction-data'; diff --git a/bindings/nodejs/lib/types/client/prepared-create-token-transaction-data.ts b/bindings/nodejs/lib/types/wallet/prepared-create-token-transaction-data.ts similarity index 85% rename from bindings/nodejs/lib/types/client/prepared-create-token-transaction-data.ts rename to bindings/nodejs/lib/types/wallet/prepared-create-token-transaction-data.ts index 033f74a6de..ed00f95307 100644 --- a/bindings/nodejs/lib/types/client/prepared-create-token-transaction-data.ts +++ b/bindings/nodejs/lib/types/wallet/prepared-create-token-transaction-data.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Type } from 'class-transformer'; -import { Transaction } from '../wallet'; -import { PreparedTransactionData } from './prepared-transaction-data'; +import { Transaction } from './transaction'; +import { PreparedTransactionData } from '../client/prepared-transaction-data'; /** The result of preparing an operation to create a native token */ export class PreparedCreateNativeTokenTransactionData { diff --git a/bindings/nodejs/lib/utils/units-helper.ts b/bindings/nodejs/lib/utils/units-helper.ts new file mode 100644 index 0000000000..704a41d5b6 --- /dev/null +++ b/bindings/nodejs/lib/utils/units-helper.ts @@ -0,0 +1,155 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +/** + * Magnitudes for the numbers. + */ +export type Magnitudes = 'P' | 'T' | 'G' | 'M' | 'K' | ''; + +/** + * Class to help with units formatting. + */ +export class UnitsHelper { + /** + * Map units. + */ + public static readonly MAGNITUDE_MAP: { + [magnitude in Magnitudes]: { val: number; dp: number }; + } = { + '': { val: 1, dp: 0 }, + K: { val: 1000, dp: 3 }, + M: { val: 1000000, dp: 6 }, + G: { val: 1000000000, dp: 9 }, + T: { val: 1000000000000, dp: 12 }, + P: { val: 1000000000000000, dp: 15 }, + }; + + /** + * Format the value in the best units. + * @param value The value to format. + * @param decimalPlaces The number of decimal places to display. + * @returns The formated value. + */ + public static formatBest(value: number, decimalPlaces = 2): string { + return UnitsHelper.formatUnits( + value, + UnitsHelper.calculateBest(value), + decimalPlaces, + ); + } + + /** + * Format the value in the best units. + * @param value The value to format. + * @param magnitude The magnitude to format with. + * @param decimalPlaces The number of decimal places to display. + * @returns The formated value. + */ + public static formatUnits( + value: number, + magnitude: Magnitudes, + decimalPlaces = 2, + ): string { + if (!UnitsHelper.MAGNITUDE_MAP[magnitude]) { + throw new Error(`Unrecognized magnitude ${magnitude}`); + } + + if (!value) { + return '0'; + } + + return magnitude === '' + ? `${value}` + : `${UnitsHelper.convertUnits(value, '', magnitude).toFixed( + decimalPlaces, + )} ${magnitude}`; + } + + /** + * Format the value in the best units. + * @param value The value to format. + * @returns The best units for the value. + */ + public static calculateBest(value: number): Magnitudes { + let bestUnits: Magnitudes = ''; + + if (!value) { + return bestUnits; + } + + const checkLength = Math.abs(value).toString().length; + + if (checkLength > UnitsHelper.MAGNITUDE_MAP.P.dp) { + bestUnits = 'P'; + } else if (checkLength > UnitsHelper.MAGNITUDE_MAP.T.dp) { + bestUnits = 'T'; + } else if (checkLength > UnitsHelper.MAGNITUDE_MAP.G.dp) { + bestUnits = 'G'; + } else if (checkLength > UnitsHelper.MAGNITUDE_MAP.M.dp) { + bestUnits = 'M'; + } else if (checkLength > UnitsHelper.MAGNITUDE_MAP.K.dp) { + bestUnits = 'K'; + } + + return bestUnits; + } + + /** + * Convert the value to different units. + * @param value The value to convert. + * @param from The from magnitude. + * @param to The to magnitude. + * @returns The formatted unit. + */ + public static convertUnits( + value: number, + from: Magnitudes, + to: Magnitudes, + ): number { + if (!value) { + return 0; + } + if (!UnitsHelper.MAGNITUDE_MAP[from]) { + throw new Error(`Unrecognized fromUnit ${from}`); + } + if (!UnitsHelper.MAGNITUDE_MAP[to]) { + throw new Error(`Unrecognized toUnit ${to}`); + } + + if (from === to) { + return Number(value); + } + + const multiplier = value < 0 ? -1 : 1; + const scaledValue = + (Math.abs(Number(value)) * UnitsHelper.MAGNITUDE_MAP[from].val) / + UnitsHelper.MAGNITUDE_MAP[to].val; + const numDecimals = UnitsHelper.MAGNITUDE_MAP[to].dp; + + // We cant use toFixed to just convert the new value to a string with + // fixed decimal places as it will round, which we don't want + // instead we want to convert the value to a string and manually + // truncate the number of digits after the decimal + // Unfortunately large numbers end up in scientific notation with + // the regular toString() so we use a custom conversion. + let fixed = scaledValue.toString(); + if (fixed.includes('e')) { + fixed = scaledValue.toFixed( + Number.parseInt(fixed.split('-')[1], 10), + ); + } + + // Now we have the number as a full string we can split it into + // whole and decimals parts + const parts = fixed.split('.'); + if (parts.length === 1) { + parts.push('0'); + } + + // Now truncate the decimals by the number allowed on the toUnit + parts[1] = parts[1].slice(0, numDecimals); + + // Finally join the parts and convert back to a real number + return Number.parseFloat(`${parts[0]}.${parts[1]}`) * multiplier; + } +} diff --git a/bindings/nodejs/lib/utils/utils.ts b/bindings/nodejs/lib/utils/utils.ts index 0bcff07e9a..e2e7784a27 100644 --- a/bindings/nodejs/lib/utils/utils.ts +++ b/bindings/nodejs/lib/utils/utils.ts @@ -6,7 +6,6 @@ import { callUtilsMethod } from '../bindings'; import { Address, Ed25519Address, - BlockId, HexEncodedString, Block, Ed25519Signature, @@ -16,7 +15,13 @@ import { MilestoneId, TransactionPayload, TransactionId, + TokenSchemeType, + Output, + IRent, + HexEncodedAmount, + OutputId, } from '../types'; +import { AliasId, BlockId, FoundryId, TokenId } from '../types/block/id'; /** Utils class for utils. */ export class Utils { @@ -44,7 +49,7 @@ export class Utils { /** * Computes the alias id for the given alias output id. */ - static computeAliasId(outputId: string): string { + static computeAliasId(outputId: string): AliasId { return callUtilsMethod({ name: 'computeAliasId', data: { @@ -53,6 +58,24 @@ export class Utils { }); } + /** + * Computes the foundry id. + */ + static computeFoundryId( + aliasId: AliasId, + serialNumber: number, + tokenSchemeKind: number, + ): FoundryId { + return callUtilsMethod({ + name: 'computeFoundryId', + data: { + aliasId, + serialNumber, + tokenSchemeKind, + }, + }); + } + /** * Computes the NFT id for the given NFT output id. */ @@ -66,19 +89,72 @@ export class Utils { } /** - * Computes the foundry id. + * Computes the inputCommitment from the output objects that are used as inputs to fund the transaction. + * @param inputs The output objects used as inputs for the transaction. + * @returns The inputs commitment. */ - static computeFoundryId( - aliasAddress: string, + static computeInputsCommitment(inputs: Output[]): HexEncodedString { + return callUtilsMethod({ + name: 'computeInputsCommitment', + data: { + inputs, + }, + }); + } + + /** + * Computes the output ID from transaction id and output index. + * @param transactionId The id of the transaction. + * @param outputIndex The index of the output. + * @returns The output id. + */ + static computeOutputId(id: TransactionId, index: number): OutputId { + return callUtilsMethod({ + name: 'computeOutputId', + data: { + id, + index, + }, + }); + } + + /** + * Computes the required storage deposit of an output. + * @param output The output. + * @param rentStructure Rent cost of objects which take node resources. + * @returns The required storage deposit. + */ + static computeStorageDeposit( + output: Output, + rentStructure: IRent, + ): HexEncodedAmount { + return callUtilsMethod({ + name: 'computeStorageDeposit', + data: { + output, + rentStructure, + }, + }); + } + + /** + * Computes a tokenId from the aliasId, serial number and token scheme type. + * @param aliasId The alias that controls the foundry. + * @param serialNumber The serial number of the foundry. + * @param tokenSchemeType The tokenSchemeType of the foundry. + * @returns The tokenId. + */ + static computeTokenId( + aliasId: AliasId, serialNumber: number, - tokenSchemeKind: number, - ): string { + tokenSchemeType: TokenSchemeType, + ): TokenId { return callUtilsMethod({ - name: 'computeFoundryId', + name: 'computeTokenId', data: { - aliasAddress, + aliasId, serialNumber, - tokenSchemeKind, + tokenSchemeType, }, }); } @@ -124,6 +200,8 @@ export class Utils { /** * Returns the transaction ID (Blake2b256 hash of the provided transaction payload) + * @param payload The transaction payload. + * @returns The transaction id. */ static transactionId(payload: TransactionPayload): TransactionId { return callUtilsMethod({ diff --git a/bindings/nodejs/lib/wallet/account.ts b/bindings/nodejs/lib/wallet/account.ts index 90cfb14c47..d7e664e4e8 100644 --- a/bindings/nodejs/lib/wallet/account.ts +++ b/bindings/nodejs/lib/wallet/account.ts @@ -35,13 +35,9 @@ import { BuildNftOutputData, SignedTransactionEssence, PreparedTransaction, -} from '../types/wallet'; -import { - INode, - Burn, - PreparedTransactionData, PreparedCreateNativeTokenTransactionData, -} from '../client'; +} from '../types/wallet'; +import { INode, Burn, PreparedTransactionData } from '../client'; import { AliasOutput, NftOutput, diff --git a/bindings/nodejs/tests/client/utilityMethods.spec.ts b/bindings/nodejs/tests/client/utilityMethods.spec.ts index f06c9a9340..ec67f9ce76 100644 --- a/bindings/nodejs/tests/client/utilityMethods.spec.ts +++ b/bindings/nodejs/tests/client/utilityMethods.spec.ts @@ -94,13 +94,13 @@ describe('Client utility methods', () => { }); it('compute foundry id', async () => { - const aliasAddress = + const aliasId = '0xcf077d276686ba64c0404b9eb2d15556782113c5a1985f262b70f9964d3bbd7f'; const serialNumber = 0; const tokenSchemeKind = 0; const foundryId = Utils.computeFoundryId( - aliasAddress, + aliasId, serialNumber, tokenSchemeKind, ); diff --git a/bindings/python/README.md b/bindings/python/README.md index a915536670..27c66e96f7 100644 --- a/bindings/python/README.md +++ b/bindings/python/README.md @@ -15,7 +15,7 @@ Python binding to the [iota-sdk library](/README.md). ## Requirements -- [Python 3.x](https://www.python.org) +- [Python 3.10+](https://www.python.org) - [pip ^21.x](https://pypi.org/project/pip) - `Rust` and `Cargo` to compile the binding. Install them [here](https://doc.rust-lang.org/cargo/getting-started/installation.html). diff --git a/bindings/python/examples/client/custom_plugin.py b/bindings/python/examples/client/custom_plugin.py new file mode 100644 index 0000000000..65ff48327d --- /dev/null +++ b/bindings/python/examples/client/custom_plugin.py @@ -0,0 +1,29 @@ +from iota_sdk import Client, init_logger +from dotenv import load_dotenv + +import json +import os + +load_dotenv() +log_config = { + 'name': 'client.log', + 'levelFilter': 'debug', + 'targetExclusions': ["h2", "hyper", "rustls"] +} + +log_config_str = json.dumps(log_config) + +init_logger(log_config_str) + +node_url = os.environ.get('NODE_URL', 'https://api.testnet.shimmer.network') + +# Create a Client instance +client = Client(nodes=[node_url]) + +# Get an nft ID from ./10_mint_nft.py +nft_id = "0x0000000000000000000000000000000000000000000000000000000000000000" +route = "outputs/nft/" + nft_id + +# Call our "custom" indexer plugin +output_id = client.call_plugin_route("api/indexer/v1/", "GET", route, None, None) +print(json.dumps(output_id, indent=4)) diff --git a/bindings/python/iota_sdk/client/_node_core_api.py b/bindings/python/iota_sdk/client/_node_core_api.py index f01af31d69..37d2c40430 100644 --- a/bindings/python/iota_sdk/client/_node_core_api.py +++ b/bindings/python/iota_sdk/client/_node_core_api.py @@ -164,3 +164,16 @@ def get_included_block_metadata(self, transaction_id: HexStr) -> BlockMetadata: return BlockMetadata.from_dict(self._call_method('getIncludedBlockMetadata', { 'transactionId': transaction_id })) + + def call_plugin_route(self, base_plugin_path: str, method: str, endpoint: str, query_params: [str] = None, request: str = None): + """Extension method which provides request methods for plugins. + """ + if query_params is None: + query_params = [] + return self._call_method('callPluginRoute', { + 'basePluginPath': base_plugin_path, + 'method': method, + 'endpoint': endpoint, + 'queryParams': query_params, + 'request': request, + }) diff --git a/bindings/python/iota_sdk/utils.py b/bindings/python/iota_sdk/utils.py index 02dfaf57e4..4847b612e3 100644 --- a/bindings/python/iota_sdk/utils.py +++ b/bindings/python/iota_sdk/utils.py @@ -97,6 +97,32 @@ def compute_alias_id(output_id: OutputId) -> HexStr: 'outputId': output_id }) + @staticmethod + def compute_foundry_id(alias_id: str, serial_number: int, token_scheme_kind: int) -> HexStr: + """Computes the foundry id. + """ + return _call_method('computeFoundryId', { + 'aliasId': alias_id, + 'serialNumber': serial_number, + 'tokenSchemeKind': token_scheme_kind + }) + + @staticmethod + def compute_inputs_commitment(inputs) -> HexStr: + """Computes the input commitment from the output objects that are used as inputs to fund the transaction. + """ + return _call_method('computeInputsCommitment', { + 'inputs': inputs + }) + + @staticmethod + def compute_storage_deposit(output, rent) -> HexStr: + """Computes the required storage deposit of an output. + """ + return _call_method('computeStorageDeposit', { + 'inputs': inputs + }) + @staticmethod def compute_nft_id(output_id: OutputId) -> HexStr: """Computes the NFT id for the given NFT output id. @@ -104,17 +130,27 @@ def compute_nft_id(output_id: OutputId) -> HexStr: return _call_method('computeNftId', { 'outputId': output_id }) + + @staticmethod + def compute_output_id(transaction_id: HexStr, index: int) -> OutputId: + """Computes the output id from transaction id and output index. + """ + return OutputId.from_string(_call_method('computeOutputId', { + 'transactionId': transaction_id, + 'index': index, + })) @staticmethod - def compute_foundry_id(alias_address: str, serial_number: int, token_scheme_kind: int) -> HexStr: - """Computes the foundry id. + def compute_token_id(alias_id: HexStr, serial_number: int, token_scheme_kind: int) -> HexStr: + """Computes a token id from the alias id, serial number and token scheme type. """ - return _call_method('computeNftId', { - 'aliasAddress': alias_address, + return _call_method('computeTokenId', { + 'aliasId': alias_id, 'serialNumber': serial_number, 'tokenSchemeKind': token_scheme_kind }) + @staticmethod def block_id(block: Block) -> HexStr: """ Returns a block ID (Blake2b256 hash of block bytes) from a block. diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 8aab33d046..bb5e5fcaaa 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for `LedgerSecretManager::sign_ed25519`; - `UnlockCondition::{is_address, is_storage_deposit_return, is_timelock, is_expiration, is_state_controller_address, is_governor_address, is_immutable_alias_address}`; - `UnlockCondition::{as_address, as_storage_deposit_return, as_timelock, as_expiration, as_state_controller_address, as_governor_address, as_immutable_alias_address}`; +- `ClientInner::call_plugin_route` to Client to fetch data from custom node plugins; ### Changed diff --git a/sdk/src/client/node_api/core/routes.rs b/sdk/src/client/node_api/core/routes.rs index 65403fb280..f231eedfe2 100644 --- a/sdk/src/client/node_api/core/routes.rs +++ b/sdk/src/client/node_api/core/routes.rs @@ -33,7 +33,7 @@ use crate::{ pub(crate) static INFO_PATH: &str = "api/core/v2/info"; /// NodeInfo wrapper which contains the node info and the url from the node (useful when multiple nodes are used) -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct NodeInfoWrapper { /// The returned node info diff --git a/sdk/src/client/node_api/error.rs b/sdk/src/client/node_api/error.rs index 154911fbce..e8bf22090f 100644 --- a/sdk/src/client/node_api/error.rs +++ b/sdk/src/client/node_api/error.rs @@ -26,4 +26,7 @@ pub enum Error { /// None of our nodes have remote Pow enabled #[error("No node available for remote Pow")] UnavailablePow, + /// We made a call to the node but the protocol was unsupported + #[error("Call to {0} is not supported on this node")] + NotSupported(String), } diff --git a/sdk/src/client/node_api/indexer/routes.rs b/sdk/src/client/node_api/indexer/routes.rs index 509eb1caf3..81090976aa 100644 --- a/sdk/src/client/node_api/indexer/routes.rs +++ b/sdk/src/client/node_api/indexer/routes.rs @@ -27,8 +27,9 @@ impl ClientInner { /// GET with query parameter returns all outputIDs that fit these filter criteria. /// Query parameters: "address", "hasStorageDepositReturn", "storageDepositReturnAddress", /// "hasExpiration", "expiresBefore", "expiresAfter", "hasTimelock", "timelockedBefore", - /// "timelockedAfter", "sender", "tag", "createdBefore" and "createdAfter". Returns an empty Vec if no results - /// are found. api/indexer/v1/outputs/basic + /// "timelockedAfter", "sender", "tag", "createdBefore" and "createdAfter". + /// Returns Err(Node(NotFound) if no results are found. + /// api/indexer/v1/outputs/basic pub async fn basic_output_ids( &self, query_parameters: impl Into> + Send, @@ -43,7 +44,7 @@ impl ClientInner { /// Get alias outputs filtered by the given parameters. /// GET with query parameter returns all outputIDs that fit these filter criteria. /// Query parameters: "stateController", "governor", "issuer", "sender", "createdBefore", "createdAfter" - /// Returns an empty list if no results are found. + /// Returns Err(Node(NotFound) if no results are found. /// api/indexer/v1/outputs/alias pub async fn alias_output_ids( &self, @@ -71,7 +72,7 @@ impl ClientInner { /// Get foundry outputs filtered by the given parameters. /// GET with query parameter returns all outputIDs that fit these filter criteria. /// Query parameters: "address", "createdBefore", "createdAfter" - /// Returns an empty list if no results are found. + /// Returns Err(Node(NotFound) if no results are found. /// api/indexer/v1/outputs/foundry pub async fn foundry_output_ids( &self, @@ -100,7 +101,7 @@ impl ClientInner { /// Query parameters: "address", "hasStorageDepositReturn", "storageDepositReturnAddress", /// "hasExpiration", "expiresBefore", "expiresAfter", "hasTimelock", "timelockedBefore", /// "timelockedAfter", "issuer", "sender", "tag", "createdBefore", "createdAfter" - /// Returns an empty list if no results are found. + /// Returns Err(Node(NotFound) if no results are found. /// api/indexer/v1/outputs/nft pub async fn nft_output_ids( &self, diff --git a/sdk/src/client/node_api/mod.rs b/sdk/src/client/node_api/mod.rs index 24343a940a..2fbb76dedf 100644 --- a/sdk/src/client/node_api/mod.rs +++ b/sdk/src/client/node_api/mod.rs @@ -12,3 +12,4 @@ pub mod mqtt; #[cfg(feature = "participation")] #[cfg_attr(docsrs, doc(cfg(feature = "participation")))] pub mod participation; +pub mod plugin; diff --git a/sdk/src/client/node_api/plugin/mod.rs b/sdk/src/client/node_api/plugin/mod.rs new file mode 100644 index 0000000000..aa7260a8f4 --- /dev/null +++ b/sdk/src/client/node_api/plugin/mod.rs @@ -0,0 +1,46 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//! Custom plugin call + +use core::str::FromStr; + +use reqwest::Method; + +use crate::client::{ClientInner, Result}; + +impl ClientInner { + /// Extension method which provides request methods for plugins. + pub async fn call_plugin_route( + &self, + base_plugin_path: &str, + method: &str, + endpoint: &str, + query_params: Vec, + request_object: Option, + ) -> Result + where + T: serde::de::DeserializeOwned + std::fmt::Debug + serde::Serialize, + { + let mut method = method.to_string(); + method.make_ascii_uppercase(); + + let req_method = reqwest::Method::from_str(&method); + + let node_manager = self.node_manager.read().await; + let path = format!("{}{}{}", base_plugin_path, endpoint, query_params.join("&")); + let timeout = self.get_timeout().await; + + match req_method { + Ok(Method::GET) => node_manager.get_request(&path, None, timeout, false, false).await, + Ok(Method::POST) => { + node_manager + .post_request_json(&path, timeout, request_object.into(), true) + .await + } + _ => Err(crate::client::Error::Node( + crate::client::node_api::error::Error::NotSupported(method.to_string()), + )), + } + } +} diff --git a/sdk/tests/client/common/mod.rs b/sdk/tests/client/common/mod.rs index 9fbe2d524f..8b5c81e5d5 100644 --- a/sdk/tests/client/common/mod.rs +++ b/sdk/tests/client/common/mod.rs @@ -14,7 +14,13 @@ pub use self::constants::{FAUCET_URL, NODE_LOCAL}; /// Sets up a Client with node health ignored. pub async fn setup_client_with_node_health_ignored() -> Client { - Client::builder().with_node(NODE_LOCAL).unwrap().finish().await.unwrap() + Client::builder() + .with_node(NODE_LOCAL) + .unwrap() + .with_ignore_node_health() + .finish() + .await + .unwrap() } /// Create a client with `DEFAULT_DEVNET_NODE_URL` and a random mnemonic, request funds from the faucet to the first diff --git a/sdk/tests/client/node_api.rs b/sdk/tests/client/node_api.rs index c6e0e0492c..f98a4e10e0 100644 --- a/sdk/tests/client/node_api.rs +++ b/sdk/tests/client/node_api.rs @@ -6,7 +6,7 @@ use iota_sdk::{ client::{ api::GetAddressesOptions, bech32_to_hex, node_api::indexer::query_parameters::QueryParameter, - request_funds_from_faucet, secret::SecretManager, Client, + request_funds_from_faucet, secret::SecretManager, Client, NodeInfoWrapper, }, types::block::{ address::ToBech32Ext, @@ -402,3 +402,19 @@ async fn test_mqtt() { } client.subscriber().disconnect().await.unwrap(); } + +#[tokio::test] +async fn test_call_plugin_route() { + let c = setup_client_with_node_health_ignored().await; + + // we call the "custom" plugin "node info" + let plugin_res: NodeInfoWrapper = c + .call_plugin_route("api/core/v2/", "GET", "info", vec![], None) + .await + .unwrap(); + + let info = c.get_info().await.unwrap(); + + // Just check name as info can change between 2 calls + assert_eq!(plugin_res.node_info.name, info.node_info.name); +}