virto-signer
Version:
A signer for constructing a signed extrinsic using Pass-provided authenticators
57 lines (56 loc) • 2.11 kB
JavaScript
import { Bytes, Option, Struct, u64, u8 } from "scale-ts";
import blake2b from "blake2b";
import { getTypedMetadata } from "./utils.js";
import { mergeUint8 } from "@polkadot-api/utils";
const UncheckedExtrinsic = Struct({
version: u8,
prelude: Struct({
extensionVersion: u8,
extensions: Bytes(),
}),
call: Bytes(),
});
const PassAuthenticate = Option(Struct({
deviceId: Bytes(),
credentials: Bytes(),
}));
const EXTRINSIC_V5 = 0b0000_0101;
const EXTRINSIC_FORMAT_GENERAL = 0b0100_0000;
export class PassSigner {
authenticator;
publicKey;
constructor(authenticator) {
this.authenticator = authenticator;
this.publicKey = blake2b(32)
.update(mergeUint8(authenticator.hashedUserId, authenticator.hashedUserId))
.digest();
}
async signTx(call, signedExtensions, encodedMetadata, atBlockNumber, hasher) {
const metadata = getTypedMetadata(encodedMetadata);
const context = u64.enc(BigInt(atBlockNumber));
const hasherFn = hasher ?? ((data) => blake2b(32).update(data).digest());
const extensions = await Promise.all(metadata.extrinsic.signedExtensions.map(async ({ identifier }) => {
const signedExtension = signedExtensions[identifier];
if (!signedExtension)
throw new Error(`Missing ${identifier} signed extension`);
if (identifier === "PassAuthenticate") {
signedExtensions[identifier].value = PassAuthenticate.enc({
deviceId: this.authenticator.deviceId,
credentials: await this.authenticator.credentials(hasherFn(context)),
});
}
return signedExtension.value;
}));
return UncheckedExtrinsic.enc({
version: EXTRINSIC_V5 | EXTRINSIC_FORMAT_GENERAL,
prelude: {
extensionVersion: 0,
extensions: mergeUint8(...extensions),
},
call,
});
}
async signBytes(_) {
throw new Error("signBytes not implemented");
}
}