aa-schnorr-multisig-sdk
Version:
Account Abstraction Schnorr Multi-Signatures SDK
97 lines (96 loc) • 4.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiSigUserOpWithSigners = void 0;
const ethers_1 = require("ethers");
const schnorr_helpers_1 = require("../helpers/schnorr-helpers");
const signers_1 = require("../signers");
class MultiSigUserOpWithSigners {
constructor(signers, opHash, userOpRequest) {
this.publicNonces = {};
this.publicKeys = {};
this.signatures = {};
if (signers.length < 2)
throw new Error("At least 2 signers should be provided");
this.signers = signers;
this.opHash = opHash;
this.userOpRequest = userOpRequest;
const _publicKeys = signers.map((signer) => {
const _address = signer.getAddress();
// generate and get public nonces
if (signer.hasNonces())
throw new Error("Signer already has nonces");
this.publicNonces[_address] = signer.generatePubNonces();
// get public keys
const _pk = signer.getPubKey();
this.publicKeys[_address] = _pk;
return _pk;
});
// get combined public key created from all signers' public keys
const _combinedPubKey = signers_1.Schnorrkel.getCombinedPublicKey(_publicKeys);
this.combinedPubKey = _combinedPubKey;
// create unique tx id
const _salt = Buffer.from(ethers_1.ethers.utils.randomBytes(32));
const encodedParams = ethers_1.ethers.utils.defaultAbiCoder.encode(["bytes", "bytes", "bytes"], [_combinedPubKey.buffer, opHash, _salt]);
this.id = ethers_1.ethers.utils.keccak256(encodedParams);
}
getOpHash() {
return this.opHash;
}
signMultiSigHash(signer) {
const op = this.opHash;
const pk = this._getPublicKeys();
const pn = this._getPublicNonces();
const _signatureOutput = signer.signMultiSigHash(op, pk, pn);
this.signatures[signer.getAddress()] = _signatureOutput;
return _signatureOutput;
}
getSummedSigData() {
if (!this.combinedPubKey || !this.signatures || this.signers.length < 2)
throw new Error("Summed signature input data is missing");
const _signatureOutputs = this._getSignatures();
const _sigs = _signatureOutputs.map((sig) => sig.signature);
const _challenges = _signatureOutputs.map((sig) => sig.challenge);
// sum all signers signatures
const _summed = (0, schnorr_helpers_1.sumSchnorrSigs)(_sigs);
// challenge for every signature must be the same - check and if so, assign first one
const isEveryChallengeEqual = _challenges.every((e) => {
if (e.toHex() === _challenges[0].toHex())
return true;
});
if (!isEveryChallengeEqual)
throw new Error("Challenges for all signers should be the same");
const e = _challenges[0];
// the multisig px and parity
const px = ethers_1.ethers.utils.hexlify(this.combinedPubKey.buffer.subarray(1, 33));
const parity = this.combinedPubKey.buffer[0] - 2 + 27;
// wrap the result
const abiCoder = new ethers_1.ethers.utils.AbiCoder();
const sigData = abiCoder.encode(["bytes32", "bytes32", "bytes32", "uint8"], [px, e.buffer, _summed.buffer, parity]);
return sigData;
}
getAddressSignature(signerAddress) {
return this._getSignatures()[signerAddress];
}
getAddressPublicNonces(signerAddress) {
return this._getPublicNonces()[signerAddress];
}
getAddressPubKeys(signerAddress) {
return this._getPublicKeys()[signerAddress];
}
_getSignatures() {
return Object.entries(this.signatures).map(([, sig]) => {
return sig;
});
}
_getPublicNonces() {
return Object.entries(this.publicNonces).map(([, nonce]) => {
return nonce;
});
}
_getPublicKeys() {
return Object.entries(this.publicKeys).map(([, pk]) => {
return pk;
});
}
}
exports.MultiSigUserOpWithSigners = MultiSigUserOpWithSigners;