@ajna-finance/sdk
Version:
A typescript SDK that can be used to create Dapps in Ajna ecosystem.
197 lines • 10.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTransaction = createTransaction;
const tslib_1 = require("tslib");
const constants_1 = require("../constants");
const types_1 = require("../types");
/**
* Creates a wrapped transaction object that can be used to submit, verify, and estimate gas for a transaction.
* @param contract The ethers.js contract instance.
* @param methodName The name of the method to call on the contract.
* @param args An array of arguments to pass to the method.
* @param overrides An optional object with transaction overrides, such as gasPrice and gasLimit.
* @returns The wrapped transaction object.
*/
function createTransaction(contract, callData, overrides) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const { methodName, args = [] } = callData;
const argsFiltered = args.filter(a => a !== undefined);
const params = overrides ? [...argsFiltered, overrides] : [...argsFiltered];
const tx = yield contract.populateTransaction[methodName](...params);
return new WrappedTransactionClass(tx, contract);
});
}
/**
* A class representing a wrapped transaction that can be used to submit, verify, and estimate gas for a transaction.
*/
class WrappedTransactionClass {
/**
* Creates a new wrapped transaction instance.
* @param transaction The populated transaction object.
* @param contract The ethers.js contract instance.
*/
constructor(transaction, contract) {
this._transaction = transaction;
this._contract = contract;
}
/**
* Verifies that the transaction can be executed by estimating its gas cost.
* @returns A Promise that resolves to the estimated gas cost.
* @throws {@link SdkError} An SDK error if the transaction execution failed and the error reason can be identified.
* @throws The original error if the transaction execution failed and the error reason cannot be identified.
*/
verify() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
return yield this._contract.provider.estimateGas(this._transaction);
}
catch (error) {
const reason = this.parseNodeError(this._contract, error);
throw new types_1.SdkError(reason, error);
}
});
}
/**
* Submits the transaction to the blockchain using the signer.
* @returns The transaction receipt.
*/
submitResponse() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield this._contract.signer.sendTransaction(this._transaction);
});
}
/**
* Submits the transaction to the blockchain and waits for it to be mined.
* @param confirmations The number of confirmations to wait for (default is 1).
* @returns A Promise that resolves to the transaction receipt.
*/
submit(confirmations) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const response = yield this.submitResponse();
return yield response.wait(confirmations);
});
}
/**
* Estimates gas, submits a transaction with gas estimate, and waits for the
* node to acknowledge the transaction is pending.
* @throws {@link SdkError} if transaction will fail at current block height.
* @returns TransactionResponse
*/
verifyAndSubmitResponse() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const estimatedGas = yield this.verify();
const txWithAdjustedGas = Object.assign(Object.assign({}, this._transaction), { gasLimit: +estimatedGas.mul(constants_1.GAS_MULTIPLIER) });
return yield this._contract.signer.sendTransaction(txWithAdjustedGas);
});
}
/**
* Estimates gas, submits a transaction with gas estimate, and waits for the transaction to be
* included in a block.
* @param confirmations Optionally wait for specific number of confirmations.
* @throws {@link SdkError} if transaction will fail at current block height.
* @returns TransactionReceipt
*/
verifyAndSubmit(confirmations) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const response = yield this.verifyAndSubmitResponse();
return yield response.wait(confirmations);
});
}
/**
* Reveals data emitted by a successful transaction.
* @param receipt Receipt from a processed transaction.
* @returns Map of events indexed by event name, and the data emitted in each event.
*/
getEventLogs(receipt) {
const eventLogs = new Map();
let eventsForName;
for (const log of receipt.logs) {
try {
const logDesc = this._contract.interface.parseLog(log);
const eventLog = { eventName: logDesc.name, args: logDesc.args };
eventsForName = eventLogs.get(logDesc.name);
if (eventsForName)
eventsForName.push(eventLog);
else
eventLogs.set(logDesc.name, [eventLog]);
}
catch (ex) {
// TODO: pass in an array of other contracts whose interfaces we could try parsing with
if (!ex.toString().startsWith('Error: no matching event'))
throw ex;
}
}
return eventLogs;
}
/**
* Looks through exception data to find the error hash for various node providers.
* @param error Error thrown by Ethers in response to an estimateGas failure.
* @returns string
*/
parseNodeError(contract, error) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
// mainnet
if ((_b = (_a = error === null || error === void 0 ? void 0 : error.error) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.originalError) {
const errorHash = error.error.data.originalError.data;
return ((_c = this.getCustomErrorFromHash(contract, errorHash)) !== null && _c !== void 0 ? _c : error.error.data.originalError.message);
}
if ((_d = error === null || error === void 0 ? void 0 : error.error) === null || _d === void 0 ? void 0 : _d.error) {
const innerError = error.error.error;
// works on mainnet-forked Ganache local testnet
if ((_e = innerError.data) === null || _e === void 0 ? void 0 : _e.reason)
return innerError.data.reason;
if ((_f = innerError.data) === null || _f === void 0 ? void 0 : _f.result) {
const errorHash = innerError.data.result;
return this.getCustomErrorFromHash(contract, errorHash);
}
// works with Alchemy node on Goerli
if (innerError.code === 3) {
// if the hash does not map to a custom error, return the node-provided error
const errorHash = innerError.data;
return (_g = this.getCustomErrorFromHash(contract, errorHash)) !== null && _g !== void 0 ? _g : error.error.error;
}
}
// metamask provider
if ((_h = error === null || error === void 0 ? void 0 : error.data) === null || _h === void 0 ? void 0 : _h.data) {
const errorHash = error.data.data.result;
return ((_l = (_j = this.getCustomErrorFromHash(contract, errorHash)) !== null && _j !== void 0 ? _j : (_k = error.data.data) === null || _k === void 0 ? void 0 : _k.reason) !== null && _l !== void 0 ? _l : (_m = error.data.data) === null || _m === void 0 ? void 0 : _m.message);
}
// hardhat
if ((_q = (_p = (_o = error === null || error === void 0 ? void 0 : error.error) === null || _o === void 0 ? void 0 : _o.data) === null || _p === void 0 ? void 0 : _p.data) === null || _q === void 0 ? void 0 : _q.data) {
const errorHash = error.error.data.data.data;
return ((_t = (_r = this.getCustomErrorFromHash(contract, errorHash)) !== null && _r !== void 0 ? _r : (_s = error.error.data) === null || _s === void 0 ? void 0 : _s.cause) !== null && _t !== void 0 ? _t : (_u = error.error.data) === null || _u === void 0 ? void 0 : _u.message);
}
// works with some L2 chains (Base, Polygon, Optimism)
if ((_v = error === null || error === void 0 ? void 0 : error.error) === null || _v === void 0 ? void 0 : _v.data) {
const errorHash = error.error.data;
if (errorHash === null || errorHash === void 0 ? void 0 : errorHash.data) {
return ((_y = (_w = this.getCustomErrorFromHash(contract, errorHash.data)) !== null && _w !== void 0 ? _w : (_x = error.error.data) === null || _x === void 0 ? void 0 : _x.cause) !== null && _y !== void 0 ? _y : (_z = error.error.data) === null || _z === void 0 ? void 0 : _z.message);
}
return ((_2 = (_0 = this.getCustomErrorFromHash(contract, errorHash)) !== null && _0 !== void 0 ? _0 : (_1 = error.error.data) === null || _1 === void 0 ? void 0 : _1.cause) !== null && _2 !== void 0 ? _2 : (_3 = error.error.data) === null || _3 === void 0 ? void 0 : _3.message);
}
return 'Revert reason unknown';
}
/**
* Matches error hash from node with custom errors in contract.
* @param contract Instance of contract with interface prepared from ABI.
* @param errorData Error hash string parsed from exception raised by node.
* @returns Human-readable reason explaining why transaction would revert.
*/
getCustomErrorFromHash(contract, errorData) {
if (!errorData) {
return undefined;
}
// retrieve the list of custom errors available to the contract
const customErrorNames = Object.keys(contract.interface.errors);
// index the contract's errors by the first 8 bytes of their hash
const errorsByHash = customErrorNames.reduce((acc, name) => {
return Object.assign(Object.assign({}, acc), { [contract.interface.getSighash(name)]: name });
}, {});
const errorPrefix = errorData.substring(0, 10);
if (errorPrefix in errorsByHash) {
return errorsByHash[errorPrefix];
}
return undefined;
}
}
//# sourceMappingURL=transactions.js.map