@aptos-labs/ts-sdk
Version:
Aptos TypeScript SDK
98 lines (94 loc) • 4.11 kB
text/typescript
import { HexInput, SigningScheme } from "../../types/index.js";
import { TEXT_ENCODER } from "../../utils/const.js";
import { Hex } from "../hex.js";
import { Ed25519PublicKey } from "./ed25519.js";
import { MultiEd25519PublicKey } from "./multiEd25519.js";
import { MultiKey } from "./multiKey.js";
import { AccountPublicKey } from "./publicKey.js";
import { AnyPublicKey } from "./singleKey.js";
import { BaseAccountPublicKey } from "./types.js";
import { detectPublicKeyVariant } from "./anyKeyRegistry.js";
/**
* Normalizes a sign/verify message into a {@link HexInput} that downstream
* callers can pass to `Hex.fromHexInput()`.
*
* Behavior — be aware before passing a string:
* - `Uint8Array` → returned as-is (used as raw bytes).
* - String that parses as hex via `Hex.isValid()` (with or without a `0x`
* prefix) → returned as the original hex string, which downstream
* `Hex.fromHexInput()` decodes to its byte form.
* - Any other string → returned as the UTF-8 byte encoding of the string.
*
* **AMBIGUITY**: a bare even-length string of hex characters is *always*
* interpreted as hex, even when the caller intended it as text. For example:
*
* ```ts
* sign("cafe") // signs 2 bytes: [0xCA, 0xFE]
* sign("decade") // signs 3 bytes: [0xDE, 0xCA, 0xDE]
* sign("0xcafe") // signs 2 bytes: [0xCA, 0xFE] (explicit hex)
* sign("hello") // signs 5 bytes: UTF-8 "hello" (not valid hex)
* sign(new TextEncoder().encode("cafe")) // signs 4 bytes: UTF-8 "cafe"
* ```
*
* If you mean *text*, pass `TextEncoder.encode(text)` or any `Uint8Array`.
* If you mean *hex bytes*, the most explicit form is also a `Uint8Array`
* (`Hex.fromHexInput("0x...").toUint8Array()`), or a string prefixed with
* `0x` for clarity. The heuristic is preserved as-is for backwards
* compatibility — changing it would silently re-interpret bytes signed by
* existing dApps and wallets — but new code should treat string inputs to
* `sign()` / `verifySignature()` as untyped and prefer `Uint8Array`.
*
* @param message a message as a string or Uint8Array
*
* @returns a valid HexInput - string or Uint8Array
* @group Implementation
* @category Serialization
*/
export const convertSigningMessage = (message: HexInput): HexInput => {
// if message is of type string, verify it is a valid Hex string
if (typeof message === "string") {
const isValid = Hex.isValid(message);
// If message is not a valid Hex string, convert it
if (!isValid.valid) {
return TEXT_ENCODER.encode(message);
}
// If message is a valid Hex string, return it
return message;
}
// message is a Uint8Array
return message;
};
export const accountPublicKeyToBaseAccountPublicKey = (publicKey: AccountPublicKey): BaseAccountPublicKey => {
if (
publicKey instanceof Ed25519PublicKey ||
publicKey instanceof AnyPublicKey ||
publicKey instanceof MultiEd25519PublicKey ||
publicKey instanceof MultiKey
) {
return publicKey;
}
// Keyless / FederatedKeyless public keys register themselves in the variant
// registry when imported. Using the registry lets us wrap them in
// `AnyPublicKey` here without a compile-time dependency on the keyless
// module (which would pull poseidon-lite into the `/account` sub-path).
if (detectPublicKeyVariant(publicKey) !== undefined) {
return new AnyPublicKey(publicKey);
}
throw new Error(`Unknown account public key: ${publicKey}`);
};
export const accountPublicKeyToSigningScheme = (publicKey: AccountPublicKey): SigningScheme => {
const baseAccountPublicKey = accountPublicKeyToBaseAccountPublicKey(publicKey);
if (baseAccountPublicKey instanceof Ed25519PublicKey) {
return SigningScheme.Ed25519;
}
if (baseAccountPublicKey instanceof AnyPublicKey) {
return SigningScheme.SingleKey;
}
if (baseAccountPublicKey instanceof MultiEd25519PublicKey) {
return SigningScheme.MultiEd25519;
}
if (baseAccountPublicKey instanceof MultiKey) {
return SigningScheme.MultiKey;
}
throw new Error(`Unknown signing scheme: ${baseAccountPublicKey}`);
};