UNPKG

@1hive/connect-core

Version:

Access and interact with Aragon Organizations and their apps.

106 lines 5.14 kB
import { Interface, FunctionFragment } from '@ethersproject/abi'; import { Contract } from '@ethersproject/contracts'; import { ErrorInvalid, ErrorUnsufficientBalance } from '../errors'; import { erc20ABI, forwarderAbi, forwarderFeeAbi } from './abis'; import { findMethodAbiFragment } from './abi'; import Transaction from '../entities/Transaction'; export 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({ from: sender, to: destination, data: new Interface([methodAbiFragment]).encodeFunctionData(FunctionFragment.from(methodAbiFragment), params), }); } export async function createDirectTransactionForApp(sender, app, methodSignature, params) { if (!app) { throw new ErrorInvalid(`Could not create transaction due to missing app artifact`); } const destination = app.address; if (!app.abi) { throw new ErrorInvalid(`No ABI specified in artifact for ${destination}`); } const fragment = findMethodAbiFragment(app.abi, methodSignature); if (!fragment) { throw new ErrorInvalid(`${methodSignature} not found on ABI for ${destination}`); } return createDirectTransaction(sender, app.address, fragment, params); } export function createForwarderTransactionBuilder(sender, directTransaction) { const forwarder = new Interface(forwarderAbi); return (forwarderAddress, script) => new Transaction({ ...directTransaction, from: sender, to: forwarderAddress, data: forwarder.encodeFunctionData('forward', [script]), }); } export async function buildApprovePreTransactions(transaction, tokenData, provider) { // Token allowance pre-transaction const { from, to } = transaction; const { address: tokenAddress, value: tokenValue, spender } = tokenData; const tokenContract = new Contract(tokenAddress, erc20ABI, provider); const balance = await tokenContract.balanceOf(from); const tokenValueBN = BigInt(tokenValue.toString()); if (BigInt(balance) < tokenValueBN) { throw new 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 Interface(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({ from, to: tokenAddress, data: erc20.encodeFunctionData('approve', [approveSpender, '0']), }); transactions.push(zeroApprovalPreTransaction); } const requestedApprovalPreTransaction = new Transaction({ from, to: tokenAddress, data: erc20.encodeFunctionData('approve', [approveSpender, tokenValue]), }); transactions.push(requestedApprovalPreTransaction); return transactions; } export async function buildForwardingFeePreTransactions(forwardingTransaction, provider) { const { to: forwarderAddress, from } = forwardingTransaction; const forwarderFee = new Contract(forwarderAddress, 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 []; } //# sourceMappingURL=transactions.js.map