diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a098a85a6..e2bb35ae7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -32,7 +32,11 @@ module.exports = { 'import/no-unresolved': [ 2, { - ignore: ['@concordium/rust-bindings', 'grpc-api'], + ignore: [ + '@concordium/rust-bindings', + 'grpc-api', + '^#.+$', // ESLint resolver does not support subpath imports: https://github.com/import-js/eslint-plugin-import/issues/1868. + ], }, ], 'import/no-extraneous-dependencies': [ diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 56b34f3e4..80e35ffb5 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -13,7 +13,7 @@ on: env: - DUMMY: 1 # For cache busting. + DUMMY: 2 # For cache busting. NODE_VERSION: 18.16.0 RUST_VERSION: 1.65 RUST_FMT: nightly-2023-04-01-x86_64-unknown-linux-gnu diff --git a/examples/cis2/dryRun.transfer.ts b/examples/cis2/dryRun.transfer.ts index eac4b31ad..258993f14 100644 --- a/examples/cis2/dryRun.transfer.ts +++ b/examples/cis2/dryRun.transfer.ts @@ -85,7 +85,7 @@ const client = new ConcordiumGRPCNodeClient( const tokenId = cli.flags.tokenId; const from = AccountAddress.fromBase58(cli.flags.from); const toAddress = parseAddress(cli.flags.to); - const to: CIS2.Receiver = AccountAddress.isAccountAddress(toAddress) + const to: CIS2.Receiver = AccountAddress.instanceOf(toAddress) ? toAddress : { address: toAddress, diff --git a/examples/cis2/transfer.ts b/examples/cis2/transfer.ts index b78ee1a53..3a6ced7c9 100644 --- a/examples/cis2/transfer.ts +++ b/examples/cis2/transfer.ts @@ -94,7 +94,7 @@ const client = new ConcordiumGRPCNodeClient( const tokenId = cli.flags.tokenId; const from = AccountAddress.fromBase58(cli.flags.from); const toAddress = parseAddress(cli.flags.to); - const to: CIS2.Receiver = AccountAddress.isAccountAddress(toAddress) + const to: CIS2.Receiver = AccountAddress.instanceOf(toAddress) ? toAddress : { address: toAddress, diff --git a/examples/cis4/registerCredential.ts b/examples/cis4/registerCredential.ts index cd293d574..0d1a64811 100644 --- a/examples/cis4/registerCredential.ts +++ b/examples/cis4/registerCredential.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import * as ed25519 from '@noble/ed25519'; +import * as ed25519 from '#ed25519'; import { AccountAddress, @@ -104,7 +104,7 @@ const signer = buildAccountSigner(wallet); let holderPubKey: HexString; if (!cli.flags.holderPubKey) { const prv = ed25519.utils.randomPrivateKey(); - const pub = Buffer.from(await ed25519.getPublicKey(prv)).toString( + const pub = Buffer.from(await ed25519.getPublicKeyAsync(prv)).toString( 'hex' ); diff --git a/examples/cis4/registerRevocationKeys.ts b/examples/cis4/registerRevocationKeys.ts index 777dced26..c7c780cb2 100644 --- a/examples/cis4/registerRevocationKeys.ts +++ b/examples/cis4/registerRevocationKeys.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import meow from 'meow'; -import * as ed25519 from '@noble/ed25519'; +import * as ed25519 from '#ed25519'; import { credentials } from '@grpc/grpc-js'; import { @@ -88,7 +88,7 @@ const signer = buildAccountSigner(wallet); let keys: HexString[] = cli.flags.keys ?? []; if (!cli.flags.keys?.length) { const prv = ed25519.utils.randomPrivateKey(); - const pub = Buffer.from(await ed25519.getPublicKey(prv)).toString( + const pub = Buffer.from(await ed25519.getPublicKeyAsync(prv)).toString( 'hex' ); diff --git a/examples/client/getModuleList.ts b/examples/client/getModuleList.ts index 71822cb54..0d678ac32 100644 --- a/examples/client/getModuleList.ts +++ b/examples/client/getModuleList.ts @@ -1,5 +1,5 @@ import { parseEndpoint } from '../shared/util.js'; -import { BlockHash, HexString } from '@concordium/web-sdk'; +import { BlockHash, ModuleReference } from '@concordium/web-sdk'; import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; @@ -54,12 +54,12 @@ const client = new ConcordiumGRPCNodeClient( cli.flags.block === undefined ? undefined : BlockHash.fromHexString(cli.flags.block); - const moduleRefs: AsyncIterable = + const moduleRefs: AsyncIterable = client.getModuleList(blockHash); // #endregion documentation-snippet // Prints module references for await (const moduleRef of moduleRefs) { - console.log(moduleRef); + console.log(ModuleReference.toHexString(moduleRef)); } })(); diff --git a/examples/package.json b/examples/package.json index 4ecbce92a..1f08868a0 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,16 +1,23 @@ { "name": "@concordium/examples", "type": "module", + "imports": { + "#ed25519": { + "node": "./shims/ed25519.node.ts", + "default": "@noble/ed25519" + } + }, "dependencies": { "@concordium/ccd-js-gen": "workspace:^", "@concordium/web-sdk": "workspace:^", "@grpc/grpc-js": "^1.3.4", - "@noble/ed25519": "^1.7.1", + "@noble/ed25519": "^2.0.0", "buffer": "^6.0.3", "meow": "11.0", "node-fetch": "^3.3.2" }, "devDependencies": { + "eslint": "^8.50.0", "ts-node": "10.9", "typescript": "^5.2.2" }, diff --git a/examples/shims/ed25519.node.ts b/examples/shims/ed25519.node.ts new file mode 100644 index 000000000..2ebf319b4 --- /dev/null +++ b/examples/shims/ed25519.node.ts @@ -0,0 +1,9 @@ +// To add support for node versions <19. +// From https://www.npmjs.com/package/@noble/ed25519#usage +import { webcrypto } from 'node:crypto'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +if (!globalThis.crypto) globalThis.crypto = webcrypto; + +export * from '@noble/ed25519'; diff --git a/package.json b/package.json index 7d54730ef..11ab2f2a9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/parser": "^6.7.0", - "eslint": "^7.29.0", + "eslint": "^8.51.0", "eslint-config-prettier": "^8.3.0", "eslint-import-resolver-exports": "^1.0.0-beta.5", "eslint-import-resolver-typescript": "^2.7.1", diff --git a/packages/ccd-js-gen/package.json b/packages/ccd-js-gen/package.json index d34f59604..2f22c04c4 100644 --- a/packages/ccd-js-gen/package.json +++ b/packages/ccd-js-gen/package.json @@ -34,6 +34,7 @@ "@concordium/web-sdk": "6.x" }, "dependencies": { + "@concordium/web-sdk": "6.x", "buffer": "^6.0.3", "commander": "^11.0.0", "sanitize-filename": "^1.6.3", @@ -41,6 +42,7 @@ }, "devDependencies": { "@types/node": "^20.5.0", + "eslint": "^8.50.0", "typescript": "^5.2.2" }, "prettier": { diff --git a/packages/rust-bindings/package.json b/packages/rust-bindings/package.json index 8b5609e76..f9b211289 100644 --- a/packages/rust-bindings/package.json +++ b/packages/rust-bindings/package.json @@ -81,9 +81,12 @@ "build-bundler": "wasm-pack build ./packages/dapp --target bundler --out-dir $INIT_CWD/lib/dapp/bundler --out-name index \"$@\" && wasm-pack build ./packages/wallet --target bundler --out-dir $INIT_CWD/lib/wallet/bundler --out-name index \"$@\"", "build": "yarn build-node \"$@\" && yarn build-bundler \"$@\" && yarn build-web \"$@\"", "build:rust-bindings": "yarn build", - "clean": "rimraf -- target lib .webpack-cache" + "clean": "rimraf -- target lib .webpack-cache", + "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", + "lint-fix": "yarn --silent lint --fix; exit 0" }, "devDependencies": { + "eslint": "^8.50.0", "rimraf": "^5.0.1", "ts-loader": "^9.4.4", "typescript": "^5.2.2", diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 6efc1ad19..a81e4b503 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -30,6 +30,7 @@ - `deserializeTypeValue` The API now uses dedicated types instead of language primitives: + - Use `AccountAddress` instead of a string with base58 encoding. Use `AccountAddress.fromBase58('')` to construct it. - Use `BlockHash` instead of a string with hex encoding. Use `BlockHash.fromHexString('')` to construct it. - Use `TranactionHash` instead of a string with hex encoding. Use `TransactionHash.fromHexString('')` to construct it. @@ -48,6 +49,7 @@ The API now uses dedicated types instead of language primitives: - Use `ModuleReference` instead of a string with hex encoding. Can be constructed using `ModuleReference.fromHexString('')`. Several types have been replaced with a module containing the type itself together with functions for constructing and converting the type: + - `AccountAddress` is now a module with functions related to account addresses: - To refer to `AccountAddress` as a type use `AccountAddress.Type`. - Constructing `new AccountAddress("
")` is now `AccountAddress.fromBase58("
")`. @@ -82,6 +84,8 @@ Several types have been replaced with a module containing the type itself togeth - `EntrypointName` is now a module with functions related to entrypoint names of a smart contract. - `ReceiveName` is now a module with functions related to receive-function names of a smart contract. - `ReturnValue` is now a module with functions related to return values from invoking a smart contract. +- Functions `jsonStringify` and `jsonParse`, which acts as a regular `JSON.stringify` and `JSON.parse` correspondingly, + with the addition of stringifying concordium domain types in a wrapper object that can be parsed into the respective types. ### Changes diff --git a/packages/sdk/jest.config.ts b/packages/sdk/jest.config.ts index e26c5f1df..7fad6c589 100644 --- a/packages/sdk/jest.config.ts +++ b/packages/sdk/jest.config.ts @@ -9,6 +9,9 @@ const config: Config = { moduleNameMapper: { '^(\\.\\.?\\/.+)\\.js$': '$1', // Remap esmodule file extensions }, + transformIgnorePatterns: [ + 'node_modules/(?!@noble/ed25519)', // @noble/ed25519 is an ES module only + ], transform: { '^.+\\.[jt]sx?$': [ 'ts-jest', diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 1875b273b..854869d9c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -33,6 +33,12 @@ "default": "./lib/esm/pub/*.js" } }, + "imports": { + "#ed25519": { + "node": "./src/shims/ed25519.node.ts", + "default": "@noble/ed25519" + } + }, "files": [ "/src/**/*", "/lib/esm/**/*", @@ -45,6 +51,7 @@ "@types/jest": "^26.0.23", "@types/json-bigint": "^1.0.1", "@types/uuid": "^8.3.4", + "eslint": "^8.51.0", "glob": "^10.3.10", "grpc-tools": "^1.11.2", "grpc_tools_node_protoc_ts": "5.3.0", @@ -70,7 +77,7 @@ "dependencies": { "@concordium/rust-bindings": "1.2.0", "@grpc/grpc-js": "^1.9.4", - "@noble/ed25519": "^1.7.1", + "@noble/ed25519": "^2.0.0", "@protobuf-ts/grpc-transport": "^2.9.1", "@protobuf-ts/grpcweb-transport": "^2.9.1", "@protobuf-ts/runtime-rpc": "^2.8.2", diff --git a/packages/sdk/src/GenericContract.ts b/packages/sdk/src/GenericContract.ts index 9bc503890..0e3dc5ee4 100644 --- a/packages/sdk/src/GenericContract.ts +++ b/packages/sdk/src/GenericContract.ts @@ -151,8 +151,8 @@ export class ContractDryRun { ): Promise { const parameter = Parameter.fromBuffer(serializer(input)); const meta = - AccountAddress.isAccountAddress(metaOrInvoker) || - ContractAddress.isContractAddress(metaOrInvoker) + AccountAddress.instanceOf(metaOrInvoker) || + ContractAddress.instanceOf(metaOrInvoker) ? { invoker: metaOrInvoker } : metaOrInvoker; return this.grpcClient.invokeContract( diff --git a/packages/sdk/src/cis2/util.ts b/packages/sdk/src/cis2/util.ts index 5454b05e7..bcc137f2c 100644 --- a/packages/sdk/src/cis2/util.ts +++ b/packages/sdk/src/cis2/util.ts @@ -237,7 +237,7 @@ export function serializeContractAddress( function serializeAddress(address: CIS2.Address): Buffer { return Buffer.concat( - ContractAddress.isContractAddress(address) + ContractAddress.instanceOf(address) ? [encodeWord8(1), serializeContractAddress(address)] : [encodeWord8(0), serializeAccountAddress(address)] ); @@ -272,7 +272,7 @@ function serializeContractReceiver(receiver: CIS2.ContractReceiver): Buffer { function serializeReceiver(receiver: CIS2.Receiver): Buffer { return Buffer.concat( - AccountAddress.isAccountAddress(receiver) + AccountAddress.instanceOf(receiver) ? [encodeWord8(0), AccountAddress.toBuffer(receiver)] : [encodeWord8(1), serializeContractReceiver(receiver)] ); @@ -556,7 +556,7 @@ export function formatCIS2UpdateOperator( ): CIS2.UpdateOperatorParamJson { return { update: input.type === 'add' ? { Add: {} } : { Remove: {} }, - operator: ContractAddress.isContractAddress(input.address) + operator: ContractAddress.instanceOf(input.address) ? { Contract: [ { @@ -575,9 +575,7 @@ export function formatCIS2UpdateOperator( export function formatCIS2Transfer( input: CIS2.Transfer ): CIS2.TransferParamJson { - const from: CIS2.AddressParamJson = ContractAddress.isContractAddress( - input.from - ) + const from: CIS2.AddressParamJson = ContractAddress.instanceOf(input.from) ? { Contract: [ { @@ -588,7 +586,7 @@ export function formatCIS2Transfer( } : { Account: [AccountAddress.toBase58(input.from)] }; let to: CIS2.ReceiverParamJson; - if (AccountAddress.isAccountAddress(input.to)) { + if (AccountAddress.instanceOf(input.to)) { to = { Account: [AccountAddress.toBase58(input.to)] }; } else { to = { diff --git a/packages/sdk/src/cis4/util.ts b/packages/sdk/src/cis4/util.ts index debfdea29..6b43dc0b1 100644 --- a/packages/sdk/src/cis4/util.ts +++ b/packages/sdk/src/cis4/util.ts @@ -1,5 +1,5 @@ import { Buffer } from 'buffer/index.js'; -import { getPublicKey } from '@noble/ed25519'; +import * as ed from '#ed25519'; import type { HexString } from '../types.js'; import type { CIS2 } from '../cis2/util.js'; @@ -287,7 +287,7 @@ export class Web3IdSigner { */ public static async from(privateKey: HexString): Promise { const publicKey = Buffer.from( - await getPublicKey(Buffer.from(privateKey, 'hex')) + await ed.getPublicKeyAsync(Buffer.from(privateKey, 'hex')) ).toString('hex'); return new Web3IdSigner(privateKey, publicKey); } diff --git a/packages/sdk/src/grpc/GRPCClient.ts b/packages/sdk/src/grpc/GRPCClient.ts index a79a14dd1..faaf83c07 100644 --- a/packages/sdk/src/grpc/GRPCClient.ts +++ b/packages/sdk/src/grpc/GRPCClient.ts @@ -671,7 +671,7 @@ export class ConcordiumGRPCClient { * * @param blockHash an optional block hash to get the accounts at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of account addresses represented as Base58 encoded strings. + * @returns an async iterable of account addresses. */ getAccountList( blockHash?: BlockHash.Type, @@ -692,16 +692,16 @@ export class ConcordiumGRPCClient { * * @param blockHash an optional block hash to get the contract modules at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of contract module references, represented as hex strings. + * @returns an async iterable of contract module references. */ getModuleList( blockHash?: BlockHash.Type, abortSignal?: AbortSignal - ): AsyncIterable { + ): AsyncIterable { const opts = { abort: abortSignal }; const hash = getBlockHashInput(blockHash); const asyncIter = this.client.getModuleList(hash, opts).responses; - return mapStream(asyncIter, translate.unwrapValToHex); + return mapStream(asyncIter, ModuleReference.fromProto); } /** @@ -714,7 +714,7 @@ export class ConcordiumGRPCClient { * @param maxAmountOfAncestors the maximum amount of ancestors as a bigint. * @param blockHash a optional block hash to get the ancestors at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of ancestors' block hashes as hex strings. + * @returns an async iterable of ancestors' block hashes. */ getAncestors( maxAmountOfAncestors: bigint, @@ -1569,7 +1569,7 @@ export function getAccountIdentifierInput( ): v2.AccountIdentifierInput { let returnIdentifier: v2.AccountIdentifierInput['accountIdentifierInput']; - if (AccountAddress.isAccountAddress(accountIdentifier)) { + if (AccountAddress.instanceOf(accountIdentifier)) { returnIdentifier = { oneofKind: 'address', address: AccountAddress.toProto(accountIdentifier), @@ -1627,14 +1627,14 @@ export function getInvokerInput( ): v2.Address | undefined { if (!invoker) { return undefined; - } else if (AccountAddress.isAccountAddress(invoker)) { + } else if (AccountAddress.instanceOf(invoker)) { return { type: { oneofKind: 'account', account: AccountAddress.toProto(invoker), }, }; - } else if (ContractAddress.isContractAddress(invoker)) { + } else if (ContractAddress.instanceOf(invoker)) { return { type: { oneofKind: 'contract', diff --git a/packages/sdk/src/pub/types.ts b/packages/sdk/src/pub/types.ts index 57958dba0..eeb66cc7d 100644 --- a/packages/sdk/src/pub/types.ts +++ b/packages/sdk/src/pub/types.ts @@ -64,6 +64,12 @@ import * as ContractEvent from '../types/ContractEvent.js'; import * as CcdAmount from '../types/CcdAmount.js'; import * as TransactionExpiry from '../types/TransactionExpiry.js'; import * as ModuleReference from '../types/ModuleReference.js'; +export { + TypedJsonParseError, + TypedJsonParseErrorCode, + TypedJson, +} from '../types/util.js'; +export { jsonParse, jsonStringify } from '../types/json.js'; // These cannot be exported directly as modules because of a bug in an eslint plugin. // https://github.com/import-js/eslint-plugin-import/issues/2289. diff --git a/packages/sdk/src/shims/ed25519.node.ts b/packages/sdk/src/shims/ed25519.node.ts new file mode 100644 index 000000000..2ebf319b4 --- /dev/null +++ b/packages/sdk/src/shims/ed25519.node.ts @@ -0,0 +1,9 @@ +// To add support for node versions <19. +// From https://www.npmjs.com/package/@noble/ed25519#usage +import { webcrypto } from 'node:crypto'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +if (!globalThis.crypto) globalThis.crypto = webcrypto; + +export * from '@noble/ed25519'; diff --git a/packages/sdk/src/signHelpers.ts b/packages/sdk/src/signHelpers.ts index dd8c865d7..9b28044a1 100644 --- a/packages/sdk/src/signHelpers.ts +++ b/packages/sdk/src/signHelpers.ts @@ -8,7 +8,7 @@ import { HexString, JsonString, } from './types.js'; -import { sign, verify } from '@noble/ed25519'; +import * as ed from '#ed25519'; import { Buffer } from 'buffer/index.js'; import * as AccountAddress from './types/AccountAddress.js'; import { sha256 } from './hash.js'; @@ -123,7 +123,7 @@ export const getSignature = async ( digest: ArrayBuffer, privateKey: HexString ): Promise => - Buffer.from(await sign(new Uint8Array(digest), privateKey)); + Buffer.from(await ed.signAsync(new Uint8Array(digest), privateKey)); /** * Creates an `AccountSigner` for an account which uses the first credential's first keypair. @@ -345,7 +345,7 @@ export async function verifyMessageSignature( ); } if ( - !(await verify( + !(await ed.verifyAsync( credentialSignature[Number(keyIndex)], digest, credentialKeys.keys[Number(keyIndex)].verifyKey diff --git a/packages/sdk/src/types/AccountAddress.ts b/packages/sdk/src/types/AccountAddress.ts index 80fdc0e5b..d080adb09 100644 --- a/packages/sdk/src/types/AccountAddress.ts +++ b/packages/sdk/src/types/AccountAddress.ts @@ -1,6 +1,18 @@ import bs58check from 'bs58check'; import { Buffer } from 'buffer/index.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; +import { Base58String } from '../types.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.AccountAddress; +type Serializable = Base58String; /** * Representation of an account address, which enforces that it: @@ -9,7 +21,7 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; */ class AccountAddress { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The account address represented in base58check. */ public readonly address: string, @@ -26,19 +38,13 @@ class AccountAddress { export type Type = AccountAddress; /** - * Type guard for AccountAddress - * @param {unknown} input Input to check. - * @returns {boolean} Boolean indicating whether input is an account address. + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} */ -export function isAccountAddress(input: unknown): input is AccountAddress { - return ( - typeof input === 'object' && - input !== null && - 'address' in input && - 'decodedAddress' in input && - typeof input.address === 'string' && - input.address.length === 50 - ); +export function instanceOf(value: unknown): value is AccountAddress { + return value instanceof AccountAddress; } /** @@ -199,3 +205,28 @@ export function toProto(accountAddress: AccountAddress): Proto.AccountAddress { export function equals(left: AccountAddress, right: AccountAddress): boolean { return left.address === right.address; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: AccountAddress): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.address, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromBase58 +); diff --git a/packages/sdk/src/types/BlockHash.ts b/packages/sdk/src/types/BlockHash.ts index 1d60b226a..99c248882 100644 --- a/packages/sdk/src/types/BlockHash.ts +++ b/packages/sdk/src/types/BlockHash.ts @@ -1,17 +1,29 @@ import type { HexString } from '../types.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; /** * The number of bytes used to represent a block hash. */ -const blockHashByteLength = 32; +const BLOCK_HASH_BYTE_LENGTH = 32; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.BlockHash; +type Serializable = HexString; /** * Represents a hash of a block in the chain. */ class BlockHash { + private typedJsonType = JSON_DISCRIMINATOR; + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal buffer of bytes representing the hash. */ public readonly buffer: Uint8Array @@ -23,6 +35,16 @@ class BlockHash { */ export type Type = BlockHash; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is BlockHash { + return value instanceof BlockHash; +} + /** * Create a BlockHash from a buffer of 32 bytes. * @param {ArrayBuffer} buffer Buffer containing 32 bytes for the hash. @@ -30,7 +52,7 @@ export type Type = BlockHash; * @returns {BlockHash} */ export function fromBuffer(buffer: ArrayBuffer): BlockHash { - if (buffer.byteLength !== blockHashByteLength) { + if (buffer.byteLength !== BLOCK_HASH_BYTE_LENGTH) { throw new Error( `Invalid transaction hash provided: Expected a buffer containing 32 bytes, instead got '${Buffer.from( buffer @@ -106,10 +128,35 @@ export function toBlockHashInput(blockHash: BlockHash): Proto.BlockHashInput { * @returns {boolean} True if they are equal. */ export function equals(left: BlockHash, right: BlockHash): boolean { - for (let i = 0; i < blockHashByteLength; i++) { + for (let i = 0; i < BLOCK_HASH_BYTE_LENGTH; i++) { if (left.buffer.at(i) !== right.buffer.at(i)) { return false; } } return true; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: BlockHash): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/CcdAmount.ts b/packages/sdk/src/types/CcdAmount.ts index 7e6e51088..8d95ce04d 100644 --- a/packages/sdk/src/types/CcdAmount.ts +++ b/packages/sdk/src/types/CcdAmount.ts @@ -1,7 +1,17 @@ import { Big, BigSource } from 'big.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; const MICRO_CCD_PER_CCD = 1_000_000; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.CcdAmount; +type Serializable = string; /** * Representation of a CCD amount. @@ -10,13 +20,13 @@ const MICRO_CCD_PER_CCD = 1_000_000; */ class CcdAmount { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal representation of Ccd amound in micro Ccd. */ public readonly microCcdAmount: bigint ) {} - toJSON(): string { + public toJSON(): string { return this.microCcdAmount.toString(); } } @@ -28,6 +38,16 @@ class CcdAmount { */ export type Type = CcdAmount; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is CcdAmount { + return value instanceof CcdAmount; +} + /** * Constructs a CcdAmount and checks that it is valid. It accepts a number, string, big, or bigint as parameter. * It can accept a string as parameter with either a comma or a dot as the decimal separator. @@ -182,3 +202,28 @@ export function toProto(amount: CcdAmount): Proto.Amount { value: amount.microCcdAmount, }; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: CcdAmount): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.microCcdAmount.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromMicroCcd +); diff --git a/packages/sdk/src/types/ContractAddress.ts b/packages/sdk/src/types/ContractAddress.ts index 91a1ac225..050a36f00 100644 --- a/packages/sdk/src/types/ContractAddress.ts +++ b/packages/sdk/src/types/ContractAddress.ts @@ -1,9 +1,20 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ContractAddress; +type Serializable = { index: string; subindex: string }; /** Address of a smart contract instance. */ class ContractAddress { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The index of the smart contract address. */ public readonly index: bigint, @@ -16,21 +27,13 @@ class ContractAddress { export type Type = ContractAddress; /** - * Type guard for ContractAddress - * @param {unknown} input Input to check. - * @returns {boolean} Boolean indicating whether input is a contract address. + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} */ -export function isContractAddress(input: unknown): input is ContractAddress { - return ( - typeof input === 'object' && - input !== null && - 'index' in input && - 'subindex' in input && - typeof input.index === 'bigint' && - typeof input.subindex === 'bigint' && - 0 <= input.index && - 0 <= input.subindex - ); +export function instanceOf(value: unknown): value is ContractAddress { + return value instanceof ContractAddress; } /** @@ -115,3 +118,34 @@ export function toProto( export function equals(left: ContractAddress, right: ContractAddress): boolean { return left.index === right.index && left.subindex === right.subindex; } + +const fromSerializable = (v: Serializable) => + new ContractAddress(BigInt(v.index), BigInt(v.subindex)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ContractAddress): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: { + index: value.index.toString(), + subindex: value.subindex.toString(), + }, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/ContractName.ts b/packages/sdk/src/types/ContractName.ts index 4bb9d2f4f..b94f1e264 100644 --- a/packages/sdk/src/types/ContractName.ts +++ b/packages/sdk/src/types/ContractName.ts @@ -1,10 +1,21 @@ import * as InitName from './InitName.js'; import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ContractName; +type Serializable = string; /** The name of a smart contract. Note: This does _not_ including the 'init_' prefix. */ class ContractName { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal string value of the contract name. */ public readonly value: string @@ -14,6 +25,16 @@ class ContractName { /** The name of a smart contract. Note: This does _not_ including the 'init_' prefix. */ export type Type = ContractName; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ContractName { + return value instanceof ContractName; +} + /** * Create a contract name from a string, ensuring it follows the format of a contract name. * @param {string} value The string of the contract name. @@ -99,3 +120,28 @@ export function fromSchemaValue(contractName: SchemaValue): ContractName { export function equals(left: ContractName, right: ContractName): boolean { return left.value === right.value; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: ContractName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/CredentialRegistrationId.ts b/packages/sdk/src/types/CredentialRegistrationId.ts index 8108f98cd..8db1c001f 100644 --- a/packages/sdk/src/types/CredentialRegistrationId.ts +++ b/packages/sdk/src/types/CredentialRegistrationId.ts @@ -1,6 +1,18 @@ import { HexString } from '../types.js'; import { isHex } from '../util.js'; import { Buffer } from 'buffer/index.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = + TypedJsonDiscriminator.CredentialRegistrationId; +type Serializable = string; /** * Representation of a credential registration id, which enforces that it: @@ -10,13 +22,13 @@ import { Buffer } from 'buffer/index.js'; */ class CredentialRegistrationId { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Representation of a credential registration id */ public readonly credId: string ) {} - toJSON(): string { + public toJSON(): string { return this.credId; } } @@ -29,6 +41,16 @@ class CredentialRegistrationId { */ export type Type = CredentialRegistrationId; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is CredentialRegistrationId { + return value instanceof CredentialRegistrationId; +} + /** * Construct a CredentialRegistrationId from a hex string. * @param {HexString} credId The hex encoding of the credential registration id. @@ -97,3 +119,30 @@ export function toHexString(cred: CredentialRegistrationId): HexString { export function toBuffer(cred: CredentialRegistrationId): Uint8Array { return Buffer.from(cred.credId, 'hex'); } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON( + value: CredentialRegistrationId +): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.credId, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/DataBlob.ts b/packages/sdk/src/types/DataBlob.ts index 22b69f724..fd74d8aeb 100644 --- a/packages/sdk/src/types/DataBlob.ts +++ b/packages/sdk/src/types/DataBlob.ts @@ -1,21 +1,63 @@ import { Buffer } from 'buffer/index.js'; import { packBufferWithWord16Length } from '../serializationHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; +import type { HexString } from '../types.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.DataBlob; +type Serializable = HexString; /** * Representation of a transfer's memo or a registerData transaction's data, which enforces that: * - the byte length is <= 256 */ export class DataBlob { - data: Buffer; + public readonly data: Buffer; + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; constructor(data: ArrayBuffer) { if (data.byteLength > 256) { throw new Error("A data blob's size cannot exceed 256 bytes"); } + this.data = Buffer.from(data); } - toJSON(): string { + public toJSON(): string { return packBufferWithWord16Length(this.data).toString('hex'); } + + /** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @returns {TypedJson} The transformed object. + */ + public toTypedJSON(): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: this.data.toString('hex'), + }; + } + + /** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode DataBlob}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {DataBlob} The parsed instance. + */ + public static fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + (v: Serializable) => { + const data = Buffer.from(v, 'hex'); + return new DataBlob(data); + } + ); } diff --git a/packages/sdk/src/types/Duration.ts b/packages/sdk/src/types/Duration.ts index 482a990d6..c1ee2bd2c 100644 --- a/packages/sdk/src/types/Duration.ts +++ b/packages/sdk/src/types/Duration.ts @@ -1,4 +1,15 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Duration; +type Serializable = string; /** * Type representing a duration of time down to milliseconds. @@ -6,7 +17,7 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; */ class Duration { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal value for representing a duration in milliseconds. */ public readonly value: bigint @@ -19,6 +30,16 @@ class Duration { */ export type Type = Duration; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Duration { + return value instanceof Duration; +} + /** * Construct a Duration from a given number of milliseconds. * @param {number} value Number of milliseconds @@ -147,3 +168,30 @@ export function toProto(duration: Duration): Proto.Duration { value: duration.value, }; } + +const fromSerializable = (v: Serializable) => fromMillis(BigInt(v)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: Duration): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/Energy.ts b/packages/sdk/src/types/Energy.ts index ddfe0a3e1..d5bc842f9 100644 --- a/packages/sdk/src/types/Energy.ts +++ b/packages/sdk/src/types/Energy.ts @@ -1,9 +1,24 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Energy; +type Serializable = string; /** Energy measure. Used as part of cost calculations for transactions. */ class Energy { + protected get serializable(): Serializable { + return this.value.toString(); + } + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal value for representing the energy. */ public readonly value: bigint @@ -13,6 +28,16 @@ class Energy { /** Energy measure. Used as part of cost calculations for transactions. */ export type Type = Energy; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Energy { + return value instanceof Energy; +} + /** * Construct an Energy type. * @param {bigint | number} value The measure of energy. @@ -47,3 +72,30 @@ export function toProto(energy: Energy): Proto.Energy { value: energy.value, }; } + +const fromSerializable = (v: Serializable) => create(BigInt(v)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: Energy): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/EntrypointName.ts b/packages/sdk/src/types/EntrypointName.ts index f7ff8343d..06214f584 100644 --- a/packages/sdk/src/types/EntrypointName.ts +++ b/packages/sdk/src/types/EntrypointName.ts @@ -1,12 +1,27 @@ import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.EntrypointName; +type Serializable = string; /** * Type representing an entrypoint of a smart contract. * @template S Use for using string literals for the type. */ -class EntrypointName { +class EntrypointName { + protected get serializable(): Serializable { + return this.value; + } + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal string value of the receive name. */ public readonly value: S @@ -18,6 +33,16 @@ class EntrypointName { */ export type Type = EntrypointName; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is EntrypointName { + return value instanceof EntrypointName; +} + /** * Create a smart contract entrypoint name from a string, ensuring it follows the required format. * @param {string} value The string with the entrypoint name. @@ -60,3 +85,30 @@ export function toString( ): S { return entrypointName.value; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ + value, +}: EntrypointName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/InitName.ts b/packages/sdk/src/types/InitName.ts index e5337bfe7..4ba9ec627 100644 --- a/packages/sdk/src/types/InitName.ts +++ b/packages/sdk/src/types/InitName.ts @@ -1,11 +1,26 @@ import * as ContractName from './ContractName.js'; import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.InitName; +type Serializable = string; /** The name of an init-function for a smart contract. Note: This is of the form 'init_'. */ class InitName { + protected get serializable(): Serializable { + return this.value; + } + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal string corresponding to the init-function. */ public readonly value: string @@ -15,6 +30,16 @@ class InitName { /** The name of an init-function for a smart contract. Note: This is of the form 'init_'. */ export type Type = InitName; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is InitName { + return value instanceof InitName; +} + /** * Create an InitName directly from a string, ensuring it follows the format of an init-function name. * @param {string} value String with the init-function name. @@ -22,10 +47,10 @@ export type Type = InitName; * @returns {InitName} */ export function fromString(value: string): InitName { - if (value.length <= 100) { + if (value.length > 100) { throw new Error('Invalid InitName: Can be atmost 100 characters long.'); } - if (value.startsWith('init_')) { + if (!value.startsWith('init_')) { throw new Error("Invalid InitName: Must be prefixed with 'init_'."); } if (value.includes('.')) { @@ -86,3 +111,28 @@ export function toProto(initName: InitName): Proto.InitName { value: initName.value, }; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: InitName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/ModuleClient.ts b/packages/sdk/src/types/ModuleClient.ts index a33250241..d167d9a0d 100644 --- a/packages/sdk/src/types/ModuleClient.ts +++ b/packages/sdk/src/types/ModuleClient.ts @@ -45,6 +45,9 @@ class ModuleClient { * Type representing a smart contract module deployed on chain. */ export type Type = ModuleClient; +export function instanceOf(value: unknown): value is ModuleClient { + return value instanceof ModuleClient; +} /** * Create a new `GenericModule` instance for interacting with a smart contract module on chain. diff --git a/packages/sdk/src/types/ModuleReference.ts b/packages/sdk/src/types/ModuleReference.ts index a69f38948..67f6fad8e 100644 --- a/packages/sdk/src/types/ModuleReference.ts +++ b/packages/sdk/src/types/ModuleReference.ts @@ -1,25 +1,37 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; import { Buffer } from 'buffer/index.js'; import { packBufferWithWord32Length } from '../serializationHelpers.js'; -import { HexString } from '../types.js'; +import type { HexString } from '../types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; /** * The number of bytes used to represent a block hash. */ -const moduleReferenceByteLength = 32; +const MODULE_REF_BYTE_LENGTH = 32; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ModuleReference; +type Serializable = HexString; /** * Reference to a smart contract module. */ class ModuleReference { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; constructor( /** Internal field, the module reference represented as a hex string. */ - public readonly moduleRef: string, + public readonly moduleRef: HexString, /** Internal field, buffer containing the 32 bytes for the module reference. */ public readonly decodedModuleRef: Uint8Array ) {} - toJSON(): string { + public toJSON(): string { return packBufferWithWord32Length(this.decodedModuleRef).toString( 'hex' ); @@ -31,6 +43,16 @@ class ModuleReference { */ export type Type = ModuleReference; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ModuleReference { + return value instanceof ModuleReference; +} + /** * Create a ModuleReference from a buffer of 32 bytes. * @param {ArrayBuffer} buffer Buffer containing 32 bytes for the hash. @@ -39,7 +61,7 @@ export type Type = ModuleReference; */ export function fromBuffer(buffer: ArrayBuffer): ModuleReference { const hex = Buffer.from(buffer).toString('hex'); - if (buffer.byteLength !== moduleReferenceByteLength) { + if (buffer.byteLength !== MODULE_REF_BYTE_LENGTH) { throw new Error( 'The provided moduleRef ' + hex + @@ -51,12 +73,12 @@ export function fromBuffer(buffer: ArrayBuffer): ModuleReference { /** * Create a ModuleReference from a hex string. - * @param {HexString} hex Hex encoding of the module reference. + * @param {HexString} moduleRef Hex encoding of the module reference. * @throws If the provided hex encoding does not correspond to a buffer of exactly 32 bytes. * @returns {ModuleReference} A module reference. */ -export function fromHexString(moduleRef: HexString) { - if (moduleRef.length !== moduleReferenceByteLength * 2) { +export function fromHexString(moduleRef: HexString): ModuleReference { + if (moduleRef.length !== MODULE_REF_BYTE_LENGTH * 2) { throw new Error( 'The provided moduleRef ' + moduleRef + @@ -69,6 +91,15 @@ export function fromHexString(moduleRef: HexString) { ); } +/** + * Get the module reference bytes encoded as hex. + * @param {ModuleReference} moduleReference The module reference. + * @returns {HexString} String with hex encoding. + */ +export function toHexString(moduleReference: ModuleReference): HexString { + return moduleReference.moduleRef; +} + /** * Convert module reference from its protobuf encoding. * @param {Proto.ModuleRef} moduleReference The module reference in protobuf. @@ -98,3 +129,33 @@ export function toProto(moduleReference: ModuleReference): Proto.ModuleRef { export function equals(left: ModuleReference, right: ModuleReference): boolean { return left.moduleRef === right.moduleRef; } + +const fromSerializable = (v: Serializable) => { + const data = Buffer.from(v, 'hex'); + return fromBuffer(data); +}; + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ModuleReference): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.moduleRef, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/Parameter.ts b/packages/sdk/src/types/Parameter.ts index 58e166cdd..46c6a19ad 100644 --- a/packages/sdk/src/types/Parameter.ts +++ b/packages/sdk/src/types/Parameter.ts @@ -3,11 +3,22 @@ import { SchemaType, serializeSchemaType } from '../schemaTypes.js'; import { serializeTypeValue } from '../schema.js'; import type { Base64String, HexString } from '../types.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Parameter; +type Serializable = HexString; /** Parameter for a smart contract entrypoint. */ class Parameter { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal buffer of bytes representing the parameter. */ public readonly buffer: Uint8Array @@ -17,6 +28,16 @@ class Parameter { /** Parameter for a smart contract entrypoint. */ export type Type = Parameter; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Parameter { + return value instanceof Parameter; +} + /** * Create an empty parameter. * @returns {Parameter} An empty parameter. @@ -123,3 +144,28 @@ export function toProto(parameter: Parameter): Proto.Parameter { value: parameter.buffer, }; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: Parameter): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/ReceiveName.ts b/packages/sdk/src/types/ReceiveName.ts index 11cd5ecbc..0cfa740a3 100644 --- a/packages/sdk/src/types/ReceiveName.ts +++ b/packages/sdk/src/types/ReceiveName.ts @@ -2,6 +2,17 @@ import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; import * as ContractName from './ContractName.js'; import * as EntrypointName from './EntrypointName.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ReceiveName; +type Serializable = string; /** * Represents a receive-function in a smart contract module. @@ -12,7 +23,7 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; */ class ReceiveName { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal string value of the receive name. */ public readonly value: string @@ -28,6 +39,16 @@ class ReceiveName { */ export type Type = ReceiveName; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ReceiveName { + return value instanceof ReceiveName; +} + /** * Create a ReceiveName. * @param {ContractName.Type} contractName The name of the smart contract using this receive-function. @@ -169,3 +190,28 @@ export function toProto(receiveName: ReceiveName): Proto.ReceiveName { export function equals(left: ReceiveName, right: ReceiveName): boolean { return left.value === right.value; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: ReceiveName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/ReturnValue.ts b/packages/sdk/src/types/ReturnValue.ts index c271a958a..15c8ae8f0 100644 --- a/packages/sdk/src/types/ReturnValue.ts +++ b/packages/sdk/src/types/ReturnValue.ts @@ -5,11 +5,22 @@ import type { HexString, SmartContractTypeValues, } from '../types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ReturnValue; +type Serializable = HexString; /** Return value from invoking a smart contract entrypoint. */ class ReturnValue { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal buffer of bytes representing the return type. */ public readonly buffer: Uint8Array @@ -19,6 +30,16 @@ class ReturnValue { /** Return value from invoking a smart contract entrypoint. */ export type Type = ReturnValue; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ReturnValue { + return value instanceof ReturnValue; +} + /** * Create an empty return value. * @returns {ReturnValue} An empty return value. @@ -90,3 +111,28 @@ export function parseWithSchemaTypeBase64( const schemaBytes = Buffer.from(schemaBase64, 'base64'); return deserializeTypeValue(returnValue.buffer, schemaBytes); } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ReturnValue): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/SequenceNumber.ts b/packages/sdk/src/types/SequenceNumber.ts index 408a777fb..40d629d5d 100644 --- a/packages/sdk/src/types/SequenceNumber.ts +++ b/packages/sdk/src/types/SequenceNumber.ts @@ -1,9 +1,20 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.SequenceNumber; +type Serializable = string; /** Transaction sequence number. (Formerly refered as Nonce) */ class SequenceNumber { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal value representing the sequence number. */ public readonly value: bigint @@ -13,6 +24,16 @@ class SequenceNumber { /** A transaction sequence number. (Formerly refered as Nonce) */ export type Type = SequenceNumber; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is SequenceNumber { + return value instanceof SequenceNumber; +} + /** * Construct an SequenceNumber type. * @param {bigint | number} sequenceNumber The account sequence number. @@ -49,3 +70,32 @@ export function toProto(sequenceNumber: SequenceNumber): Proto.SequenceNumber { value: sequenceNumber.value, }; } + +const fromSerializable = (v: Serializable) => create(BigInt(v)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ + value, +}: SequenceNumber): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/Timestamp.ts b/packages/sdk/src/types/Timestamp.ts index 759e0b5ec..eb8a51067 100644 --- a/packages/sdk/src/types/Timestamp.ts +++ b/packages/sdk/src/types/Timestamp.ts @@ -1,9 +1,20 @@ import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Timestamp; +type Serializable = string; /** Represents a timestamp. */ class Timestamp { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** The internal value for representing the timestamp as milliseconds since Unix epoch. */ public readonly value: bigint @@ -13,6 +24,16 @@ class Timestamp { /** Represents a timestamp. */ export type Type = Timestamp; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Timestamp { + return value instanceof Timestamp; +} + /** * Create a Timestamp from milliseconds since Unix epoch. * @param {number} value Milliseconds since Unix epoch. @@ -91,3 +112,30 @@ export function toProto(timestamp: Timestamp): Proto.Timestamp { value: timestamp.value, }; } + +const fromSerializable = (v: Serializable) => fromMillis(BigInt(v)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: Timestamp): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/TransactionExpiry.ts b/packages/sdk/src/types/TransactionExpiry.ts index 5f26bb029..8839def81 100644 --- a/packages/sdk/src/types/TransactionExpiry.ts +++ b/packages/sdk/src/types/TransactionExpiry.ts @@ -1,18 +1,29 @@ import { secondsSinceEpoch } from '../util.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.TransactionExpiry; +type Serializable = string; /** * Representation of a transaction expiry date. */ class TransactionExpiry { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal representation of expiry. Seconds since unix epoch */ public readonly expiryEpochSeconds: bigint ) {} - toJSON(): number { + public toJSON(): number { return Number(this.expiryEpochSeconds); } } @@ -22,6 +33,16 @@ class TransactionExpiry { */ export type Type = TransactionExpiry; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is TransactionExpiry { + return value instanceof TransactionExpiry; +} + /** * Construct a TransactionExpiry from a number of seconds since unix epoch. * @param {bigint | number} seconds Number of seconds since unix epoch. @@ -85,3 +106,30 @@ export function toProto(expiry: TransactionExpiry): Proto.TransactionTime { value: expiry.expiryEpochSeconds, }; } + +const fromSerializable = (v: Serializable) => fromEpochSeconds(BigInt(v)); + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: TransactionExpiry): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: value.expiryEpochSeconds.toString(), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/TransactionHash.ts b/packages/sdk/src/types/TransactionHash.ts index 8c842b933..7d801abcd 100644 --- a/packages/sdk/src/types/TransactionHash.ts +++ b/packages/sdk/src/types/TransactionHash.ts @@ -1,15 +1,26 @@ import type { HexString } from '../types.js'; import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.TransactionHash; +type Serializable = HexString; /** * The number of bytes used to represent a transaction hash. */ -const transactionHashByteLength = 32; +const TRANSACTION_HASH_BYTE_LENGTH = 32; /** Hash of a transaction. */ class TransactionHash { /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ - private __nominal = true; + private __type = JSON_DISCRIMINATOR; constructor( /** Internal buffer with the hash. */ public readonly buffer: Uint8Array @@ -19,6 +30,16 @@ class TransactionHash { /** Hash of a transaction. */ export type Type = TransactionHash; +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is TransactionHash { + return value instanceof TransactionHash; +} + /** * Create a TransactionHash from a buffer. * @param {ArrayBuffer} buffer Bytes for the transaction hash. Must be exactly 32 bytes. @@ -26,7 +47,7 @@ export type Type = TransactionHash; * @returns {TransactionHash} */ export function fromBuffer(buffer: ArrayBuffer): TransactionHash { - if (buffer.byteLength !== transactionHashByteLength) { + if (buffer.byteLength !== TRANSACTION_HASH_BYTE_LENGTH) { throw new Error( `Invalid transaction hash provided: Expected a buffer containing 32 bytes, instead got '${Buffer.from( buffer @@ -95,10 +116,35 @@ export function toProto( * @returns {boolean} True if they are equal. */ export function equals(left: TransactionHash, right: TransactionHash): boolean { - for (let i = 0; i < transactionHashByteLength; i++) { + for (let i = 0; i < TRANSACTION_HASH_BYTE_LENGTH; i++) { if (left.buffer.at(i) !== right.buffer.at(i)) { return false; } } return true; } + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: TransactionHash): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/json.ts b/packages/sdk/src/types/json.ts new file mode 100644 index 000000000..74e65e485 --- /dev/null +++ b/packages/sdk/src/types/json.ts @@ -0,0 +1,331 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Parameter from './Parameter.js'; +import * as ReturnValue from './ReturnValue.js'; +import * as SequenceNumber from './SequenceNumber.js'; +import * as Energy from './Energy.js'; +import * as TransactionHash from './TransactionHash.js'; +import * as BlockHash from './BlockHash.js'; +import * as ContractName from './ContractName.js'; +import * as InitName from './InitName.js'; +import * as ReceiveName from './ReceiveName.js'; +import * as CredentialRegistrationId from './CredentialRegistrationId.js'; +import * as AccountAddress from './AccountAddress.js'; +import * as ContractAddress from './ContractAddress.js'; +import * as EntrypointName from './EntrypointName.js'; +import * as Timestamp from './Timestamp.js'; +import * as Duration from './Duration.js'; +import * as CcdAmount from './CcdAmount.js'; +import * as TransactionExpiry from './TransactionExpiry.js'; +import * as ModuleReference from './ModuleReference.js'; +import { + DataBlob, + JSON_DISCRIMINATOR as DATA_BLOB_DISCRIMINATOR, +} from './DataBlob.js'; +import { TypedJson, isTypedJsonCandidate } from './util.js'; + +function reviveConcordiumTypes(value: unknown) { + if (isTypedJsonCandidate(value)) { + switch (value['@type']) { + case Parameter.JSON_DISCRIMINATOR: + return Parameter.fromTypedJSON(value); + case ReturnValue.JSON_DISCRIMINATOR: + return ReturnValue.fromTypedJSON(value); + case SequenceNumber.JSON_DISCRIMINATOR: + return SequenceNumber.fromTypedJSON(value); + case Energy.JSON_DISCRIMINATOR: + return Energy.fromTypedJSON(value); + case TransactionHash.JSON_DISCRIMINATOR: + return TransactionHash.fromTypedJSON(value); + case BlockHash.JSON_DISCRIMINATOR: + return BlockHash.fromTypedJSON(value); + case ContractName.JSON_DISCRIMINATOR: + return ContractName.fromTypedJSON(value); + case InitName.JSON_DISCRIMINATOR: + return InitName.fromTypedJSON(value); + case ReceiveName.JSON_DISCRIMINATOR: + return ReceiveName.fromTypedJSON(value); + case CredentialRegistrationId.JSON_DISCRIMINATOR: + return CredentialRegistrationId.fromTypedJSON(value); + case AccountAddress.JSON_DISCRIMINATOR: + return AccountAddress.fromTypedJSON(value); + case ContractAddress.JSON_DISCRIMINATOR: + return ContractAddress.fromTypedJSON(value); + case EntrypointName.JSON_DISCRIMINATOR: + return EntrypointName.fromTypedJSON(value); + case Timestamp.JSON_DISCRIMINATOR: + return Timestamp.fromTypedJSON(value); + case Duration.JSON_DISCRIMINATOR: + return Duration.fromTypedJSON(value); + case CcdAmount.JSON_DISCRIMINATOR: + return CcdAmount.fromTypedJSON(value); + case TransactionExpiry.JSON_DISCRIMINATOR: + return TransactionExpiry.fromTypedJSON(value); + case ModuleReference.JSON_DISCRIMINATOR: + return ModuleReference.fromTypedJSON(value); + case DATA_BLOB_DISCRIMINATOR: + return DataBlob.fromTypedJSON(value); + } + } + + return value; +} + +/** + * Acts as an inverse for {@linkcode jsonStringify} + */ +export function jsonParse( + input: string, + reviver?: (this: any, key: string, value: any) => any +): any { + return JSON.parse(input, (k, v) => + reviver === undefined + ? reviveConcordiumTypes(v) + : reviver(k, reviveConcordiumTypes(v)) + ); +} + +/** + * Replaces values of concordium domain types with values that can be revived into their original types. + */ +function transformConcordiumType( + value: unknown +): + | { transformed: true; value: TypedJson } + | { transformed: false; value: unknown } { + let newValue: TypedJson | undefined = undefined; + switch (true) { + case AccountAddress.instanceOf(value): + newValue = AccountAddress.toTypedJSON(value as AccountAddress.Type); + break; + case BlockHash.instanceOf(value): + newValue = BlockHash.toTypedJSON(value as BlockHash.Type); + break; + case CcdAmount.instanceOf(value): + newValue = CcdAmount.toTypedJSON(value as CcdAmount.Type); + break; + case ContractAddress.instanceOf(value): + newValue = ContractAddress.toTypedJSON( + value as ContractAddress.Type + ); + break; + case ContractName.instanceOf(value): + newValue = ContractName.toTypedJSON(value as ContractName.Type); + break; + case CredentialRegistrationId.instanceOf(value): + newValue = CredentialRegistrationId.toTypedJSON( + value as CredentialRegistrationId.Type + ); + break; + case value instanceof DataBlob: + newValue = (value as DataBlob).toTypedJSON(); + break; + case Duration.instanceOf(value): + newValue = Duration.toTypedJSON(value as Duration.Type); + break; + case Energy.instanceOf(value): + newValue = Energy.toTypedJSON(value as Energy.Type); + break; + case EntrypointName.instanceOf(value): + newValue = EntrypointName.toTypedJSON(value as EntrypointName.Type); + break; + case InitName.instanceOf(value): + newValue = InitName.toTypedJSON(value as InitName.Type); + break; + case ModuleReference.instanceOf(value): + newValue = ModuleReference.toTypedJSON( + value as ModuleReference.Type + ); + break; + case Parameter.instanceOf(value): + newValue = Parameter.toTypedJSON(value as Parameter.Type); + break; + case ReceiveName.instanceOf(value): + newValue = ReceiveName.toTypedJSON(value as ReceiveName.Type); + break; + case ReturnValue.instanceOf(value): + newValue = ReturnValue.toTypedJSON(value as ReturnValue.Type); + break; + case SequenceNumber.instanceOf(value): + newValue = SequenceNumber.toTypedJSON(value as SequenceNumber.Type); + break; + case Timestamp.instanceOf(value): + newValue = Timestamp.toTypedJSON(value as Timestamp.Type); + break; + case TransactionExpiry.instanceOf(value): + newValue = TransactionExpiry.toTypedJSON( + value as TransactionExpiry.Type + ); + break; + case TransactionHash.instanceOf(value): + newValue = TransactionHash.toTypedJSON( + value as TransactionHash.Type + ); + break; + } + + if (newValue !== undefined) { + return { transformed: true, value: newValue }; + } + + return { transformed: false, value }; +} + +type ReplacerFun = (this: any, key: string, value: any) => any; + +/** + * Thrown if a circular reference is found while trying to stringify object. + */ +export class JsonCircularReferenceError extends Error { + public override readonly name = 'CircularReferenceError'; + /** + * @param {string} key - The key the circular reference was found at. + */ + constructor(public readonly key: string) { + super(`Circular reference found in object at path ${key}`); + } +} + +/** + * Creates a replacer function which is a no-op, but throws {@link JsonCircularReferenceError} when finding circular references. + * Modified from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value. + * + * @throws {JsonCircularReferenceError} If a cyclic reference is found. + * @returns {ReplacerFun} replacer function, which throws when finding circular references. + * The function returned expects parent object to be accessible on `this` + * + * @example + * const check = getCheckCircular(); + * + * const circularReference = { otherData: 123 }; + * circularReference.myself = circularReference; + * + * for (cosnt key in circularReference) { + * check.call(circularReference, key, circularReference[key]); // throws `JsonCircularReferenceError` + * } + */ +function getCheckCircular(): ReplacerFun { + const ancestors: any[] = []; + return function (this: any, key: string, value: any) { + if (typeof value !== 'object' || value === null) { + return value; + } + // `this` is the object that value is contained in, + // i.e., its direct parent. + while (ancestors.length > 0 && ancestors.at(-1) !== this) { + ancestors.pop(); + } + if (ancestors.includes(value)) { + throw new JsonCircularReferenceError(key); + } + ancestors.push(value); + return value; + }; +} + +/** + * Transforms concordium domain types in an object of arbitrary depth in a non-recursive manner. + * + * @param {unknown} obj - The object to transform + * @param {ReplacerFun} [replacer] - An optional replacer function to run in addition to transforming concordium domain types. + * + * @throws {JsonCircularReferenceError} If a circular reference is found. + * @returns {any} The transformed object. + */ +function transformConcordiumTypes(obj: unknown, replacer?: ReplacerFun): any { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + type StackItem = { + obj: Record; + path: string[]; + }; + + const checkCircular = getCheckCircular(); + + const stack: StackItem[] = [{ obj, path: [] }]; + const result: Record = {}; + + while (stack.length) { + const { path, obj } = stack[0]; + + for (const k in obj) { + const originalValue = obj[k]; + checkCircular.call(obj, k, originalValue); // Throws if a circular reference is found. + + // Transform concordium types first. + const { transformed, value } = + transformConcordiumType(originalValue); + // Then run values through user defined replacer function. + const jsonValue = (value as any).toJSON?.(k) ?? value; + const replacedValue = + replacer?.call(obj, k, jsonValue) ?? jsonValue; + + // Find the node matching the path registered for the object. + const local = path.reduce((acc, key) => acc[key], result); + if (transformed) { + local[k] = replacedValue; + } else if ( + typeof replacedValue === 'object' && + replacedValue !== null + ) { + // If the value was not replaced and is a valid object, push it to the stack. + stack.push({ obj: replacedValue, path: [...path, k] }); + // And override the value with a shallow clone to avoid modifying the original. + local[k] = { ...replacedValue }; + } + } + + stack.shift(); + } + + return result; +} + +/** + * Stringify, which ensures concordium domain types are stringified in a restorable fashion. + * + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * + * @throws {JsonCircularReferenceError} If a circular reference is found. + */ +export function jsonStringify( + value: any, + replacer?: ReplacerFun, + space?: string | number +): string; + +/** + * Stringify, which ensures concordium domain types are stringified in a restorable fashion. + * + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer An array of strings and numbers that acts as an approved list for selecting the object properties that will be stringified. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + * + * @throws {JsonCircularReferenceError} If a circular reference is found. + */ +export function jsonStringify( + value: any, + replacer?: (number | string)[] | null, + space?: string | number +): string; +export function jsonStringify( + input: any, + replacer?: any, + space?: string | number +): string { + // Runs replace function for concordium types prior to JSON.stringify, as otherwise + // an attempt to run `toJSON` on objects is done before any replacer function. + const transformed = transformConcordiumTypes( + input, + typeof replacer === 'function' ? replacer : undefined + ); + return JSON.stringify( + transformed, + // Only add replacer if it hasn't already been run in the concordium transformer function. + typeof replacer === 'function' ? undefined : replacer, + space + ); +} diff --git a/packages/sdk/src/types/util.ts b/packages/sdk/src/types/util.ts new file mode 100644 index 000000000..ca17fa100 --- /dev/null +++ b/packages/sdk/src/types/util.ts @@ -0,0 +1,158 @@ +/** + * Discriminator for {@linkcode TypedJson}. The member used to identify each type is + * exported from each type module and can be accessed through named export `JSON_DISCRIMINATOR`. + */ +export enum TypedJsonDiscriminator { + AccountAddress = 'ccd_account_address', + BlockHash = 'ccd_block_hash', + CcdAmount = 'ccd_ccd_amount', + ContractAddress = 'ccd_contract_address', + ContractName = 'ccd_contract_name', + CredentialRegistrationId = 'ccd_cred_reg_id', + DataBlob = 'ccd_data_blob', + Duration = 'ccd_duration', + Energy = 'ccd_energy', + EntrypointName = 'ccd_entrypoint_name', + InitName = 'ccd_init_name', + ModuleReference = 'ccd_module_reference', + Parameter = 'ccd_parameter', + ReceiveName = 'ccd_receive_name', + ReturnValue = 'ccd_return_value', + SequenceNumber = 'ccd_sequence_number', + Timestamp = 'ccd_timestamp', + TransactionExpiry = 'ccd_transaction_expiry', + TransactionHash = 'ccd_transaction_hash', +} + +/** + * Type describing the JSON representation of strong types used in the SDK. + * + * @template V - The serializable JSON value + */ +export type TypedJson = { + /** The type discriminator */ + ['@type']: TypedJsonDiscriminator; + /** The serializable type value */ + value: V; +}; + +/** + * Type predeicate for {@linkcode TypedJson}. + * + * @param value value to test + * @returns boolean indicating whether `value` is {@linkcode TypedJson} + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isTypedJsonCandidate(value: unknown): value is TypedJson { + if (typeof value !== 'object' || value === null) { + return false; + } + + return ['@type', 'value'].every((name) => + Object.getOwnPropertyNames(value).includes(name) + ); +} + +/** + * Describes the type of the JsonParseError. + */ +export enum TypedJsonParseErrorCode { + /** Malformed JSON passed to parser function */ + MALFORMED = 'MALFORMED', + /** JSON passed to parser function had unexpected {@linkcode TypedJsonDiscriminator} type discriminator */ + WRONG_TYPE = 'WRONG_TYPE', + /** Value could not be parsed successfully */ + INVALID_VALUE = 'INVALID_VALUE', +} + +/** + * Error thrown from trying to parse objects of type {@linkcode TypedJson} + */ +export abstract class TypedJsonParseError extends Error { + public abstract readonly code: TypedJsonParseErrorCode; + private _name: string = 'TypedJsonParseError'; + + /** + * @param {string} message - The error message. + */ + constructor(message: string) { + super(message); + } + + public override get name() { + return `${this._name}.${this.code}`; + } +} + +export class TypedJsonMalformedError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.MALFORMED; +} + +export class TypedJsonWrongTypeError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.WRONG_TYPE; + + /** + * @param {TypedJsonDiscriminator} expected - The discriminator expected by the typed JSON parser. + * @param {TypedJsonDiscriminator} actual - The discriminator received by the typed JSON parser. + */ + constructor( + public readonly expected: TypedJsonDiscriminator, + public readonly actual: TypedJsonDiscriminator + ) { + super( + `Wrong type discriminator found in JSON. Expected "${expected}", found "${actual}"` + ); + } +} + +export class TypedJsonInvalidValueError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.INVALID_VALUE; + + /** + * @param {string} inner - The original cause of the error. + */ + constructor(public readonly inner: unknown) { + super(`Unable to parse value (${(inner as Error)?.message ?? inner})`); + + if (inner instanceof Error) { + this.stack = inner.stack ?? this.stack; + } + } +} + +/** + * Creates a function to convert {@linkcode TypedJson} to their corresponding type instance. + * + * @template V - The JSON value + * @template T - The type returned + * + * @param {TypedJsonDiscriminator} expectedTypeDiscriminator - The discriminator expected in the JSON string parsed + * @param {Function} toType - A function converting values of type `V` to instances of type `T` + * + * @throws {TypedJsonParseError} {@linkcode TypedJsonParseError} if the returned function fails to parse the passed value. + * + * @returns The JSON parser function + */ +export function makeFromTypedJson( + expectedTypeDiscriminator: TypedJsonDiscriminator, + toType: (value: V) => T +) { + return ({ ['@type']: type, value }: TypedJson): T | V => { + if (type === undefined || value === undefined) { + throw new TypedJsonMalformedError( + 'Expected both "@type" and "value" properties to be available in JSON' + ); + } + + if (expectedTypeDiscriminator !== type) { + throw new TypedJsonWrongTypeError(expectedTypeDiscriminator, type); + } + + try { + return toType(value); + } catch (e) { + // Value cannot be successfully parsed + throw new TypedJsonInvalidValueError(value); + } + }; +} diff --git a/packages/sdk/src/wasm/credentialDeploymentTransactions.ts b/packages/sdk/src/wasm/credentialDeploymentTransactions.ts index fdc4b8b35..888dad6f4 100644 --- a/packages/sdk/src/wasm/credentialDeploymentTransactions.ts +++ b/packages/sdk/src/wasm/credentialDeploymentTransactions.ts @@ -1,5 +1,5 @@ import { Buffer } from 'buffer/index.js'; -import { sign } from '@noble/ed25519'; +import * as ed from '#ed25519'; import * as wasm from '@concordium/rust-bindings/wallet'; import { AttributeKey, @@ -302,5 +302,5 @@ export async function signCredentialTransaction( signingKey: HexString ): Promise { const digest = getCredentialDeploymentSignDigest(credDeployment); - return Buffer.from(await sign(digest, signingKey)).toString('hex'); + return Buffer.from(await ed.signAsync(digest, signingKey)).toString('hex'); } diff --git a/packages/sdk/test/ci/HdWallet.test.ts b/packages/sdk/test/ci/HdWallet.test.ts index 8af7ceb1a..ce1ca8e20 100644 --- a/packages/sdk/test/ci/HdWallet.test.ts +++ b/packages/sdk/test/ci/HdWallet.test.ts @@ -3,7 +3,7 @@ import { ConcordiumHdWallet } from '../../src/wasm/HdWallet.js'; import { Buffer } from 'buffer/index.js'; export const TEST_SEED_1 = 'efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860'; -import * as ed from '@noble/ed25519'; +import * as ed from '#ed25519'; test('Mainnet signing key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Mainnet'); @@ -30,8 +30,8 @@ test('Mainnet public and signing key match', async () => { const privateKey = wallet.getAccountSigningKey(0, 0, 0); const publicKey = wallet.getAccountPublicKey(0, 0, 0); const message = 'abcd1234abcd5678'; - const signature = await ed.sign(message, privateKey.toString('hex')); - expect(await ed.verify(signature, message, publicKey)).toBeTruthy(); + const signature = await ed.signAsync(message, privateKey.toString('hex')); + expect(await ed.verifyAsync(signature, message, publicKey)).toBeTruthy(); }); test('Mainnet Id Cred Sec', () => { @@ -114,8 +114,8 @@ test('Testnet public and signing key match', async () => { const privateKey = wallet.getAccountSigningKey(0, 0, 0); const publicKey = wallet.getAccountPublicKey(0, 0, 0); const message = 'abcd1234abcd5678'; - const signature = await ed.sign(message, privateKey.toString('hex')); - expect(await ed.verify(signature, message, publicKey)).toBeTruthy(); + const signature = await ed.signAsync(message, privateKey.toString('hex')); + expect(await ed.verifyAsync(signature, message, publicKey)).toBeTruthy(); }); test('Testnet Id Cred Sec', () => { diff --git a/packages/sdk/test/ci/types/json.test.ts b/packages/sdk/test/ci/types/json.test.ts new file mode 100644 index 000000000..020a07098 --- /dev/null +++ b/packages/sdk/test/ci/types/json.test.ts @@ -0,0 +1,90 @@ +import { + Parameter, + ReturnValue, + SequenceNumber, + Energy, + TransactionHash, + BlockHash, + ContractName, + InitName, + ReceiveName, + CredentialRegistrationId, + AccountAddress, + ContractAddress, + EntrypointName, + Timestamp, + Duration, + CcdAmount, + TransactionExpiry, + ModuleReference, + DataBlob, + jsonStringify, + jsonParse, +} from '../../../src/pub/types.js'; +import { JsonCircularReferenceError } from '../../../src/types/json.js'; + +describe('JSON ID test', () => { + test('Stringified types are parsed correctly', () => { + const original = { + parameter: Parameter.fromHexString('010203'), + nested: { + returnValue: ReturnValue.fromHexString('020103'), + }, + sequenceNumber: SequenceNumber.create(1), + energy: Energy.create(123), + transactionhash: TransactionHash.fromHexString( + '443682391401cd5938a8b87275e5f5e6d0c8178d512391bca81d9a2a45f11a63' + ), + blockHash: BlockHash.fromHexString( + 'c2ef4acafd8ac8956ad941b4c4b87688baa714eb43510f078427db1b52e824e3 ' + ), + some: { + deeply: { + nested: { + contractName: ContractName.fromString('test-contract'), + }, + }, + }, + initName: InitName.fromString('init_another-contract'), + receiveName: ReceiveName.fromString('some-contract.receive'), + credRegId: CredentialRegistrationId.fromHexString( + '83e4b29e1e2582a6f1dcc93bf2610ce6b0a6ba89c8f03e661f403b4c2e055d3adb80d071c2723530926bb8aed3ed52b1' + ), + accountAddress: AccountAddress.fromBase58( + '35CJPZohio6Ztii2zy1AYzJKvuxbGG44wrBn7hLHiYLoF2nxnh' + ), + contractAddress: ContractAddress.create(1234), + entrypointName: EntrypointName.fromString('entrypoint'), + timestamp: Timestamp.fromDate(new Date()), + duration: Duration.fromMillis(100000), + ccdAmount: CcdAmount.fromMicroCcd(123), + transactionExpiry: TransactionExpiry.futureMinutes(5), + moduleRef: ModuleReference.fromHexString( + '5d99b6dfa7ba9dc0cac8626754985500d51d6d06829210748b3fd24fa30cde4a' + ), + dataBlob: new DataBlob(Buffer.from('030201', 'hex')), + }; + + const json = jsonStringify(original); + const parsed = jsonParse(json); + + expect(parsed).toEqual(original); + }); +}); + +describe('jsonStringify', () => { + test('Throws on circular reference', () => { + const obj = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (obj as any)['circular'] = obj; + + expect(() => jsonStringify(obj)).toThrowError( + JsonCircularReferenceError + ); + }); + + test('Allow non-circular references to same object', () => { + const other = { test: 1 }; + expect(() => jsonStringify([other, other])).not.toThrow(); + }); +}); diff --git a/packages/sdk/test/client/clientV2.test.ts b/packages/sdk/test/client/clientV2.test.ts index 99ee3e76e..95f8a2f81 100644 --- a/packages/sdk/test/client/clientV2.test.ts +++ b/packages/sdk/test/client/clientV2.test.ts @@ -19,7 +19,7 @@ import { getNodeClientV2, getNodeClientWeb, } from './testHelpers.js'; -import * as ed from '@noble/ed25519'; +import * as ed from '#ed25519'; import * as expected from './resources/expectedJsons.js'; import { TextEncoder, TextDecoder } from 'util'; @@ -494,7 +494,7 @@ test.each([clientV2, clientWeb])('createAccount', async (client) => { const signingKey1 = '1053de23867e0f92a48814aabff834e2ca0b518497abaef71cad4e1be506334a'; const signature = Buffer.from( - await ed.sign(hashToSign, signingKey1) + await ed.signAsync(hashToSign, signingKey1) ).toString('hex'); const signatures: string[] = [signature]; const payload = v1.serializeCredentialDeploymentPayload( diff --git a/packages/sdk/test/client/credentialDeployment.test.ts b/packages/sdk/test/client/credentialDeployment.test.ts index 2f3a1e47c..d95210ec2 100644 --- a/packages/sdk/test/client/credentialDeployment.test.ts +++ b/packages/sdk/test/client/credentialDeployment.test.ts @@ -12,7 +12,7 @@ import { serializeCredentialDeploymentTransactionForSubmission, } from '../../src/index.js'; import fs from 'fs'; -import * as ed from '@noble/ed25519'; +import * as ed from '#ed25519'; test('test deserialize credentialDeployment ', async () => { const identityInput: IdentityInput = getIdentityInput(); @@ -67,10 +67,10 @@ test('test deserialize credentialDeployment ', async () => { 'fcd0e499f5dc7a989a37f8c89536e9af956170d7f502411855052ff75cfc3646'; const signature1 = Buffer.from( - await ed.sign(hashToSign, signingKey1) + await ed.signAsync(hashToSign, signingKey1) ).toString('hex'); const signature2 = Buffer.from( - await ed.sign(hashToSign, signingKey2) + await ed.signAsync(hashToSign, signingKey2) ).toString('hex'); const signatures: string[] = [signature1, signature2]; @@ -97,4 +97,4 @@ test('test deserialize credentialDeployment ', async () => { expect(BigInt(deployment.transaction.expiry)).toEqual( credentialDeploymentTransaction.expiry.expiryEpochSeconds ); -}); +}, 8000); diff --git a/packages/sdk/test/client/resources/expectedJsons.ts b/packages/sdk/test/client/resources/expectedJsons.ts index 8a38147ba..891142fbf 100644 --- a/packages/sdk/test/client/resources/expectedJsons.ts +++ b/packages/sdk/test/client/resources/expectedJsons.ts @@ -309,11 +309,11 @@ export const accountList: AccountAddress.Type[] = [ '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', ].map(AccountAddress.fromBase58); -export const moduleList = [ +export const moduleList: ModuleReference.Type[] = [ '67d568433bd72e4326241f262213d77f446db8ba03dfba351ae35c1b2e7e5109', '6f0524700ed808a8fe0d7e23014c5138e4fac1fd8ec85c5e3591096f48609206', 'ceb018e4cd3456c0ccc0bca14285a69fd55f4cb09c322195d49c5c22f85930fe', -]; +].map(ModuleReference.fromHexString); export const ancestorList: BlockHash.Type[] = [ 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e', diff --git a/yarn.lock b/yarn.lock index 28eaa8961..ec4c3ab8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 6 cacheKey: 8 +"@aashutoshrathi/word-wrap@npm:^1.2.3": + version: 1.2.6 + resolution: "@aashutoshrathi/word-wrap@npm:1.2.6" + checksum: ada901b9e7c680d190f1d012c84217ce0063d8f5c5a7725bb91ec3c5ed99bb7572680eb2d2938a531ccbaec39a95422fcd8a6b4a13110c7d98dd75402f66a0cd + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.1.0": version: 2.2.0 resolution: "@ampproject/remapping@npm:2.2.0" @@ -25,15 +32,6 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:7.12.11": - version: 7.12.11 - resolution: "@babel/code-frame@npm:7.12.11" - dependencies: - "@babel/highlight": ^7.10.4 - checksum: 3963eff3ebfb0e091c7e6f99596ef4b258683e4ba8a134e4e95f77afe85be5c931e184fff6435fb4885d12eba04a5e25532f7fbc292ca13b48e7da943474e2f3 - languageName: node - linkType: hard - "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.18.6": version: 7.18.6 resolution: "@babel/code-frame@npm:7.18.6" @@ -372,7 +370,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.18.6": +"@babel/highlight@npm:^7.18.6": version: 7.18.6 resolution: "@babel/highlight@npm:7.18.6" dependencies: @@ -656,9 +654,11 @@ __metadata: version: 0.0.0-use.local resolution: "@concordium/ccd-js-gen@workspace:packages/ccd-js-gen" dependencies: + "@concordium/web-sdk": 6.x "@types/node": ^20.5.0 buffer: ^6.0.3 commander: ^11.0.0 + eslint: ^8.50.0 sanitize-filename: ^1.6.3 ts-morph: ^19.0.0 typescript: ^5.2.2 @@ -676,8 +676,9 @@ __metadata: "@concordium/ccd-js-gen": "workspace:^" "@concordium/web-sdk": "workspace:^" "@grpc/grpc-js": ^1.3.4 - "@noble/ed25519": ^1.7.1 + "@noble/ed25519": ^2.0.0 buffer: ^6.0.3 + eslint: ^8.50.0 meow: 11.0 node-fetch: ^3.3.2 ts-node: 10.9 @@ -690,6 +691,7 @@ __metadata: resolution: "@concordium/rust-bindings@workspace:packages/rust-bindings" dependencies: buffer: ^6.0.3 + eslint: ^8.50.0 rimraf: ^5.0.1 ts-loader: ^9.4.4 typescript: ^5.2.2 @@ -699,13 +701,13 @@ __metadata: languageName: unknown linkType: soft -"@concordium/web-sdk@workspace:^, @concordium/web-sdk@workspace:packages/sdk": +"@concordium/web-sdk@6.x, @concordium/web-sdk@workspace:^, @concordium/web-sdk@workspace:packages/sdk": version: 0.0.0-use.local resolution: "@concordium/web-sdk@workspace:packages/sdk" dependencies: "@concordium/rust-bindings": 1.2.0 "@grpc/grpc-js": ^1.9.4 - "@noble/ed25519": ^1.7.1 + "@noble/ed25519": ^2.0.0 "@protobuf-ts/grpc-transport": ^2.9.1 "@protobuf-ts/grpcweb-transport": ^2.9.1 "@protobuf-ts/plugin": ^2.9.1 @@ -719,6 +721,7 @@ __metadata: big.js: ^6.2.0 bs58check: ^3.0.1 buffer: ^6.0.3 + eslint: ^8.51.0 glob: ^10.3.10 grpc-tools: ^1.11.2 grpc_tools_node_protoc_ts: 5.3.0 @@ -751,7 +754,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.4.0": +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" dependencies: @@ -769,20 +772,34 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^0.4.3": - version: 0.4.3 - resolution: "@eslint/eslintrc@npm:0.4.3" +"@eslint-community/regexpp@npm:^4.6.1": + version: 4.9.1 + resolution: "@eslint-community/regexpp@npm:4.9.1" + checksum: 06fb839e9c756f6375cc545c2f2e05a0a64576bd6370e8e3c07983fd29a3d6e164ef4aa48a361f7d27e6713ab79c83053ff6a2ccb78748bc955e344279c4a3b6 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^2.1.2": + version: 2.1.2 + resolution: "@eslint/eslintrc@npm:2.1.2" dependencies: ajv: ^6.12.4 - debug: ^4.1.1 - espree: ^7.3.0 - globals: ^13.9.0 - ignore: ^4.0.6 + debug: ^4.3.2 + espree: ^9.6.0 + globals: ^13.19.0 + ignore: ^5.2.0 import-fresh: ^3.2.1 - js-yaml: ^3.13.1 - minimatch: ^3.0.4 + js-yaml: ^4.1.0 + minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: 03a7704150b868c318aab6a94d87a33d30dc2ec579d27374575014f06237ba1370ae11178db772f985ef680d469dc237e7b16a1c5d8edaaeb8c3733e7a95a6d3 + checksum: bc742a1e3b361f06fedb4afb6bf32cbd27171292ef7924f61c62f2aed73048367bcc7ac68f98c06d4245cd3fabc43270f844e3c1699936d4734b3ac5398814a7 + languageName: node + linkType: hard + +"@eslint/js@npm:8.51.0": + version: 8.51.0 + resolution: "@eslint/js@npm:8.51.0" + checksum: 0228bf1e1e0414843e56d9ff362a2a72d579c078f93174666f29315690e9e30a8633ad72c923297f7fd7182381b5a476805ff04dac8debe638953eb1ded3ac73 languageName: node linkType: hard @@ -817,18 +834,25 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.5.0": - version: 0.5.0 - resolution: "@humanwhocodes/config-array@npm:0.5.0" +"@humanwhocodes/config-array@npm:^0.11.11": + version: 0.11.11 + resolution: "@humanwhocodes/config-array@npm:0.11.11" dependencies: - "@humanwhocodes/object-schema": ^1.2.0 + "@humanwhocodes/object-schema": ^1.2.1 debug: ^4.1.1 - minimatch: ^3.0.4 - checksum: 44ee6a9f05d93dd9d5935a006b17572328ba9caff8002442f601736cbda79c580cc0f5a49ce9eb88fbacc5c3a6b62098357c2e95326cd17bb9f1a6c61d6e95e7 + minimatch: ^3.0.5 + checksum: db84507375ab77b8ffdd24f498a5b49ad6b64391d30dd2ac56885501d03964d29637e05b1ed5aefa09d57ac667e28028bc22d2da872bfcd619652fbdb5f4ca19 languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^1.2.0": +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^1.2.1": version: 1.2.1 resolution: "@humanwhocodes/object-schema@npm:1.2.1" checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 @@ -1260,10 +1284,10 @@ __metadata: languageName: node linkType: hard -"@noble/ed25519@npm:^1.7.1": - version: 1.7.1 - resolution: "@noble/ed25519@npm:1.7.1" - checksum: b8e50306ac70f5cecc349111997e72e897b47a28d406b96cf95d0ebe7cbdefb8380d26117d7847d94102281db200aa3a494e520f9fc12e2f292e0762cb0fa333 +"@noble/ed25519@npm:^2.0.0": + version: 2.0.0 + resolution: "@noble/ed25519@npm:2.0.0" + checksum: 4404deb3ca4f7a07863a362d697dc624ff0e82083ca4e41f976f76ec6fe879363b4722529389aa1ae280fe62558d9f229c8403b0a077add8b5f1ec1290d6e2d7 languageName: node linkType: hard @@ -1298,7 +1322,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -2150,7 +2174,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.3.1": +"acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -2166,15 +2190,6 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^7.4.0": - version: 7.4.1 - resolution: "acorn@npm:7.4.1" - bin: - acorn: bin/acorn - checksum: 1860f23c2107c910c6177b7b7be71be350db9e1080d814493fae143ae37605189504152d1ba8743ba3178d0b37269ce1ffc42b101547fdc1827078f82671e407 - languageName: node - linkType: hard - "acorn@npm:^8.4.1": version: 8.7.1 resolution: "acorn@npm:8.7.1" @@ -2184,7 +2199,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.7.1, acorn@npm:^8.8.2": +"acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.10.0 resolution: "acorn@npm:8.10.0" bin: @@ -2232,7 +2247,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.12.4, ajv@npm:^6.12.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -2244,25 +2259,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^8.0.1": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" - dependencies: - fast-deep-equal: ^3.1.1 - json-schema-traverse: ^1.0.0 - require-from-string: ^2.0.2 - uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef - languageName: node - linkType: hard - -"ansi-colors@npm:^4.1.1": - version: 4.1.3 - resolution: "ansi-colors@npm:4.1.3" - checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -3132,7 +3128,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -3417,15 +3413,6 @@ __metadata: languageName: node linkType: hard -"enquirer@npm:^2.3.5": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" - dependencies: - ansi-colors: ^4.1.1 - checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 - languageName: node - linkType: hard - "entities@npm:^4.2.0, entities@npm:^4.4.0": version: 4.5.0 resolution: "entities@npm:4.5.0" @@ -3655,7 +3642,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": +"eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -3665,94 +3652,78 @@ __metadata: languageName: node linkType: hard -"eslint-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "eslint-utils@npm:2.1.0" +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" dependencies: - eslint-visitor-keys: ^1.1.0 - checksum: 27500938f348da42100d9e6ad03ae29b3de19ba757ae1a7f4a087bdcf83ac60949bbb54286492ca61fac1f5f3ac8692dd21537ce6214240bf95ad0122f24d71d - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^1.1.0, eslint-visitor-keys@npm:^1.3.0": - version: 1.3.0 - resolution: "eslint-visitor-keys@npm:1.3.0" - checksum: 37a19b712f42f4c9027e8ba98c2b06031c17e0c0a4c696cd429bd9ee04eb43889c446f2cd545e1ff51bef9593fcec94ecd2c2ef89129fcbbf3adadbef520376a - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^2.0.0": - version: 2.1.0 - resolution: "eslint-visitor-keys@npm:2.1.0" - checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d + esrecurse: ^4.3.0 + estraverse: ^5.2.0 + checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1": +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 languageName: node linkType: hard -"eslint@npm:^7.29.0": - version: 7.32.0 - resolution: "eslint@npm:7.32.0" +"eslint@npm:^8.50.0, eslint@npm:^8.51.0": + version: 8.51.0 + resolution: "eslint@npm:8.51.0" dependencies: - "@babel/code-frame": 7.12.11 - "@eslint/eslintrc": ^0.4.3 - "@humanwhocodes/config-array": ^0.5.0 - ajv: ^6.10.0 + "@eslint-community/eslint-utils": ^4.2.0 + "@eslint-community/regexpp": ^4.6.1 + "@eslint/eslintrc": ^2.1.2 + "@eslint/js": 8.51.0 + "@humanwhocodes/config-array": ^0.11.11 + "@humanwhocodes/module-importer": ^1.0.1 + "@nodelib/fs.walk": ^1.2.8 + ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 - debug: ^4.0.1 + debug: ^4.3.2 doctrine: ^3.0.0 - enquirer: ^2.3.5 escape-string-regexp: ^4.0.0 - eslint-scope: ^5.1.1 - eslint-utils: ^2.1.0 - eslint-visitor-keys: ^2.0.0 - espree: ^7.3.1 - esquery: ^1.4.0 + eslint-scope: ^7.2.2 + eslint-visitor-keys: ^3.4.3 + espree: ^9.6.1 + esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 - functional-red-black-tree: ^1.0.1 - glob-parent: ^5.1.2 - globals: ^13.6.0 - ignore: ^4.0.6 - import-fresh: ^3.0.0 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + globals: ^13.19.0 + graphemer: ^1.4.0 + ignore: ^5.2.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 - js-yaml: ^3.13.1 + is-path-inside: ^3.0.3 + js-yaml: ^4.1.0 json-stable-stringify-without-jsonify: ^1.0.1 levn: ^0.4.1 lodash.merge: ^4.6.2 - minimatch: ^3.0.4 + minimatch: ^3.1.2 natural-compare: ^1.4.0 - optionator: ^0.9.1 - progress: ^2.0.0 - regexpp: ^3.1.0 - semver: ^7.2.1 - strip-ansi: ^6.0.0 - strip-json-comments: ^3.1.0 - table: ^6.0.9 + optionator: ^0.9.3 + strip-ansi: ^6.0.1 text-table: ^0.2.0 - v8-compile-cache: ^2.0.3 bin: eslint: bin/eslint.js - checksum: cc85af9985a3a11085c011f3d27abe8111006d34cc274291b3c4d7bea51a4e2ff6135780249becd919ba7f6d6d1ecc38a6b73dacb6a7be08d38453b344dc8d37 + checksum: 214fa5d1fcb67af1b8992ce9584ccd85e1aa7a482f8b8ea5b96edc28fa838a18a3b69456db45fc1ed3ef95f1e9efa9714f737292dc681e572d471d02fda9649c languageName: node linkType: hard -"espree@npm:^7.3.0, espree@npm:^7.3.1": - version: 7.3.1 - resolution: "espree@npm:7.3.1" +"espree@npm:^9.6.0, espree@npm:^9.6.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" dependencies: - acorn: ^7.4.0 - acorn-jsx: ^5.3.1 - eslint-visitor-keys: ^1.3.0 - checksum: aa9b50dcce883449af2e23bc2b8d9abb77118f96f4cb313935d6b220f77137eaef7724a83c3f6243b96bc0e4ab14766198e60818caad99f9519ae5a336a39b45 + acorn: ^8.9.0 + acorn-jsx: ^5.3.2 + eslint-visitor-keys: ^3.4.1 + checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9 languageName: node linkType: hard @@ -3766,12 +3737,12 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" +"esquery@npm:^1.4.2": + version: 1.5.0 + resolution: "esquery@npm:1.5.0" dependencies: estraverse: ^5.1.0 - checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 + checksum: aefb0d2596c230118656cd4ec7532d447333a410a48834d80ea648b1e7b5c9bc9ed8b5e33a89cb04e487b60d622f44cf5713bf4abed7c97343edefdc84a35900 languageName: node linkType: hard @@ -4121,13 +4092,6 @@ __metadata: languageName: node linkType: hard -"functional-red-black-tree@npm:^1.0.1": - version: 1.0.1 - resolution: "functional-red-black-tree@npm:1.0.1" - checksum: ca6c170f37640e2d94297da8bb4bf27a1d12bea3e00e6a3e007fd7aa32e37e000f5772acf941b4e4f3cf1c95c3752033d0c509af157ad8f526e7f00723b9eb9f - languageName: node - linkType: hard - "functions-have-names@npm:^1.2.2": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" @@ -4233,6 +4197,15 @@ __metadata: languageName: node linkType: hard +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: ^4.0.3 + checksum: c13ee97978bef4f55106b71e66428eb1512e71a7466ba49025fc2aec59a5bfb0954d5abd58fc5ee6c9b076eef4e1f6d3375c2e964b88466ca390da4419a786a8 + languageName: node + linkType: hard + "glob-to-regexp@npm:^0.4.1": version: 0.4.1 resolution: "glob-to-regexp@npm:0.4.1" @@ -4319,12 +4292,12 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.6.0, globals@npm:^13.9.0": - version: 13.16.0 - resolution: "globals@npm:13.16.0" +"globals@npm:^13.19.0": + version: 13.23.0 + resolution: "globals@npm:13.23.0" dependencies: type-fest: ^0.20.2 - checksum: e571b28462b8922a29ac78c8df89848cfd5dc9bdd5d8077440c022864f512a4aae82e7561a2f366337daa86fd4b366aec16fd3f08686de387e4089b01be6cb14 + checksum: 194c97cf8d1ef6ba59417234c2386549c4103b6e5f24b1ff1952de61a4753e5d2069435ba629de711a6480b1b1d114a98e2ab27f85e966d5a10c319c3bbd3dc3 languageName: node linkType: hard @@ -4611,13 +4584,6 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^4.0.6": - version: 4.0.6 - resolution: "ignore@npm:4.0.6" - checksum: 248f82e50a430906f9ee7f35e1158e3ec4c3971451dd9f99c9bc1548261b4db2b99709f60ac6c6cac9333494384176cc4cc9b07acbe42d52ac6a09cad734d800 - languageName: node - linkType: hard - "ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" @@ -4632,7 +4598,7 @@ __metadata: languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.2.1": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -4865,6 +4831,13 @@ __metadata: languageName: node linkType: hard +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 + languageName: node + linkType: hard + "is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" @@ -5582,13 +5555,6 @@ __metadata: languageName: node linkType: hard -"json-schema-traverse@npm:^1.0.0": - version: 1.0.0 - resolution: "json-schema-traverse@npm:1.0.0" - checksum: 02f2f466cdb0362558b2f1fd5e15cce82ef55d60cd7f8fa828cf35ba74330f8d767fcae5c5c2adb7851fa811766c694b9405810879bc4e1ddd78a7c0e03658ad - languageName: node - linkType: hard - "json-stable-stringify-without-jsonify@npm:^1.0.1": version: 1.0.1 resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" @@ -5818,13 +5784,6 @@ __metadata: languageName: node linkType: hard -"lodash.truncate@npm:^4.4.2": - version: 4.4.2 - resolution: "lodash.truncate@npm:4.4.2" - checksum: b463d8a382cfb5f0e71c504dcb6f807a7bd379ff1ea216669aa42c52fc28c54e404bfbd96791aa09e6df0de2c1d7b8f1b7f4b1a61f324d38fe98bc535aeee4f5 - languageName: node - linkType: hard - "lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -6143,7 +6102,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -6589,17 +6548,17 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.1": - version: 0.9.1 - resolution: "optionator@npm:0.9.1" +"optionator@npm:^0.9.3": + version: 0.9.3 + resolution: "optionator@npm:0.9.3" dependencies: + "@aashutoshrathi/word-wrap": ^1.2.3 deep-is: ^0.1.3 fast-levenshtein: ^2.0.6 levn: ^0.4.1 prelude-ls: ^1.2.1 type-check: ^0.4.0 - word-wrap: ^1.2.3 - checksum: dbc6fa065604b24ea57d734261914e697bd73b69eff7f18e967e8912aa2a40a19a9f599a507fa805be6c13c24c4eae8c71306c239d517d42d4c041c942f508a0 + checksum: 09281999441f2fe9c33a5eeab76700795365a061563d66b098923eb719251a42bdbe432790d35064d0816ead9296dbeb1ad51a733edf4167c96bd5d0882e428a languageName: node linkType: hard @@ -6937,7 +6896,7 @@ __metadata: languageName: node linkType: hard -"progress@npm:^2.0.0, progress@npm:^2.0.3": +"progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 @@ -7113,13 +7072,6 @@ __metadata: languageName: node linkType: hard -"regexpp@npm:^3.1.0": - version: 3.2.0 - resolution: "regexpp@npm:3.2.0" - checksum: a78dc5c7158ad9ddcfe01aa9144f46e192ddbfa7b263895a70a5c6c73edd9ce85faf7c0430e59ac38839e1734e275b9c3de5c57ee3ab6edc0e0b1bdebefccef8 - languageName: node - linkType: hard - "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -7127,13 +7079,6 @@ __metadata: languageName: node linkType: hard -"require-from-string@npm:^2.0.2": - version: 2.0.2 - resolution: "require-from-string@npm:2.0.2" - checksum: a03ef6895445f33a4015300c426699bc66b2b044ba7b670aa238610381b56d3f07c686251740d575e22f4c87531ba662d06937508f0f3c0f1ddc04db3130560b - languageName: node - linkType: hard - "resolve-cwd@npm:^3.0.0": version: 3.0.0 resolution: "resolve-cwd@npm:3.0.0" @@ -7249,7 +7194,7 @@ __metadata: dependencies: "@typescript-eslint/eslint-plugin": ^6.7.0 "@typescript-eslint/parser": ^6.7.0 - eslint: ^7.29.0 + eslint: ^8.51.0 eslint-config-prettier: ^8.3.0 eslint-import-resolver-exports: ^1.0.0-beta.5 eslint-import-resolver-typescript: ^2.7.1 @@ -7386,25 +7331,25 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.2.1, semver@npm:^7.3.5": - version: 7.3.7 - resolution: "semver@npm:7.3.7" +"semver@npm:^7.3.4": + version: 7.3.8 + resolution: "semver@npm:7.3.8" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 languageName: node linkType: hard -"semver@npm:^7.3.4": - version: 7.3.8 - resolution: "semver@npm:7.3.8" +"semver@npm:^7.3.5": + version: 7.3.7 + resolution: "semver@npm:7.3.7" dependencies: lru-cache: ^6.0.0 bin: semver: bin/semver.js - checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 languageName: node linkType: hard @@ -7775,7 +7720,7 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": +"strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 @@ -7823,19 +7768,6 @@ __metadata: languageName: node linkType: hard -"table@npm:^6.0.9": - version: 6.8.0 - resolution: "table@npm:6.8.0" - dependencies: - ajv: ^8.0.1 - lodash.truncate: ^4.4.2 - slice-ansi: ^4.0.0 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - checksum: 5b07fe462ee03d2e1fac02cbb578efd2e0b55ac07e3d3db2e950aa9570ade5a4a2b8d3c15e9f25c89e4e50b646bc4269934601ee1eef4ca7968ad31960977690 - languageName: node - linkType: hard - "tapable@npm:^2.1.1, tapable@npm:^2.2.0": version: 2.2.1 resolution: "tapable@npm:2.2.1" @@ -8339,13 +8271,6 @@ __metadata: languageName: node linkType: hard -"v8-compile-cache@npm:^2.0.3": - version: 2.3.0 - resolution: "v8-compile-cache@npm:2.3.0" - checksum: adb0a271eaa2297f2f4c536acbfee872d0dd26ec2d76f66921aa7fc437319132773483344207bdbeee169225f4739016d8d2dbf0553913a52bb34da6d0334f8e - languageName: node - linkType: hard - "v8-to-istanbul@npm:^9.0.1": version: 9.1.0 resolution: "v8-to-istanbul@npm:9.1.0" @@ -8568,13 +8493,6 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:^1.2.3": - version: 1.2.3 - resolution: "word-wrap@npm:1.2.3" - checksum: 30b48f91fcf12106ed3186ae4fa86a6a1842416df425be7b60485de14bec665a54a68e4b5156647dec3a70f25e84d270ca8bc8cd23182ed095f5c7206a938c1f - languageName: node - linkType: hard - "wordwrap@npm:^1.0.0": version: 1.0.0 resolution: "wordwrap@npm:1.0.0"