123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- const { ethers } = require('hardhat');
- const { formatType } = require('./eip712');
- const PersonalSign = formatType({ prefixed: 'bytes' });
- const TypedDataSign = contentsTypeName =>
- formatType({
- contents: contentsTypeName,
- name: 'string',
- version: 'string',
- chainId: 'uint256',
- verifyingContract: 'address',
- salt: 'bytes32',
- });
- class ERC7739Signer extends ethers.AbstractSigner {
- #signer;
- #domain;
- constructor(signer, domain) {
- super(signer.provider);
- this.#signer = signer;
- this.#domain = domain;
- }
- static from(signer, domain) {
- return new this(signer, domain);
- }
- get signingKey() {
- return this.#signer.signingKey;
- }
- get privateKey() {
- return this.#signer.privateKey;
- }
- async getAddress() {
- return this.#signer.getAddress();
- }
- connect(provider) {
- this.#signer.connect(provider);
- }
- async signTransaction(tx) {
- return this.#signer.signTransaction(tx);
- }
- async signMessage(message) {
- return this.#signer.signTypedData(this.#domain, { PersonalSign }, ERC4337Utils.preparePersonalSign(message));
- }
- async signTypedData(domain, types, value) {
- const { allTypes, contentsTypeName, contentsDescr } = ERC4337Utils.getContentsDetail(types);
- return Promise.resolve(
- this.#signer.signTypedData(domain, allTypes, ERC4337Utils.prepareSignTypedData(value, this.#domain)),
- ).then(signature =>
- ethers.concat([
- signature,
- ethers.TypedDataEncoder.hashDomain(domain), // appDomainSeparator
- ethers.TypedDataEncoder.hashStruct(contentsTypeName, types, value), // contentsHash
- ethers.toUtf8Bytes(contentsDescr),
- ethers.toBeHex(contentsDescr.length, 2),
- ]),
- );
- }
- }
- class ERC4337Utils {
- static preparePersonalSign(message) {
- return {
- prefixed: ethers.concat([
- ethers.toUtf8Bytes(ethers.MessagePrefix),
- ethers.toUtf8Bytes(String(message.length)),
- typeof message === 'string' ? ethers.toUtf8Bytes(message) : message,
- ]),
- };
- }
- static prepareSignTypedData(contents, signerDomain) {
- return {
- name: signerDomain.name ?? '',
- version: signerDomain.version ?? '',
- chainId: signerDomain.chainId ?? 0,
- verifyingContract: signerDomain.verifyingContract ?? ethers.ZeroAddress,
- salt: signerDomain.salt ?? ethers.ZeroHash,
- contents,
- };
- }
- static getContentsDetail(contentsTypes, contentsTypeName = Object.keys(contentsTypes).at(0)) {
- // Examples values
- //
- // contentsTypeName B
- // typedDataSignType TypedDataSign(B contents,...)A(uint256 v)B(Z z)Z(A a)
- // contentsType A(uint256 v)B(Z z)Z(A a)
- // contentsDescr A(uint256 v)B(Z z)Z(A a)B
- const allTypes = { TypedDataSign: TypedDataSign(contentsTypeName), ...contentsTypes };
- const typedDataSignType = ethers.TypedDataEncoder.from(allTypes).encodeType('TypedDataSign');
- const contentsType = typedDataSignType.slice(typedDataSignType.indexOf(')') + 1); // Remove TypedDataSign (first object)
- const contentsDescr = contentsType + (contentsType.startsWith(contentsTypeName) ? '' : contentsTypeName);
- return {
- allTypes,
- contentsTypes,
- contentsTypeName,
- contentsDescr,
- };
- }
- }
- module.exports = {
- ERC7739Signer,
- ERC4337Utils,
- PersonalSign,
- TypedDataSign,
- };
|