diff --git a/src/contracts/contract-modules/masa-module-base.ts b/src/contracts/contract-modules/masa-module-base.ts index a600293b..f32d7268 100644 --- a/src/contracts/contract-modules/masa-module-base.ts +++ b/src/contracts/contract-modules/masa-module-base.ts @@ -220,7 +220,7 @@ export abstract class MasaModuleBase extends MasaBase { | SoulLinker, domain: TypedDataDomain, types: Record>, - value: Record, + value: Record, signature: string, authorityAddress: string, ) => { diff --git a/src/contracts/contract-modules/sbt/SSSBT/sssbt-contract-wrapper.ts b/src/contracts/contract-modules/sbt/SSSBT/sssbt-contract-wrapper.ts index bfb66f48..4fe03137 100644 --- a/src/contracts/contract-modules/sbt/SSSBT/sssbt-contract-wrapper.ts +++ b/src/contracts/contract-modules/sbt/SSSBT/sssbt-contract-wrapper.ts @@ -16,6 +16,14 @@ import { SBTContractWrapper } from "../SBT/sbt-contract-wrapper"; export class SSSBTContractWrapper< Contract extends ReferenceSBTSelfSovereign, > extends SBTContractWrapper { + public readonly types = { + Mint: [ + { name: "to", type: "address" }, + { name: "authorityAddress", type: "address" }, + { name: "signatureDate", type: "uint256" }, + ], + }; + /** * Signs an SBT based on its address * @param name @@ -63,7 +71,7 @@ export class SSSBTContractWrapper< * @param authorityAddress * @param slippage */ - public prepareMint = async ( + protected prepareMint = async ( paymentMethod: PaymentMethod, name: string, types: Record>, @@ -145,14 +153,6 @@ export class SSSBTContractWrapper< } } - const types = { - Mint: [ - { name: "to", type: "address" }, - { name: "authorityAddress", type: "address" }, - { name: "signatureDate", type: "uint256" }, - ], - }; - // fill the collection with data const value: { to: string; @@ -167,7 +167,7 @@ export class SSSBTContractWrapper< const { price, paymentAddress } = await this.prepareMint( paymentMethod, "ReferenceSBTSelfSovereign", - types, + this.types, value, signature, authorityAddress, diff --git a/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract-wrapper.ts b/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract-wrapper.ts new file mode 100644 index 00000000..64d4bfa0 --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract-wrapper.ts @@ -0,0 +1,298 @@ +import { LogDescription } from "@ethersproject/abi"; +import { BigNumber } from "@ethersproject/bignumber"; +import { ReferenceSBTDynamicSelfSovereign } from "@masa-finance/masa-contracts-identity"; +import type { TypedDataField } from "ethers"; +import { PayableOverrides, TypedDataDomain } from "ethers"; + +import { Messages } from "../../../../../collections"; +import type { PaymentMethod } from "../../../../../interface"; +import { + generateSignatureDomain, + isNativeCurrency, + signTypedData, +} from "../../../../../utils"; +import { DynamicSBTContractWrapper } from "../dynamic-sbt-contract-wrapper"; + +export class DynamicSSSBTContractWrapper< + Contract extends ReferenceSBTDynamicSelfSovereign, +> extends DynamicSBTContractWrapper { + public readonly types = { + SetState: [ + { name: "account", type: "address" }, + { name: "state", type: "string" }, + { name: "value", type: "bool" }, + { name: "authorityAddress", type: "address" }, + { name: "signatureDate", type: "uint256" }, + ], + }; + + /** + * Signs an SBT based on its address + * @param name + * @param types + * @param value + */ + public signState = async ( + name: string, + types: Record>, + value: Record, + ): Promise<{ + signature: string; + authorityAddress: string; + }> => { + const authorityAddress = await this.masa.config.signer.getAddress(); + + const { signature, domain } = await signTypedData( + this.contract, + this.masa.config.signer, + name, + types, + value, + ); + + await this.verify( + "Signing Dynamic SBT failed!", + this.contract, + domain, + types, + value, + signature, + authorityAddress, + ); + + return { signature, authorityAddress }; + }; + + /** + * + * @param name + * @param types + * @param value + * @param signature + * @param authorityAddress + */ + protected prepareSetState = async ( + name: string, + types: Record>, + value: Record, + signature: string, + authorityAddress: string, + ): Promise => { + const domain: TypedDataDomain = await generateSignatureDomain( + this.masa.config.signer, + name, + this.contract.address, + ); + + await this.verify( + "Verifying Dynamic SBT failed!", + this.contract, + domain, + types, + value, + signature, + authorityAddress, + ); + + return true; + }; + + /** + * + * @param receiver + * @param state + * @param stateValue + * @param signature + * @param signatureDate + * @param authorityAddress + */ + public setState = async ( + receiver: string, + state: string, + stateValue: boolean, + signature: string, + signatureDate: number, + authorityAddress: string, + ): Promise => { + const value: { + account: string; + state: string; + value: boolean; + authorityAddress: string; + signatureDate: number; + } = { + account: receiver, + state, + value: stateValue, + authorityAddress, + signatureDate, + }; + + const { + "setState(address,string,bool,address,uint256,bytes)": setState, + estimateGas: { + "setState(address,string,bool,address,uint256,bytes)": estimateGas, + }, + } = this.contract; + + this.prepareSetState( + "adsasdads", + this.types, + value, + signature, + authorityAddress, + ); + + const dynamicSSSBTSetStateArguments: [ + account: string, + state: string, + value: boolean, + authorityAddress: string, + signatureDate: number, + signature: string, + ] = [ + receiver, + state, + stateValue, + authorityAddress, + signatureDate, + signature, + ]; + + const mintSSSBTOverrides: PayableOverrides = await this.createOverrides(); + + let gasLimit: BigNumber = await estimateGas( + ...dynamicSSSBTSetStateArguments, + mintSSSBTOverrides, + ); + + if (this.masa.config.network?.gasSlippagePercentage) { + gasLimit = DynamicSSSBTContractWrapper.addSlippage( + gasLimit, + this.masa.config.network.gasSlippagePercentage, + ); + } + + const { wait, hash } = await setState(...dynamicSSSBTSetStateArguments, { + ...mintSSSBTOverrides, + gasLimit, + }); + + console.log( + Messages.WaitingToFinalize( + hash, + this.masa.config.network?.blockExplorerUrls?.[0], + ), + ); + + await wait(); + + return true; + }; + + /** + * + * @param paymentMethod + * @param receiver + */ + public mint = async ( + paymentMethod: PaymentMethod, + receiver: string, + ): Promise => { + // current limit for SSSBT is 1 on the default installation + let limit: number = 1; + + try { + limit = (await this.contract.maxSBTToMint()).toNumber(); + } catch { + if (this.masa.config.verbose) { + console.info("Loading limit failed, falling back to 1!"); + } + } + + try { + const balance: BigNumber = await this.contract.balanceOf(receiver); + + if (limit > 0 && balance.gte(limit)) { + console.error( + `Minting of SSSBT failed: '${receiver}' exceeded the limit of '${limit}'!`, + ); + return false; + } + } catch (error: unknown) { + if (error instanceof Error) { + console.warn(error.message); + } + } + + const { price, paymentAddress } = await this.getPrice(paymentMethod); + + const mintSSSBTArguments: [ + string, // paymentMethod string + ] = [paymentAddress]; + + const mintSSSBTOverrides: PayableOverrides = await this.createOverrides( + isNativeCurrency(paymentMethod) ? price : undefined, + ); + + if (this.masa.config.verbose) { + console.dir( + { + mintSSSBTArguments, + mintSSSBTOverrides, + }, + { + depth: null, + }, + ); + } + + const { + "mint(address)": mint, + estimateGas: { "mint(address)": estimateGas }, + } = this.contract; + + let gasLimit: BigNumber = await estimateGas( + ...mintSSSBTArguments, + mintSSSBTOverrides, + ); + + if (this.masa.config.network?.gasSlippagePercentage) { + gasLimit = DynamicSSSBTContractWrapper.addSlippage( + gasLimit, + this.masa.config.network.gasSlippagePercentage, + ); + } + + const { wait, hash } = await mint(...mintSSSBTArguments, { + ...mintSSSBTOverrides, + gasLimit, + }); + + console.log( + Messages.WaitingToFinalize( + hash, + this.masa.config.network?.blockExplorerUrls?.[0], + ), + ); + + const { logs } = await wait(); + + const parsedLogs = this.masa.contracts.parseLogs(logs, [this.contract]); + + const mintEvent = parsedLogs.find( + (log: LogDescription) => log.name === "Mint", + ); + + if (mintEvent) { + const { args } = mintEvent; + console.log( + `Minted to token with ID: ${args._tokenId} receiver '${args._owner}'`, + ); + + return true; + } + + return false; + }; +} diff --git a/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract.ts b/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract.ts new file mode 100644 index 00000000..bbaa3459 --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/SSSBT/dynamic-sssbt-contract.ts @@ -0,0 +1,35 @@ +import type { ReferenceSBTDynamicSelfSovereign } from "@masa-finance/masa-contracts-identity"; +import { ReferenceSBTDynamicSelfSovereign__factory } from "@masa-finance/masa-contracts-identity"; + +import type { ContractFactory } from "../../../../../interface/contract-factory"; +import { MasaSBTModuleBase } from "../../masa-sbt-module-base"; +import { DynamicSSSBTContractWrapper } from "./dynamic-sssbt-contract-wrapper"; + +export class DynamicSSSBTContract extends MasaSBTModuleBase { + /** + * + * @param contract + */ + public attach = ( + contract: Contract, + ): DynamicSSSBTContractWrapper => { + return new DynamicSSSBTContractWrapper(this.masa, this.instances, contract); + }; + + /** + * + * @param address + * @param factory + */ + public connect = async ( + address: string, + factory: ContractFactory = ReferenceSBTDynamicSelfSovereign__factory, + ): Promise> => { + const contract: Contract = await this.loadSBTContract( + address, + factory, + ); + + return this.attach(contract); + }; +} diff --git a/src/contracts/contract-modules/sbt/dynamic/SSSBT/index.ts b/src/contracts/contract-modules/sbt/dynamic/SSSBT/index.ts new file mode 100644 index 00000000..05ccd584 --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/SSSBT/index.ts @@ -0,0 +1,2 @@ +export * from "./dynamic-sssbt-contract"; +export * from "./dynamic-sssbt-contract-wrapper"; diff --git a/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract-wrapper.ts b/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract-wrapper.ts new file mode 100644 index 00000000..04a919a3 --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract-wrapper.ts @@ -0,0 +1,7 @@ +import { MasaSBTDynamic } from "@masa-finance/masa-contracts-identity"; + +import { SBTContractWrapper } from "../SBT"; + +export class DynamicSBTContractWrapper< + Contract extends MasaSBTDynamic, +> extends SBTContractWrapper {} diff --git a/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract.ts b/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract.ts new file mode 100644 index 00000000..a151407b --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/dynamic-sbt-contract.ts @@ -0,0 +1,39 @@ +import type { MasaSBTDynamic } from "@masa-finance/masa-contracts-identity"; +import { MasaSBTDynamic__factory } from "@masa-finance/masa-contracts-identity"; + +import type { ContractFactory } from "../../../../interface/contract-factory"; +import { MasaSBTModuleBase } from "../masa-sbt-module-base"; +import { DynamicSBTContractWrapper } from "./dynamic-sbt-contract-wrapper"; + +export class DynamicSBTContract extends MasaSBTModuleBase { + /** + * + * @param contract + */ + public attach = ( + contract: Contract, + ): DynamicSBTContractWrapper => { + return new DynamicSBTContractWrapper( + this.masa, + this.instances, + contract, + ); + }; + + /** + * loads an sbt instance and connects the contract functions to it + * @param address + * @param factory + */ + public connect = async ( + address: string, + factory: ContractFactory = MasaSBTDynamic__factory, + ): Promise> => { + const contract: Contract = await this.loadSBTContract( + address, + factory, + ); + + return this.attach(contract); + }; +} diff --git a/src/contracts/contract-modules/sbt/dynamic/index.ts b/src/contracts/contract-modules/sbt/dynamic/index.ts new file mode 100644 index 00000000..b52000f2 --- /dev/null +++ b/src/contracts/contract-modules/sbt/dynamic/index.ts @@ -0,0 +1,2 @@ +export * from "./dynamic-sbt-contract"; +export * from "./dynamic-sbt-contract-wrapper"; diff --git a/src/contracts/masa-contracts.ts b/src/contracts/masa-contracts.ts index c54f2ab9..336a748e 100644 --- a/src/contracts/masa-contracts.ts +++ b/src/contracts/masa-contracts.ts @@ -8,6 +8,8 @@ import { CreditScore } from "./contract-modules/credit-score"; import { Green } from "./contract-modules/green"; import { Identity } from "./contract-modules/identity"; import { ASBTContract } from "./contract-modules/sbt/ASBT"; +import { DynamicSBTContract } from "./contract-modules/sbt/dynamic"; +import { DynamicSSSBTContract } from "./contract-modules/sbt/dynamic/SSSBT"; import { SBTContract } from "./contract-modules/sbt/SBT/sbt-contract"; import { SSSBTContract } from "./contract-modules/sbt/SSSBT/sssbt-contract"; import { SoulLinker } from "./contract-modules/soul-linker"; @@ -25,6 +27,9 @@ export class MasaContracts extends MasaBase { public sbt: SBTContract; public sssbt: SSSBTContract; public asbt: ASBTContract; + // dynamic + public ["dynamic-sbt"]: DynamicSBTContract; + public ["dynamic-sssbt"]: DynamicSSSBTContract; /** * Soul Linker */ @@ -69,6 +74,9 @@ export class MasaContracts extends MasaBase { this.sbt = new SBTContract(this.masa, this.instances); this.sssbt = new SSSBTContract(this.masa, this.instances); this.asbt = new ASBTContract(this.masa, this.instances); + // dynamic + this["dynamic-sbt"] = new DynamicSBTContract(this.masa, this.instances); + this["dynamic-sssbt"] = new DynamicSSSBTContract(this.masa, this.instances); /** * Soul Linker */ diff --git a/src/modules/sbt/ASBT/masa-asbt-wrapper.ts b/src/modules/sbt/ASBT/masa-asbt-wrapper.ts index 83266a06..e4edf6b8 100644 --- a/src/modules/sbt/ASBT/masa-asbt-wrapper.ts +++ b/src/modules/sbt/ASBT/masa-asbt-wrapper.ts @@ -23,7 +23,7 @@ export class MasaASBTWrapper< console.log(`Contract Address: '${this.contract.address}'`); console.log(`To receiver: '${receiver}'`); - const { mint } = await this.masa.contracts.asbt.attach(this.contract); + const { mint } = this.masa.contracts.asbt.attach(this.contract); return mint(paymentMethod, receiver); }; @@ -48,7 +48,7 @@ export class MasaASBTWrapper< console.log(`Contract Address: '${this.contract.address}'`); console.log(`To receiver: '${receivers}'`); - const { bulkMint } = await this.masa.contracts.asbt.attach(this.contract); + const { bulkMint } = this.masa.contracts.asbt.attach(this.contract); return bulkMint(paymentMethod, receivers); }; diff --git a/src/modules/sbt/SSSBT/masa-sssbt-wrapper.ts b/src/modules/sbt/SSSBT/masa-sssbt-wrapper.ts index 8af28382..183399f0 100644 --- a/src/modules/sbt/SSSBT/masa-sssbt-wrapper.ts +++ b/src/modules/sbt/SSSBT/masa-sssbt-wrapper.ts @@ -32,14 +32,6 @@ export class MasaSSSBTWrapper< const signatureDate = Date.now(); - const types = { - Mint: [ - { name: "to", type: "address" }, - { name: "authorityAddress", type: "address" }, - { name: "signatureDate", type: "uint256" }, - ], - }; - // fill the collection with data const value: { to: string; @@ -51,7 +43,7 @@ export class MasaSSSBTWrapper< signatureDate, }; - const { sign } = await this.masa.contracts.sssbt.attach(this.contract); + const { sign, types } = this.masa.contracts.sssbt.attach(this.contract); // sign to create a signature const signResult = await sign("ReferenceSBTSelfSovereign", types, value); @@ -101,7 +93,7 @@ export class MasaSSSBTWrapper< console.log(`Contract Address: '${this.contract.address}'`); console.log(`To receiver: '${receiver}'`); - const { mint } = await this.masa.contracts.sssbt.attach(this.contract); + const { mint } = this.masa.contracts.sssbt.attach(this.contract); return mint( paymentMethod, diff --git a/src/modules/sbt/dynamic/SSSBT/index.ts b/src/modules/sbt/dynamic/SSSBT/index.ts new file mode 100644 index 00000000..d50e7d90 --- /dev/null +++ b/src/modules/sbt/dynamic/SSSBT/index.ts @@ -0,0 +1,2 @@ +export * from "./masa-dynamic-sssbt"; +export * from "./masa-dynamic-sssbt-wrapper"; diff --git a/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt-wrapper.ts b/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt-wrapper.ts new file mode 100644 index 00000000..552c00d8 --- /dev/null +++ b/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt-wrapper.ts @@ -0,0 +1,110 @@ +import type { ReferenceSBTDynamicSelfSovereign } from "@masa-finance/masa-contracts-identity"; + +import type { PaymentMethod } from "../../../../interface"; +import { MasaDynamicSBTWrapper } from "../masa-dynamic-sbt-wrapper"; + +export class MasaDynamicSSSBTWrapper< + Contract extends ReferenceSBTDynamicSelfSovereign, +> extends MasaDynamicSBTWrapper { + /** + * + * @param receiver + * @param state + * @param stateValue + */ + public signState = async ( + receiver: string, + state: string, + stateValue: boolean, + ) => { + let result: + | { + authorityAddress: string; + signatureDate: number; + signature: string; + } + | undefined; + + const [name, symbol] = await Promise.all([ + this.contract.name(), + this.contract.symbol(), + ]); + + console.log(`Signing Dynamic SSSBT on: '${this.masa.config.networkName}'`); + console.log(`Contract Name: '${name}'`); + console.log(`Contract Symbol: '${symbol}'`); + console.log(`Contract Address: '${this.contract.address}'`); + console.log(`To receiver: '${receiver}'`); + + const signatureDate = Date.now(); + + // fill the collection with data + const value: { + account: string; + state: string; + value: boolean; + authorityAddress: string; + signatureDate: number; + } = { + account: receiver, + state, + value: stateValue, + authorityAddress: await this.masa.config.signer.getAddress(), + signatureDate, + }; + + const { signState, types } = this.masa.contracts["dynamic-sssbt"].attach( + this.contract, + ); + + // sign to create a signature + const signResult = await signState( + "ReferenceSBTSelfSovereign", + types, + value, + ); + + if (signResult) { + const { signature, authorityAddress } = signResult; + if (this.masa.config.verbose) { + console.info({ + signature, + authorityAddress, + signatureDate, + }); + } + result = { + authorityAddress, + signatureDate, + signature, + }; + } + + return result; + }; + + /** + * + * @param paymentMethod + */ + public mint = async ( + paymentMethod: PaymentMethod = "ETH", + ): Promise => { + const receiver = await this.masa.config.signer.getAddress(); + + const [name, symbol] = await Promise.all([ + this.contract.name(), + this.contract.symbol(), + ]); + + console.log(`Minting Dynamic SSSBT on: '${this.masa.config.networkName}'`); + console.log(`Contract Name: '${name}'`); + console.log(`Contract Symbol: '${symbol}'`); + console.log(`Contract Address: '${this.contract.address}'`); + console.log(`To receiver: '${receiver}'`); + + const { mint } = this.masa.contracts["dynamic-sssbt"].attach(this.contract); + + return mint(paymentMethod, receiver); + }; +} diff --git a/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt.ts b/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt.ts new file mode 100644 index 00000000..1224d21d --- /dev/null +++ b/src/modules/sbt/dynamic/SSSBT/masa-dynamic-sssbt.ts @@ -0,0 +1,34 @@ +import type { ReferenceSBTDynamicSelfSovereign } from "@masa-finance/masa-contracts-identity"; +import { ReferenceSBTDynamicSelfSovereign__factory } from "@masa-finance/masa-contracts-identity"; + +import type { ContractFactory } from "../../../../interface/contract-factory"; +import { MasaBase } from "../../../../masa-base"; +import { MasaDynamicSSSBTWrapper } from "./masa-dynamic-sssbt-wrapper"; + +export class MasaDynamicSSSBT extends MasaBase { + /** + * + * @param contract + */ + public attach = ( + contract: Contract, + ): MasaDynamicSSSBTWrapper => { + return new MasaDynamicSSSBTWrapper(this.masa, contract); + }; + + /** + * + * @param address + * @param factory + */ + public connect = async ( + address: string, + factory: ContractFactory = ReferenceSBTDynamicSelfSovereign__factory, + ): Promise> => { + const { contract } = await this.masa.contracts[ + "dynamic-sssbt" + ].connect(address, factory); + + return this.attach(contract); + }; +} diff --git a/src/modules/sbt/dynamic/index.ts b/src/modules/sbt/dynamic/index.ts new file mode 100644 index 00000000..9c95aa60 --- /dev/null +++ b/src/modules/sbt/dynamic/index.ts @@ -0,0 +1,2 @@ +export * from "./masa-dynamic-sbt"; +export * from "./masa-dynamic-sbt-wrapper"; diff --git a/src/modules/sbt/dynamic/masa-dynamic-sbt-wrapper.ts b/src/modules/sbt/dynamic/masa-dynamic-sbt-wrapper.ts new file mode 100644 index 00000000..45a0b478 --- /dev/null +++ b/src/modules/sbt/dynamic/masa-dynamic-sbt-wrapper.ts @@ -0,0 +1,7 @@ +import type { MasaSBTDynamic } from "@masa-finance/masa-contracts-identity"; + +import { MasaSBTWrapper } from "../SBT"; + +export class MasaDynamicSBTWrapper< + Contract extends MasaSBTDynamic, +> extends MasaSBTWrapper {} diff --git a/src/modules/sbt/dynamic/masa-dynamic-sbt.ts b/src/modules/sbt/dynamic/masa-dynamic-sbt.ts new file mode 100644 index 00000000..62e3775b --- /dev/null +++ b/src/modules/sbt/dynamic/masa-dynamic-sbt.ts @@ -0,0 +1,35 @@ +import { + MasaSBT__factory, + MasaSBTDynamic, +} from "@masa-finance/masa-contracts-identity"; + +import type { ContractFactory } from "../../../interface/contract-factory"; +import { MasaBase } from "../../../masa-base"; +import { MasaDynamicSBTWrapper } from "./masa-dynamic-sbt-wrapper"; + +export class MasaDynamicSBTBase extends MasaBase { + /** + * + * @param contract + */ + public attach = (contract: Contract) => { + return new MasaDynamicSBTWrapper(this.masa, contract); + }; + + /** + * + * @param address + * @param factory + */ + public connect = async ( + address: string, + factory: ContractFactory = MasaSBT__factory, + ): Promise> => { + const { contract } = await this.masa.contracts.sbt.connect( + address, + factory, + ); + + return this.attach(contract); + }; +} diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts index e410125d..e12a717a 100644 --- a/src/utils/crypto.ts +++ b/src/utils/crypto.ts @@ -77,7 +77,7 @@ export const signTypedData = async ( wallet: Signer, name: string, types: Record>, - value: Record, + value: Record, ): Promise<{ signature: string; domain: TypedDataDomain }> => { const domain = await generateSignatureDomain(wallet, name, contract.address); const signature = await (wallet as Signer & TypedDataSigner)._signTypedData(