UNPKG

@superfluid-finance/sdk-core

Version:
129 lines 6.8 kB
import { ethers } from "ethers"; import { batchOperationTypeStringToTypeMap, getCallDataFunctionArgs, } from "./BatchCall"; import { SFError } from "./SFError"; import multiplyGasLimit from "./multiplyGasLimit"; import { Superfluid__factory } from "./typechain-types"; import { removeSigHashFromCallData } from "./utils"; /** * Operation Helper Class * @description A helper class to create `Operation` objects which can be executed or batched. */ export default class Operation { constructor(txn, type, forwarderPopulatedPromise) { /** * Executes the operation via the provided signer. * @description Populates all fields of the transaction, signs it and sends it to the network. * @param signer The signer of the transaction * @param gasLimitMultiplier A multiplier to provide gasLimit buffer on top of the estimated gas limit (1.2x is the default) * @returns {ethers.providers.TransactionResponse} A TransactionResponse object which can be awaited */ this.exec = async (signer, gasLimitMultiplier = 1.2) => { const populatedTransaction = await this.getPopulatedTransactionRequest(signer, gasLimitMultiplier); return await signer.sendTransaction(populatedTransaction); }; /** * Get the populated transaction by awaiting `populateTransactionPromise`. * `providerOrSigner` is used for gas estimation if necessary. * NOTE: we use the forwarder populated promise if this exists */ this.getPopulatedTransactionRequest = async (providerOrSigner, gasLimitMultiplier = 1.2) => { const populatedTransaction = this.forwarderPopulatedPromise ? await this.forwarderPopulatedPromise : await this.populateTransactionPromise; // if gasLimit exists, an Overrides object has been passed or the user has explicitly set // a gasLimit for their transaction prior to execution and so we keep it as is else we apply // a specified or the default (1.2) multiplier on the gas limit. if (!populatedTransaction.gasLimit) { const estimatedGasLimit = await providerOrSigner.estimateGas(populatedTransaction); populatedTransaction.gasLimit = multiplyGasLimit(estimatedGasLimit, gasLimitMultiplier); } return populatedTransaction; }; /** * Signs the populated transaction via the provided signer (what you intend on sending to the network). * @param signer The signer of the transaction * @returns {Promise<string>} Fully serialized, signed transaction */ this.getSignedTransaction = async (signer, gasLimitMultiplier = 1.2) => { const populatedTransaction = await this.getPopulatedTransactionRequest(signer, gasLimitMultiplier); const signerPopulatedTransaction = await signer.populateTransaction(populatedTransaction); const signedTransaction = await signer.signTransaction(signerPopulatedTransaction); return signedTransaction; }; /** * Gets the transaction hash of the transaction. * @description Calculates this by getting the keccak256 hash of the signedTxn. * @param signer The signer of the transaction * @returns {Promise<string>} The transaction hash of the transaction */ this.getTransactionHash = async (signer) => { const signedTxn = await this.getSignedTransaction(signer); return ethers.utils.keccak256(signedTxn); }; /** * Gets the `OperationStruct` object. * @param operation an `Operation` object * @param index the index of the `Operation` in the batchCall * @returns {Promise<OperationStruct>} OperationStruct object for batchCall */ this.toOperationStruct = async (index) => { const batchOperationType = batchOperationTypeStringToTypeMap.get(this.type); const populatedTransaction = await this.populateTransactionPromise; if (!batchOperationType) { throw new SFError({ type: "UNSUPPORTED_OPERATION", message: "The operation at index " + index + " is unsupported.", }); } /* istanbul ignore next */ if (!populatedTransaction.to || !populatedTransaction.data) { throw new SFError({ type: "MISSING_TRANSACTION_PROPERTIES", message: "The transaction is missing the to or data property.", }); } const encoder = ethers.utils.defaultAbiCoder; // Handles Superfluid.callAgreement if (this.type === "SUPERFLUID_CALL_AGREEMENT") { const functionArgs = getCallDataFunctionArgs(Superfluid__factory.abi, populatedTransaction.data); const data = encoder.encode(["bytes", "bytes"], [functionArgs["callData"], functionArgs["userData"]]); return { operationType: batchOperationType, target: functionArgs["agreementClass"], data, value: populatedTransaction.value, }; } // Handles Superfluid.callAppAction if (this.type === "CALL_APP_ACTION") { const functionArgs = getCallDataFunctionArgs(Superfluid__factory.abi, populatedTransaction.data); return { operationType: batchOperationType, target: functionArgs["app"], data: functionArgs["callData"], value: populatedTransaction.value, }; } if (this.type === "SIMPLE_FORWARD_CALL" || this.type === "ERC2771_FORWARD_CALL") { return { operationType: batchOperationType, target: populatedTransaction.to, data: populatedTransaction.data, value: populatedTransaction.value, }; } // Handles remaining ERC20/ERC777/SuperToken Operations (including SIMPLE_FORWARD_CALL and ERC2771_FORWARD_CALL) return { operationType: batchOperationType, target: populatedTransaction.to, data: removeSigHashFromCallData(populatedTransaction.data), value: populatedTransaction.value, }; }; this.populateTransactionPromise = txn; this.type = type; this.forwarderPopulatedPromise = forwarderPopulatedPromise; } } //# sourceMappingURL=Operation.js.map