import invariant from "tiny-invariant"; import { Sign, TransactionConfig, TransactionReceipt } from "web3-core"; import { NodeWallet } from "../../web3-types/NodeWallet"; import { TransactionOptions, WALLET_FACTORY_ADDRESS } from "../constants"; import { getWalletContract, getWalletFactoryContract, } from "../constants/contracts"; import { convertResultToAddress } from "../utils"; import { EOA } from "./EOA"; import { Wallet, WalletConfig } from "./Wallet"; /** * Implementation of wallet as a Smart Contract wallet */ export class SmartWallet extends Wallet { public eoa: EOA | undefined; private contract: NodeWallet | undefined; public async _loadWallet(config: WalletConfig): Promise { this.eoa = new EOA(undefined, this.homeChain, config.eoa); await this.eoa._loadWallet(config); if (config.smartWallet) { this.address = config.smartWallet; this.contract = getWalletContract(this.web3, this.address); } else { await this.loadWalletContract(); } return this; } private async loadWalletContract() { const factory = getWalletFactoryContract(this.web3, this.homeChain); this.address = convertResultToAddress( await factory?.methods.getWallets(this.eoa?.address ?? "").call() ); if (this.address) { this.contract = getWalletContract(this.web3, this.address); } else { await this.createWallet(); } return this.address; } private async createWallet() { const factory = getWalletFactoryContract(this.web3, this.homeChain); const encodedTxn = factory.methods.createWallet().encodeABI(); const txnObject = { from: this.eoa?.address, to: WALLET_FACTORY_ADDRESS[this.homeChain], gas: 8000000, data: encodedTxn, }; await this.eoa?.signAndSendTransaction([txnObject]); this.address = convertResultToAddress( await factory?.methods.getWallets(this.eoa?.address ?? "").call() ); this.contract = getWalletContract(this.web3, this.address); return this.address; } public async signAndSendTransaction( transactions: TransactionConfig[], opts?: TransactionOptions ): Promise { invariant(!!this.contract, "No wallet contract instantiated"); invariant(!!this.eoa, "No signer for smart wallet"); const targets = transactions.map(({ to }) => { invariant(!!to, "No target for transaction"); return to; }); const callData = transactions.map(({ data }) => { invariant(!!data, "No callData provided for transaction"); return data; }); const encodedExecuteMany = this.contract.methods .executeMany(targets, callData) .encodeABI(); return await this.eoa.signAndSendTransaction( [{ to: this.address, from: this.eoa.address, data: encodedExecuteMany }], opts ); } signMessage(data: string): Sign { invariant(this.eoa, "No underlying EOA provided for signature"); return this.eoa.signMessage(data); } }