@superfluid-finance/sdk-core
Version:
SDK Core for building with Superfluid Protocol
135 lines • 7.14 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ethers_1 = require("ethers");
const BatchCall_1 = require("./BatchCall");
const SFError_1 = require("./SFError");
const multiplyGasLimit_1 = __importDefault(require("./multiplyGasLimit"));
const typechain_types_1 = require("./typechain-types");
const utils_1 = require("./utils");
/**
* Operation Helper Class
* @description A helper class to create `Operation` objects which can be executed or batched.
*/
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 = (0, multiplyGasLimit_1.default)(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_1.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 = BatchCall_1.batchOperationTypeStringToTypeMap.get(this.type);
const populatedTransaction = await this.populateTransactionPromise;
if (!batchOperationType) {
throw new SFError_1.SFError({
type: "UNSUPPORTED_OPERATION",
message: "The operation at index " + index + " is unsupported.",
});
}
/* istanbul ignore next */
if (!populatedTransaction.to || !populatedTransaction.data) {
throw new SFError_1.SFError({
type: "MISSING_TRANSACTION_PROPERTIES",
message: "The transaction is missing the to or data property.",
});
}
const encoder = ethers_1.ethers.utils.defaultAbiCoder;
// Handles Superfluid.callAgreement
if (this.type === "SUPERFLUID_CALL_AGREEMENT") {
const functionArgs = (0, BatchCall_1.getCallDataFunctionArgs)(typechain_types_1.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 = (0, BatchCall_1.getCallDataFunctionArgs)(typechain_types_1.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: (0, utils_1.removeSigHashFromCallData)(populatedTransaction.data),
value: populatedTransaction.value,
};
};
this.populateTransactionPromise = txn;
this.type = type;
this.forwarderPopulatedPromise = forwarderPopulatedPromise;
}
}
exports.default = Operation;
//# sourceMappingURL=Operation.js.map