@1hive/connect-core
Version:
Access and interact with Aragon Organizations and their apps.
117 lines • 5.98 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildForwardingFeePreTransactions = exports.buildApprovePreTransactions = exports.createForwarderTransactionBuilder = exports.createDirectTransactionForApp = exports.createDirectTransaction = void 0;
const abi_1 = require("@ethersproject/abi");
const contracts_1 = require("@ethersproject/contracts");
const errors_1 = require("../errors");
const abis_1 = require("./abis");
const abi_2 = require("./abi");
const Transaction_1 = __importDefault(require("../entities/Transaction"));
async function createDirectTransaction(sender, destination, methodAbiFragment, params) {
if (methodAbiFragment.type === 'fallback' && params.length > 1) {
throw new Error(`Could not create transaction to fallback function due to too many parameters: ${params}`);
}
// The direct transaction we eventually want to perform
return new Transaction_1.default({
from: sender,
to: destination,
data: new abi_1.Interface([methodAbiFragment]).encodeFunctionData(abi_1.FunctionFragment.from(methodAbiFragment), params),
});
}
exports.createDirectTransaction = createDirectTransaction;
async function createDirectTransactionForApp(sender, app, methodSignature, params) {
if (!app) {
throw new errors_1.ErrorInvalid(`Could not create transaction due to missing app artifact`);
}
const destination = app.address;
if (!app.abi) {
throw new errors_1.ErrorInvalid(`No ABI specified in artifact for ${destination}`);
}
const fragment = abi_2.findMethodAbiFragment(app.abi, methodSignature);
if (!fragment) {
throw new errors_1.ErrorInvalid(`${methodSignature} not found on ABI for ${destination}`);
}
return createDirectTransaction(sender, app.address, fragment, params);
}
exports.createDirectTransactionForApp = createDirectTransactionForApp;
function createForwarderTransactionBuilder(sender, directTransaction) {
const forwarder = new abi_1.Interface(abis_1.forwarderAbi);
return (forwarderAddress, script) => new Transaction_1.default({
...directTransaction,
from: sender,
to: forwarderAddress,
data: forwarder.encodeFunctionData('forward', [script]),
});
}
exports.createForwarderTransactionBuilder = createForwarderTransactionBuilder;
async function buildApprovePreTransactions(transaction, tokenData, provider) {
// Token allowance pre-transaction
const { from, to } = transaction;
const { address: tokenAddress, value: tokenValue, spender } = tokenData;
const tokenContract = new contracts_1.Contract(tokenAddress, abis_1.erc20ABI, provider);
const balance = await tokenContract.balanceOf(from);
const tokenValueBN = BigInt(tokenValue.toString());
if (BigInt(balance) < tokenValueBN) {
throw new errors_1.ErrorUnsufficientBalance(`Balance too low. ${from} balance of ${tokenAddress} token is ${balance} (attempting to send ${tokenValue})`);
}
// Approve the transaction destination unless an spender is passed to approve a different contract
const approveSpender = spender !== null && spender !== void 0 ? spender : to;
const allowance = await tokenContract.allowance(from, approveSpender);
// If allowance is already greater than or equal to amount, there is no need to do an approve transaction
const allowanceBN = BigInt(allowance);
if (allowanceBN >= tokenValueBN) {
return [];
}
const transactions = [];
const erc20 = new abi_1.Interface(abis_1.erc20ABI);
// If the current allowance is greater than zero, we send a first pre-transaction to set it to zero
if (allowanceBN > BigInt(0)) {
console.warn(`${from} already approved ${approveSpender} some amount, adding one extra pre-transaction to set it to zero to avoid a failing approval.`);
const zeroApprovalPreTransaction = new Transaction_1.default({
from,
to: tokenAddress,
data: erc20.encodeFunctionData('approve', [approveSpender, '0']),
});
transactions.push(zeroApprovalPreTransaction);
}
const requestedApprovalPreTransaction = new Transaction_1.default({
from,
to: tokenAddress,
data: erc20.encodeFunctionData('approve', [approveSpender, tokenValue]),
});
transactions.push(requestedApprovalPreTransaction);
return transactions;
}
exports.buildApprovePreTransactions = buildApprovePreTransactions;
async function buildForwardingFeePreTransactions(forwardingTransaction, provider) {
const { to: forwarderAddress, from } = forwardingTransaction;
const forwarderFee = new contracts_1.Contract(forwarderAddress, abis_1.forwarderFeeAbi, provider);
const feeDetails = { amount: BigInt(0), tokenAddress: '' };
try {
const overrides = {
from,
};
// Passing the EOA as `msg.sender` to the forwardFee call is useful for use cases where the fee differs relative to the account
const [tokenAddress, amount] = await forwarderFee.forwardFee(overrides); // forwardFee() returns (address, uint256)
feeDetails.tokenAddress = tokenAddress;
feeDetails.amount = BigInt(amount);
}
catch (err) {
// Not all forwarders implement the `forwardFee()` interface
}
if (feeDetails.tokenAddress && feeDetails.amount > BigInt(0)) {
// Needs a token approval pre-transaction
const tokenData = {
address: feeDetails.tokenAddress,
spender: forwarderAddress,
value: feeDetails.amount.toString(),
};
return buildApprovePreTransactions(forwardingTransaction, tokenData, provider);
}
return [];
}
exports.buildForwardingFeePreTransactions = buildForwardingFeePreTransactions;
//# sourceMappingURL=transactions.js.map