UNPKG

evm-blockchain-tools

Version:

This is a collection of resuseable tools to support development for EVM-powered blockchains

242 lines (212 loc) 6.24 kB
import { BigNumber, ethers, utils } from "ethers"; import * as mathjs from "mathjs"; import * as addressUtils from "./address-utils"; import { TransferValidationData, TransferValidationResult, } from "../common/interfaces"; import { ERC20_FN_SIGNATURE, ERR_CODE } from "../common/constants"; import { BlockchainService } from "../services"; export async function validateERC20Transfer( txData: ethers.providers.TransactionResponse, validationData: Partial<TransferValidationData>, blockchainService = new BlockchainService(null) ): Promise<TransferValidationResult> { try { const { from: executorAddress, to: calledContractAddress, data, value, confirmations, } = txData; const { signature, signContent, destinationAddress, amountData, minConfirmations, } = validationData; if (minConfirmations && confirmations < minConfirmations) { return { isValid: false, message: "confirmation too low", code: ERR_CODE.CONFIRMATION_TOO_LOW, }; } // Validate signature to make sure the one submiting this is really the transaction executor if (signature && signContent) { const signerAddress = blockchainService.recoverMessageSigner( signContent, signature ); if (!addressUtils.areAddressesSame(signerAddress, executorAddress)) { return { isValid: false, message: "signer is not transaction executor", code: ERR_CODE.INVALID_SIGNER_ADDRESS, }; } } if (validationData.userAddress) { if ( !addressUtils.areAddressesSame( validationData.userAddress, executorAddress ) ) { return { isValid: false, message: "executor is not user address", code: ERR_CODE.INVALID_SIGNER_ADDRESS, }; } } const { args, signature: functionName } = await blockchainService.parseERC20TxByNetwork(data, value, txData.hash); // Validate if this is Transfer function if (functionName !== ERC20_FN_SIGNATURE.TRANSFER) { return { isValid: false, message: "invalid method", code: ERR_CODE.NOT_TRANSFER_METHOD, }; } // Validate if destination address is correct const [transferredToAddress, transferredAmount] = args; if ( destinationAddress && !addressUtils.areAddressesSame(transferredToAddress, destinationAddress) ) { return { isValid: false, message: "invalid destination address", code: ERR_CODE.INVALID_DESTINATION_ADDRESS, }; } // Validates if the transferred amount is correct if (amountData?.length) { const foundValidTransferAmountData = amountData.find((data) => { if ( !addressUtils.areAddressesSame( calledContractAddress, data.contractAddress ) ) { return false; } // Don't validata amount. Just validate contract if (!data.amount) { return true; } // The amount to validate against transferredAmount const parsedAmount = ethers.utils.parseEther(data.amount); // Exact match if (!data.tolerancePercentage) { return parsedAmount.eq(transferredAmount); } // Price diff between latest price and the transferred amount const parsedTransferredAmount = utils.formatEther(transferredAmount); const priceDiff = mathjs.subtract( Number(data.amount), Number(parsedTransferredAmount) ); if (priceDiff === 0) { return true; } const diffPercentage = mathjs.divide( priceDiff, Number(parsedTransferredAmount) ); return diffPercentage * 100 <= data.tolerancePercentage; }); if (!foundValidTransferAmountData) { return { isValid: false, message: "invalid currency or amount", code: ERR_CODE.INVALID_CURRENCY_OR_AMOUNT, }; } return { isValid: true, data: { amountData: foundValidTransferAmountData, }, }; } return { isValid: true, }; } catch (error) { return { isValid: false, message: error.message, }; } } export async function validateEtherTransfer( txData: ethers.providers.TransactionResponse, validationData: Partial<Omit<TransferValidationData, "amountData">>, blockchainService = new BlockchainService(null) ): Promise<TransferValidationResult> { try { const { from: executorAddress, to: recipientAddress, confirmations, } = txData; const { signature, signContent, destinationAddress, minConfirmations, userAddress, } = validationData; if (minConfirmations && confirmations < minConfirmations) { return { isValid: false, message: "confirmation too low", code: ERR_CODE.CONFIRMATION_TOO_LOW, }; } // Validate signature to make sure the one submiting this is really the transaction executor if (signature && signContent) { const signerAddress = blockchainService.recoverMessageSigner( signContent, signature ); if (!addressUtils.areAddressesSame(signerAddress, executorAddress)) { return { isValid: false, message: "signer is not transaction executor", code: ERR_CODE.INVALID_SIGNER_ADDRESS, }; } } if ( userAddress && !addressUtils.areAddressesSame(userAddress, executorAddress) ) { return { isValid: false, message: "executor is not user address", code: ERR_CODE.INVALID_SIGNER_ADDRESS, }; } if (!addressUtils.areAddressesSame(recipientAddress, destinationAddress)) { return { isValid: false, message: "invalid recipient address", code: ERR_CODE.INVALID_DESTINATION_ADDRESS, }; } return { isValid: true, }; } catch (error) { return { isValid: false, message: error.message, }; } }