Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@vechain/sdk-core

Package Overview
Dependencies
Maintainers
7
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vechain/sdk-core - npm Package Compare versions

Comparing version 1.0.0-beta.16 to 1.0.0-beta.17

15

package.json
{
"name": "@vechain/sdk-core",
"version": "1.0.0-beta.16",
"version": "1.0.0-beta.17",
"description": "Includes modules for fundamental operations like hashing and cryptography",

@@ -40,14 +40,17 @@ "author": "vechain Foundation",

"@ethereumjs/rlp": "^5.0.2",
"@noble/ciphers": "^0.5.2",
"@scure/bip32": "^1.4.0",
"@scure/bip39": "^1.3.0",
"@types/elliptic": "^6.4.18",
"@vechain/sdk-errors": "1.0.0-beta.16",
"@vechain/sdk-logging": "1.0.0-beta.16",
"@vechain/sdk-errors": "1.0.0-beta.17",
"@vechain/sdk-logging": "1.0.0-beta.17",
"bignumber.js": "^9.1.2",
"blakejs": "^1.2.1",
"elliptic": "^6.5.5",
"ethers": "6.13.0",
"fast-json-stable-stringify": "^2.1.0",
"@noble/ciphers": "^0.5.2"
"ethers": "6.13.1",
"fast-json-stable-stringify": "^2.1.0"
},
"devDependencies": {
"thor-devkit": "^2.0.9"
}
}

186

src/certificate/certificate.ts
import fastJsonStableStringify from 'fast-json-stable-stringify';
import { Hex, Hex0x } from '../utils';
import { addressUtils } from '../address';
import { assert, buildError, CERTIFICATE } from '@vechain/sdk-errors';
import { blake2b256 } from '../hash';
import { assert, CERTIFICATE } from '@vechain/sdk-errors';
import { blake2b256, NORMALIZATION_FORM_CANONICAL_COMPOSITION } from '../hash';
import { hexToBytes } from '@noble/curves/abstract/utils';

@@ -11,31 +11,62 @@ import { secp256k1 } from '../secp256k1';

/**
* Encodes a certificate object to a JSON string.
* Used to encode strings into UInt8Array
*
* The JSON representation of the signer's address is represented according the
* [EIP/ERC-55: Mixed-case checksum address encoding](https://eips.ethereum.org/EIPS/eip-55).
* @see {https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder TextEncoder}
*/
const TEXT_ENCODER = new TextEncoder();
/**
* Encodes a certificate object to an array of bytes of its JSON representation after the following
* normalization operations are applied:
* * only the properties defined in the {@link Certificate} interface are evaluated;
* * the properties are sorted in ascending alphabetic order;
* * the key/value properties are delimited with `"`;
* * any not meaningful blank characters are ignored;
* * the `signer` property is a hexadecimal address represented lowercase to back compatible with the certificates
* not implementing the [EIP/ERC-55: Mixed-case checksum address encoding](https://eips.ethereum.org/EIPS/eip-55);
* * the UTF-8 code is normalized according the
* [normalization form for canonical composition](https://en.wikipedia.org/wiki/Unicode_equivalence#Normal_forms)
*
* Secure audit function.
* - {@link addressUtils.toERC55Checksum}
*
* @param {Certificate} cert - The certificate object to encode.
* @return {string} - The encoded JSON string.
* @return {Uint8Array} - The byte encoded certificate.
*
* @throws {InvalidAddressError} if `address` is not a valid hexadecimal
* representation 40 digits long, prefixed with `0x`.
*
* @see {NORMALIZATION_FORM_CANONICAL_COMPOSITION}
* @see {TEXT_ENCODER}
* @see {https://www.npmjs.com/package/fast-json-stable-stringify fastJsonStableStringify}
* @see {sign}
* @see {verify}
*/
function encode(cert: Certificate): string {
return fastJsonStableStringify({
...cert,
signer: addressUtils.toERC55Checksum(cert.signer),
signature: cert.signature
});
function encode(cert: Certificate): Uint8Array {
return TEXT_ENCODER.encode(
// The following `fastJsonStableStringify` strips blank chars and serialize alphabetical sorted properties.
fastJsonStableStringify({
purpose: cert.purpose,
payload: {
type: cert.payload.type,
content: cert.payload.content
},
domain: cert.domain,
timestamp: cert.timestamp,
signer: cert.signer.toLowerCase()
}).normalize(NORMALIZATION_FORM_CANONICAL_COMPOSITION)
);
}
/**
* Matches a certificate against a given address and signature.
* Signs a certificate using a private key.
*
* This method is insensitive to the case representation of the signer's address.
* The signature is computed encoding the certificate according the following normalization rules:
* * only the properties defined in the {@link Certificate} interface are evaluated;
* * the properties are sorted in ascending alphabetic order;
* * the key/value properties are delimited with `"`;
* * any not meaningful blank characters are ignored;
* * the `signer` property is a hexadecimal address represented lowercase to back compatible with the certificates
* not implementing the [EIP/ERC-55: Mixed-case checksum address encoding](https://eips.ethereum.org/EIPS/eip-55);
* * the UTF-8 code is normalized according the
* [normalization form for canonical composition](https://en.wikipedia.org/wiki/Unicode_equivalence#Normal_forms).
*
* The [BLAKE2](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2) hash is computed from the encoded
* certificate, then the hash is signed using the [SECP256K1](https://en.bitcoin.it/wiki/Secp256k1) parameters.
*
* [EIP/ERC-55: Mixed-case checksum address encoding](https://eips.ethereum.org/EIPS/eip-55).

@@ -46,54 +77,28 @@ * is supported.

* - {@link blake2b256};
* - {@link secp256k1.recover}.
* - {@link secp256k1.sign}.
*
* @param {Uint8Array} cert - The certificate to match. computed from the certificate without the `signature` property.
* @param {string} address - The address to match against, optionally prefixed with `0x`.
* @param {string} signature - The signature to verify expressed in hexadecimal form, optionally prefixed with `0x`.
* @param {Certificate} cert - The certificate to be signed.
* Any instance extending the {@link Certificate} interface is supported.
* @param {Uint8Array} privateKey - The private key used for signing.
*
* @returns {void} - No return value.
* @returns {Certificate} - A new instance of the certificate with the signature added.
*
* @throws CertificateInvalidSignatureFormatError - If the certificate signature's is not a valid hexadecimal expression prefixed with `0x`.
* @throws CertificateNotSignedError - If the certificate is not signed.
* @throws CertificateInvalidSignerError - If the certificate's signature's doesn't match with the signer;s public key.
* @throws {InvalidSecp256k1PrivateKeyError} - If the private key is invalid.
*
*/
function match(cert: Uint8Array, address: string, signature: string): void {
// Invalid hexadecimal as signature.
assert(
'certificate.match',
Hex0x.isValid(signature, true, true),
CERTIFICATE.CERTIFICATE_INVALID_SIGNATURE_FORMAT,
'Verification failed: signature format is invalid.',
{ signature }
);
try {
// The `encode` method could throw `InvalidAddressError`.
const signingHash = blake2b256(cert, 'buffer');
const signingPublicKey = secp256k1.recover(
signingHash,
hexToBytes(Hex.canon(signature))
);
const signingAddress = addressUtils.fromPublicKey(signingPublicKey);
// Signature does not match with the signer's public key.
assert(
'certificate.match',
signingAddress.toLowerCase() === address.toLowerCase(),
CERTIFICATE.CERTIFICATE_INVALID_SIGNER,
"Verification failed: signature does not correspond to the signer's public key.",
{ pubKey: signingPublicKey, cert }
);
} catch (e) {
throw buildError(
'certificate.match',
CERTIFICATE.CERTIFICATE_INVALID_SIGNER,
(e as Error).message,
{ address, certificate: cert, signature },
e
);
}
function sign(cert: Certificate, privateKey: Uint8Array): Certificate {
return {
...cert,
signature: Hex0x.of(
secp256k1.sign(blake2b256(encode(cert)), privateKey)
)
};
}
/**
* Verifies the validity of a certificate.
* Verifies the validity of a certificate, throwing an error if the certificate is not valid.
*
* The certificate is valid when the signer's address computed from the signature
* matches with the property {@link Certificate.signer}.
*
* This method is insensitive to the case representation of the signer's address.

@@ -105,24 +110,16 @@ *

* Secure audit function.
* - {@link certificate.encode};
* - {@link match}.
* - {@link blake2b256};
* - {@link secp256k1.recover}.
*
* @param {Certificate} cert - The certificate to verify.
* Any instance extending the {@link Certificate} interface is supported.
*
* @returns {void} - No return value.
* @throws {Error} CERTIFICATE.CERTIFICATE_NOT_SIGNED - If the certificate's signature is missing.
* @throws {Error} CERTIFICATE.CERTIFICATE_INVALID_SIGNATURE_FORMAT - If the signature format is invalid.
* @throws {Error} CERTIFICATE.CERTIFICATE_INVALID_SIGNER - If the signature does not correspond to the signer's public key.
*
* @throws CertificateInvalidSignatureFormatError - If the certificate signature's is not a valid hexadecimal expression prefixed with `0x`.
* @throws CertificateNotSignedError - If the certificate is not signed.
* @throws CertificateInvalidSignerError - If the certificate's signature's doesn't match with the signer;s public key.
*
* @remark This methods {@link certificate.encode} the `cert` instance
* to extract its signer 's address and compare it with the address computed from the public key recovered from the
* certificate using the
* [BLAKE2](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2)
* hash of its JSON encoded representation.
*
* @see {encode}
* @see {match}
*/
function verify(cert: Certificate): void {
// No signature.
// The certificate must be signed.
assert(

@@ -143,24 +140,21 @@ 'certificate.verify',

);
try {
// Encode the certificate without the signature.
const encoded = new TextEncoder().encode(
certificate
.encode({ ...cert, signature: undefined })
.normalize('NFC')
);
match(new Uint8Array(encoded), cert.signer, cert.signature as string);
} catch (e) {
throw buildError(
'certificate.verify',
CERTIFICATE.CERTIFICATE_INVALID_SIGNER,
(e as Error).message,
{ cert },
e
);
}
// If the signature is not a string, an exception is thrown above.
const sign = hexToBytes(Hex.canon(cert.signature as string));
const hash = blake2b256(encode(cert));
// The signer address is compared in lowercase to avoid
const signer = addressUtils
.fromPublicKey(secp256k1.recover(hash, sign))
.toLowerCase();
assert(
'certificate.verify',
signer === cert.signer?.toLowerCase(),
CERTIFICATE.CERTIFICATE_INVALID_SIGNER,
"Verification failed: signature does not correspond to the signer's public key.",
{ cert }
);
}
/**
* Exposes the certificate encoding and verification functions.
* Exposes the certificate sign and verification functions.
*/
export const certificate = { encode, match, verify };
export const certificate = { encode, sign, verify };
import { abi, coder, type FunctionFragment } from '../abi';
import { type TransactionClause } from '../transaction';
import {
type ClauseOptions,
type ExtendedTransactionClause,
type TransactionClause
} from '../transaction';
import type { DeployParams } from './types';

@@ -14,2 +18,3 @@ import { ERC721_ABI, VIP180_ABI } from '../utils';

*
* @param clauseOptions - Optional settings for the clause.
* @returns A clause for deploying a smart contract.

@@ -19,4 +24,5 @@ */

contractBytecode: string,
deployParams?: DeployParams
): TransactionClause {
deployParams?: DeployParams,
clauseOptions?: ClauseOptions
): TransactionClause | ExtendedTransactionClause {
let encodedParams = '';

@@ -29,3 +35,3 @@ if (deployParams != null) {

return {
const transactionClause: TransactionClause = {
to: null,

@@ -35,2 +41,11 @@ value: 0,

};
if (clauseOptions?.comment !== undefined) {
return {
...transactionClause,
comment: clauseOptions.comment
} satisfies ExtendedTransactionClause;
} else {
return transactionClause;
}
}

@@ -46,2 +61,3 @@

* @param value - The amount of VET to send with the transaction.
* @param clauseOptions - Optional settings for the clause.
* @returns A clause for interacting with a smart contract function.

@@ -55,5 +71,6 @@ *

args: unknown[],
value = 0
): TransactionClause {
return {
value = 0,
clauseOptions?: ClauseOptions
): TransactionClause | ExtendedTransactionClause {
const transactionClause: TransactionClause = {
to: contractAddress,

@@ -63,2 +80,15 @@ value,

};
if (clauseOptions !== undefined) {
return {
...transactionClause,
comment: clauseOptions.comment,
abi:
clauseOptions.includeABI === true
? functionFragment.format('json')
: undefined
} satisfies ExtendedTransactionClause;
} else {
return transactionClause;
}
}

@@ -74,2 +104,3 @@

*
* @param clauseOptions - Optional settings for the clause.
* @returns A clause for transferring VIP180 tokens.

@@ -82,4 +113,5 @@ *

recipientAddress: string,
amount: number | bigint | string
): TransactionClause {
amount: number | bigint | string,
clauseOptions?: ClauseOptions
): TransactionClause | ExtendedTransactionClause {
try {

@@ -91,3 +123,5 @@ return functionInteraction(

.getFunction('transfer') as FunctionFragment,
[recipientAddress, BigInt(amount)]
[recipientAddress, BigInt(amount)],
undefined,
clauseOptions
);

@@ -108,2 +142,3 @@ } catch (error) {

* @param amount - The amount of VET to transfer in wei.
* @param clauseOptions - Optional settings for the clause.
* @returns A clause for transferring VET.

@@ -115,4 +150,5 @@ *

recipientAddress: string,
amount: number | bigint | string
): TransactionClause {
amount: number | bigint | string,
clauseOptions?: ClauseOptions
): TransactionClause | ExtendedTransactionClause {
try {

@@ -129,3 +165,3 @@ const bnAmount = BigInt(amount);

return {
const transactionClause: TransactionClause = {
to: recipientAddress,

@@ -135,2 +171,11 @@ value: `0x${BigInt(amount).toString(16)}`,

};
if (clauseOptions?.comment !== undefined) {
return {
...transactionClause,
comment: clauseOptions.comment
} satisfies ExtendedTransactionClause;
} else {
return transactionClause;
}
} catch (error) {

@@ -155,2 +200,3 @@ throw buildError(

* @param {string} tokenId - The unique identifier of the NFT to be transferred.
* @param clauseOptions - Optional settings for the clause.
* @returns {TransactionClause} - An object representing the transaction clause required for the transfer.

@@ -164,4 +210,5 @@ *

recipientAddress: string,
tokenId: string
): TransactionClause {
tokenId: string,
clauseOptions?: ClauseOptions
): TransactionClause | ExtendedTransactionClause {
assert(

@@ -181,8 +228,12 @@ 'transferNFT',

const functionFragment = coder
.createInterface(ERC721_ABI)
.getFunction('transferFrom') as FunctionFragment;
return functionInteraction(
contractAddress,
coder
.createInterface(ERC721_ABI)
.getFunction('transferFrom') as FunctionFragment,
[senderAddress, recipientAddress, tokenId]
functionFragment,
[senderAddress, recipientAddress, tokenId],
undefined,
clauseOptions
);

@@ -189,0 +240,0 @@ }

@@ -165,2 +165,6 @@ import { Hex, Hex0x } from '../utils';

export { blake2b256, blake2b256OfHex };
export {
NORMALIZATION_FORM_CANONICAL_COMPOSITION,
blake2b256,
blake2b256OfHex
};

@@ -26,2 +26,32 @@ /**

/**
* Extended type for transaction clause that includes wallet related properties.
*/
interface ExtendedTransactionClause extends TransactionClause {
/**
* Optional comment for the clause, helpful for displaying what the clause is doing.
*/
comment?: string;
/**
* Optional ABI for the contract method invocation.
*/
abi?: string;
}
/**
* Options for creating a clause.
*/
interface ClauseOptions {
/**
* Optional comment for the clause, helpful for displaying what the clause is doing.
*/
comment?: string;
/**
* Optional ABI for the contract method invocation.
*/
includeABI?: boolean;
}
/**
* Type for transaction body.

@@ -114,2 +144,7 @@ */

export type { TransactionBody, TransactionClause };
export type {
TransactionBody,
TransactionClause,
ExtendedTransactionClause,
ClauseOptions
};

@@ -32,2 +32,3 @@ import { dataUtils } from '../data';

revision === 'best' ||
revision === 'finalized' ||
(typeof revision === 'string' && Hex0x.isValid(revision)) ||

@@ -34,0 +35,0 @@ (typeof revision === 'string' && dataUtils.isDecimalString(revision)) ||

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc