diff --git a/.eslintrc.js b/.eslintrc.js index 5fe0ab6..8a05585 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,11 +1,6 @@ module.exports = { plugins: ["import", "prettier"], - extends: [ - "plugin:import/recommended", - "plugin:import/typescript", - "plugin:prettier/recommended", - "react-app", - ], + extends: ["plugin:import/recommended", "plugin:import/typescript", "plugin:prettier/recommended", "react-app"], ignorePatterns: ["build/", "node_modules/", "!.prettierrc.js", "lib/"], rules: { "import/order": [ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5e0adc6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "trailingComma": "all", + "singleQuote": false +} diff --git a/config-overrides.js b/config-overrides.js index 7a8877d..0338f2a 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -12,8 +12,7 @@ module.exports = { config.headers = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", - "Access-Control-Allow-Headers": - "X-Requested-With, content-type, Authorization", + "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization", }; return config; diff --git a/package.json b/package.json index 14d54fb..6e5a068 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "eslint-config-react-app": "^6.0.0", "eslint-plugin-flowtype": "^5.7.2", "eslint-plugin-import": "^2.23.2", - "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-react": "^7.23.2", "eslint-plugin-react-hooks": "^4.2.0", diff --git a/src/App.tsx b/src/App.tsx index e2e9faf..5c741d7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,16 +19,10 @@ const App: React.FC = () => { const { tokenList, isLoading } = useTokenList(); const [submitting, setSubmitting] = useState(false); const [transferContent, setTransferContent] = useState([]); - const [csvText, setCsvText] = useState( - "token_address,receiver,amount,decimals" - ); - const { addMessage, setCodeWarnings, setMessages } = - useContext(MessageContext); + const [csvText, setCsvText] = useState("token_address,receiver,amount,decimals"); + const { addMessage, setCodeWarnings, setMessages } = useContext(MessageContext); - const web3Provider = useMemo( - () => new ethers.providers.Web3Provider(new SafeAppProvider(safe, sdk)), - [sdk, safe] - ); + const web3Provider = useMemo(() => new ethers.providers.Web3Provider(new SafeAppProvider(safe, sdk)), [sdk, safe]); const onChangeTextHandler = useCallback( (csvText: string) => { @@ -39,23 +33,20 @@ const App: React.FC = () => { .then(([transfers, warnings]) => { console.log("CSV parsed!"); const summary = transfersToSummary(transfers); - checkAllBalances(summary, web3Provider, safe, tokenList).then( - (insufficientBalances) => - setMessages( - insufficientBalances.map((insufficientBalanceInfo) => ({ - message: `Insufficient Balance: ${insufficientBalanceInfo.transferAmount} of ${insufficientBalanceInfo.token}`, - severity: "warning", - })) - ) + checkAllBalances(summary, web3Provider, safe, tokenList).then((insufficientBalances) => + setMessages( + insufficientBalances.map((insufficientBalanceInfo) => ({ + message: `Insufficient Balance: ${insufficientBalanceInfo.transferAmount} of ${insufficientBalanceInfo.token}`, + severity: "warning", + })), + ), ); setTransferContent(transfers); setCodeWarnings(warnings); }) - .catch((reason: any) => - addMessage({ severity: "error", message: reason.message }) - ); + .catch((reason: any) => addMessage({ severity: "error", message: reason.message })); }, - [addMessage, safe, setCodeWarnings, setMessages, tokenList, web3Provider] + [addMessage, safe, setCodeWarnings, setMessages, tokenList, web3Provider], ); const submitTx = useCallback(async () => { diff --git a/src/__tests__/parser.test.ts b/src/__tests__/parser.test.ts index b5e928f..1d87578 100644 --- a/src/__tests__/parser.test.ts +++ b/src/__tests__/parser.test.ts @@ -38,85 +38,43 @@ describe("Parsing CSVs ", () => { it("should throw errors for invalid CSVs", async () => { // thins csv contains more values than headers in row1 const invalidCSV = "head1,header2\nvalue1,value2,value3"; - expect(parseCSV(invalidCSV, tokenList)).to.be.rejectedWith( - "column header mismatch expected: 2 columns got: 3" - ); + expect(parseCSV(invalidCSV, tokenList)).to.be.rejectedWith("column header mismatch expected: 2 columns got: 3"); }); it("should transform simple, valid CSVs correctly", async () => { const rowWithoutDecimal = [listedToken.address, validReceiverAddress, "1"]; - const rowWithDecimal = [ - unlistedTokenAddress, - validReceiverAddress, - "69.420", - "18", - ]; + const rowWithDecimal = [unlistedTokenAddress, validReceiverAddress, "69.420", "18"]; const rowWithoutTokenAddress = ["", validReceiverAddress, "1"]; const [payment, warnings] = await parseCSV( - csvStringFromRows( - rowWithoutDecimal, - rowWithDecimal, - rowWithoutTokenAddress - ), - tokenList + csvStringFromRows(rowWithoutDecimal, rowWithDecimal, rowWithoutTokenAddress), + tokenList, ); expect(warnings).to.be.empty; expect(payment).to.have.lengthOf(3); - const [ - paymentWithoutDecimal, - paymentWithDecimal, - paymentWithoutTokenAddress, - ] = payment; + const [paymentWithoutDecimal, paymentWithDecimal, paymentWithoutTokenAddress] = payment; expect(paymentWithoutDecimal.decimals).to.be.undefined; expect(paymentWithoutDecimal.receiver).to.equal(validReceiverAddress); expect(paymentWithoutDecimal.tokenAddress).to.equal(listedToken.address); expect(paymentWithoutDecimal.amount.isEqualTo(new BigNumber(1))).to.be.true; expect(paymentWithDecimal.receiver).to.equal(validReceiverAddress); - expect(paymentWithDecimal.tokenAddress.toLowerCase()).to.equal( - unlistedTokenAddress.toLowerCase() - ); + expect(paymentWithDecimal.tokenAddress.toLowerCase()).to.equal(unlistedTokenAddress.toLowerCase()); expect(paymentWithDecimal.decimals).to.equal(18); - expect(paymentWithDecimal.amount.isEqualTo(new BigNumber(69.42))).to.be - .true; + expect(paymentWithDecimal.amount.isEqualTo(new BigNumber(69.42))).to.be.true; expect(paymentWithoutTokenAddress.decimals).to.be.undefined; expect(paymentWithoutTokenAddress.receiver).to.equal(validReceiverAddress); expect(paymentWithoutTokenAddress.tokenAddress).to.equal(null); - expect(paymentWithoutTokenAddress.amount.isEqualTo(new BigNumber(1))).to.be - .true; + expect(paymentWithoutTokenAddress.amount.isEqualTo(new BigNumber(1))).to.be.true; }); it("should generate validation warnings", async () => { - const rowWithNegativeAmount = [ - listedToken.address, - validReceiverAddress, - "-1", - ]; - const rowWithInvalidDecimal = [ - unlistedTokenAddress, - validReceiverAddress, - "1", - "-2", - ]; - const unlistedTokenWithoutDecimal = [ - unlistedTokenAddress, - validReceiverAddress, - "1", - ]; - const rowWithInvalidTokenAddress = [ - "0x420", - validReceiverAddress, - "1", - "18", - ]; - const rowWithInvalidReceiverAddress = [ - unlistedTokenAddress, - "0x420", - "1", - "18", - ]; + const rowWithNegativeAmount = [listedToken.address, validReceiverAddress, "-1"]; + const rowWithInvalidDecimal = [unlistedTokenAddress, validReceiverAddress, "1", "-2"]; + const unlistedTokenWithoutDecimal = [unlistedTokenAddress, validReceiverAddress, "1"]; + const rowWithInvalidTokenAddress = ["0x420", validReceiverAddress, "1", "18"]; + const rowWithInvalidReceiverAddress = [unlistedTokenAddress, "0x420", "1", "18"]; const [payment, warnings] = await parseCSV( csvStringFromRows( @@ -124,9 +82,9 @@ describe("Parsing CSVs ", () => { rowWithInvalidDecimal, unlistedTokenWithoutDecimal, rowWithInvalidTokenAddress, - rowWithInvalidReceiverAddress + rowWithInvalidReceiverAddress, ), - tokenList + tokenList, ); expect(warnings).to.have.lengthOf(5); const [ @@ -138,26 +96,18 @@ describe("Parsing CSVs ", () => { ] = warnings; expect(payment).to.be.empty; - expect(warningNegativeAmount.message).to.equal( - "Only positive amounts possible: -1" - ); + expect(warningNegativeAmount.message).to.equal("Only positive amounts possible: -1"); expect(warningNegativeAmount.lineNo).to.equal(1); expect(warningNegativeDecimals.message).to.equal("Invalid decimals: -2"); expect(warningNegativeDecimals.lineNo).to.equal(2); - expect(warningUndefinedDecimals.message).to.equal( - "Invalid decimals: undefined" - ); + expect(warningUndefinedDecimals.message).to.equal("Invalid decimals: undefined"); expect(warningUndefinedDecimals.lineNo).to.equal(3); - expect(warningInvalidTokenAddress.message).to.equal( - "Invalid Token Address: 0x420" - ); + expect(warningInvalidTokenAddress.message).to.equal("Invalid Token Address: 0x420"); expect(warningInvalidTokenAddress.lineNo).to.equal(4); - expect(warningInvalidReceiverAddress.message).to.equal( - "Invalid Receiver Address: 0x420" - ); + expect(warningInvalidReceiverAddress.message).to.equal("Invalid Receiver Address: 0x420"); expect(warningInvalidReceiverAddress.lineNo).to.equal(5); }); }); diff --git a/src/__tests__/transfers.test.ts b/src/__tests__/transfers.test.ts index 04cad70..d6adfec 100644 --- a/src/__tests__/transfers.test.ts +++ b/src/__tests__/transfers.test.ts @@ -51,26 +51,17 @@ describe("Build Transfers:", () => { }, ]; - const [listedTransfer, unlistedTransfer, nativeTransfer] = buildTransfers( - largePayments, - tokenList - ); + const [listedTransfer, unlistedTransfer, nativeTransfer] = buildTransfers(largePayments, tokenList); expect(listedTransfer.value).to.be.equal("0"); expect(listedTransfer.to).to.be.equal(listedToken.address); expect(listedTransfer.data).to.be.equal( - erc20Interface.encodeFunctionData("transfer", [ - receiver, - MAX_U256.toFixed(), - ]) + erc20Interface.encodeFunctionData("transfer", [receiver, MAX_U256.toFixed()]), ); expect(unlistedTransfer.value).to.be.equal("0"); expect(unlistedTransfer.to).to.be.equal(testData.unlistedToken.address); expect(unlistedTransfer.data).to.be.equal( - erc20Interface.encodeFunctionData("transfer", [ - receiver, - MAX_U256.toFixed(), - ]) + erc20Interface.encodeFunctionData("transfer", [receiver, MAX_U256.toFixed()]), ); expect(nativeTransfer.value).to.be.equal(MAX_U256.toFixed()); @@ -106,17 +97,11 @@ describe("Build Transfers:", () => { }, ]; - const [listed, unlisted, native] = buildTransfers( - smallPayments, - tokenList - ); + const [listed, unlisted, native] = buildTransfers(smallPayments, tokenList); expect(listed.value).to.be.equal("0"); expect(listed.to).to.be.equal(listedToken.address); expect(listed.data).to.be.equal( - erc20Interface.encodeFunctionData("transfer", [ - receiver, - toWei(tinyAmount, listedToken.decimals).toFixed(), - ]) + erc20Interface.encodeFunctionData("transfer", [receiver, toWei(tinyAmount, listedToken.decimals).toFixed()]), ); expect(unlisted.value).to.be.equal("0"); @@ -125,7 +110,7 @@ describe("Build Transfers:", () => { erc20Interface.encodeFunctionData("transfer", [ receiver, toWei(tinyAmount, testData.unlistedToken.decimals).toFixed(), - ]) + ]), ); expect(native.value).to.be.equal(toWei(tinyAmount, 18).toString()); @@ -161,17 +146,11 @@ describe("Build Transfers:", () => { }, ]; - const [listed, unlisted, native] = buildTransfers( - mixedPayments, - tokenList - ); + const [listed, unlisted, native] = buildTransfers(mixedPayments, tokenList); expect(listed.value).to.be.equal("0"); expect(listed.to).to.be.equal(listedToken.address); expect(listed.data).to.be.equal( - erc20Interface.encodeFunctionData("transfer", [ - receiver, - toWei(mixedAmount, listedToken.decimals).toFixed(), - ]) + erc20Interface.encodeFunctionData("transfer", [receiver, toWei(mixedAmount, listedToken.decimals).toFixed()]), ); expect(unlisted.value).to.be.equal("0"); @@ -180,7 +159,7 @@ describe("Build Transfers:", () => { erc20Interface.encodeFunctionData("transfer", [ receiver, toWei(mixedAmount, testData.unlistedToken.decimals).toFixed(), - ]) + ]), ); expect(native.value).to.be.equal(toWei(mixedAmount, 18).toFixed()); @@ -210,10 +189,7 @@ describe("Build Transfers:", () => { expect(transfer.value).to.be.equal("0"); expect(transfer.to).to.be.equal(crappyToken.address); expect(transfer.data).to.be.equal( - erc20Interface.encodeFunctionData("transfer", [ - receiver, - toWei(amount, crappyToken.decimals).toFixed(), - ]) + erc20Interface.encodeFunctionData("transfer", [receiver, toWei(amount, crappyToken.decimals).toFixed()]), ); }); }); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index 5edaa75..b15cc97 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -20,9 +20,7 @@ describe("toWei()", () => { it("mixed", () => { expect(toWei(1.234, 0).eq(ONE)); expect(toWei(1.234, 3).eq(new BigNumber(1234))); - expect( - toWei(1.00000000000000000001, 18).eq(new BigNumber(1000000000000000000)) - ); + expect(toWei(1.00000000000000000001, 18).eq(new BigNumber(1000000000000000000))); }); }); @@ -43,7 +41,7 @@ describe("fromWei()", () => { }); }); -describe("transerToSummary()", () => { +describe("transferToSummary()", () => { it("works for integer native currency", () => { const transfers: Payment[] = [ { @@ -107,9 +105,7 @@ describe("transerToSummary()", () => { }, ]; const summary = transfersToSummary(transfers); - expect( - summary.get(testData.unlistedToken.address).amount.toFixed() - ).to.equal("0.111"); + expect(summary.get(testData.unlistedToken.address).amount.toFixed()).to.equal("0.111"); }); it("works for integer in erc20", () => { @@ -131,9 +127,7 @@ describe("transerToSummary()", () => { }, ]; const summary = transfersToSummary(transfers); - expect( - summary.get(testData.unlistedToken.address).amount.toFixed() - ).to.equal("6"); + expect(summary.get(testData.unlistedToken.address).amount.toFixed()).to.equal("6"); }); it("works for mixed payments", () => { @@ -165,9 +159,7 @@ describe("transerToSummary()", () => { }, ]; const summary = transfersToSummary(transfers); - expect( - summary.get(testData.unlistedToken.address).amount.toFixed() - ).to.equal("6.4"); + expect(summary.get(testData.unlistedToken.address).amount.toFixed()).to.equal("6.4"); expect(summary.get(null).amount.toFixed()).to.equal("3.33"); }); }); diff --git a/src/components/CSVEditor.tsx b/src/components/CSVEditor.tsx index b187f2d..cdb2eac 100644 --- a/src/components/CSVEditor.tsx +++ b/src/components/CSVEditor.tsx @@ -45,7 +45,7 @@ export const CSVEditor = (props: CSVEditorProps): JSX.Element => { type: "fullLine", startCol: 0, endCol: 30, - }) + }), )} annotations={codeWarnings.map( (warning): IAnnotation => ({ @@ -53,7 +53,7 @@ export const CSVEditor = (props: CSVEditorProps): JSX.Element => { type: "error", column: 0, text: warning.message, - }) + }), )} /> diff --git a/src/components/CSVForm.tsx b/src/components/CSVForm.tsx index 6e0e03e..c4f6e00 100644 --- a/src/components/CSVForm.tsx +++ b/src/components/CSVForm.tsx @@ -1,11 +1,5 @@ -import { - Card, - Text, - Button, - Table, - Loader, -} from "@gnosis.pm/safe-react-components"; -import React, { useContext } from "react"; +import { Card, Text, Button, Table, Loader } from "@gnosis.pm/safe-react-components"; +import { useContext } from "react"; import styled from "styled-components"; import { MessageContext } from "../../src/contexts/MessageContextProvider"; @@ -92,21 +86,12 @@ export const CSVForm = (props: CSVFormProps): JSX.Element => { <>
- ) : ( - )} diff --git a/src/components/CSVUpload.tsx b/src/components/CSVUpload.tsx index f159bc7..e08f3c7 100644 --- a/src/components/CSVUpload.tsx +++ b/src/components/CSVUpload.tsx @@ -1,9 +1,4 @@ -import { - Button, - Link, - Text, - theme as GnosisTheme, -} from "@gnosis.pm/safe-react-components"; +import { Button, Link, Text, theme as GnosisTheme } from "@gnosis.pm/safe-react-components"; import { createStyles } from "@material-ui/core"; import React, { useCallback, useMemo } from "react"; import { useDropzone } from "react-dropzone"; @@ -28,16 +23,10 @@ export const CSVUpload = (props: CSVUploadProps): JSX.Element => { reader.readAsText(file); }); }, - [onChange] + [onChange], ); - const { - getRootProps, - getInputProps, - isDragActive, - isDragAccept, - isDragReject, - } = useDropzone({ + const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({ maxFiles: 1, onDrop, accept: "text/csv", @@ -50,7 +39,7 @@ export const CSVUpload = (props: CSVUploadProps): JSX.Element => { ...(isDragAccept ? styles.acceptStyle : {}), ...(isDragReject ? styles.rejectStyle : {}), }), - [isDragActive, isDragReject, isDragAccept] + [isDragActive, isDragReject, isDragAccept], ); return ( @@ -74,12 +63,7 @@ export const CSVUpload = (props: CSVUploadProps): JSX.Element => { gap: "8px", }} > - diff --git a/src/contexts/MessageContextProvider.tsx b/src/contexts/MessageContextProvider.tsx index 11bf7a7..2ef8361 100644 --- a/src/contexts/MessageContextProvider.tsx +++ b/src/contexts/MessageContextProvider.tsx @@ -38,9 +38,7 @@ export const MessageContextProvider = (props: MessageContextProviderProps) => { const [codeWarnings, setCodeWarnings] = useState([]); const removeMessage = (messageToRemove: Message | CodeWarning) => - setMessages( - messages.filter((message) => message.message !== messageToRemove.message) - ); + setMessages(messages.filter((message) => message.message !== messageToRemove.message)); const addMessage = (messageToAdd: Message | CodeWarning) => { // Do not add equal message @@ -58,9 +56,5 @@ export const MessageContextProvider = (props: MessageContextProviderProps) => { setCodeWarnings, }; - return ( - - {props.children} - - ); + return {props.children}; }; diff --git a/src/erc20.ts b/src/erc20.ts index f4a54de..5a68956 100644 --- a/src/erc20.ts +++ b/src/erc20.ts @@ -4,9 +4,6 @@ import { IERC20, IERC20__factory } from "./contracts"; export const erc20Interface = IERC20__factory.createInterface(); -export function erc20Instance( - address: string, - provider: ethers.providers.Provider -): IERC20 { +export function erc20Instance(address: string, provider: ethers.providers.Provider): IERC20 { return IERC20__factory.connect(address, provider); } diff --git a/src/hooks/tokenList.ts b/src/hooks/tokenList.ts index 081a252..5c0b70c 100644 --- a/src/hooks/tokenList.ts +++ b/src/hooks/tokenList.ts @@ -17,9 +17,7 @@ function tokenMap(tokenList: TokenInfo[]): TokenMap { return res; } -export const fetchTokenList = async ( - networkName: string -): Promise => { +export const fetchTokenList = async (networkName: string): Promise => { let tokens: TokenInfo[]; if (networkName === "MAINNET") { const mainnetTokenURL = "https://tokens.coingecko.com/uniswap/all.json"; diff --git a/src/index.tsx b/src/index.tsx index eac8b6d..ee8a60d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -26,5 +26,5 @@ ReactDOM.render( , - document.getElementById("root") + document.getElementById("root"), ); diff --git a/src/parser.ts b/src/parser.ts index 8845299..7903634 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -27,34 +27,27 @@ const generateWarnings = ( // We need the row parameter because of the api of fast-csv _row: Payment, rowNumber: number, - warnings: string + warnings: string, ) => { - const messages: CodeWarning[] = warnings - .split(";") - .map((warning: string) => ({ - message: warning, - severity: "warning", - lineNo: rowNumber, - })); + const messages: CodeWarning[] = warnings.split(";").map((warning: string) => ({ + message: warning, + severity: "warning", + lineNo: rowNumber, + })); return messages; }; -export const parseCSV = ( - csvText: string, - tokenList: TokenMap -): Promise<[Payment[], CodeWarning[]]> => { +export const parseCSV = (csvText: string, tokenList: TokenMap): Promise<[Payment[], CodeWarning[]]> => { return new Promise<[Payment[], CodeWarning[]]>((resolve, reject) => { const results: any[] = []; const resultingWarnings: CodeWarning[] = []; parseString(csvText, { headers: true }) .transform(transformRow) - .validate((row: Payment, callback: RowValidateCallback) => - validateRow(row, tokenList, callback) - ) + .validate((row: Payment, callback: RowValidateCallback) => validateRow(row, tokenList, callback)) .on("data", (data) => results.push(data)) .on("end", () => resolve([results, resultingWarnings])) .on("data-invalid", (row: Payment, rowNumber: number, warnings: string) => - resultingWarnings.push(...generateWarnings(row, rowNumber, warnings)) + resultingWarnings.push(...generateWarnings(row, rowNumber, warnings)), ) .on("error", (error) => reject(error)); }); @@ -72,25 +65,15 @@ const transformRow = (row: CSVRow): Payment => ({ ? utils.getAddress(row.token_address) : row.token_address, amount: new BigNumber(row.amount), - receiver: utils.isAddress(row.receiver) - ? utils.getAddress(row.receiver) - : row.receiver, + receiver: utils.isAddress(row.receiver) ? utils.getAddress(row.receiver) : row.receiver, decimals: row.decimals ? Number(row.decimals) : undefined, }); /** * Validates, that addresses are valid, the amount is big enough and a decimal is given or can be found in token lists. */ -const validateRow = ( - row: Payment, - tokenList: TokenMap, - callback: RowValidateCallback -) => { - const warnings = [ - ...areAddressesValid(row), - ...isAmountPositive(row), - ...isDecimalValid(row, tokenList), - ]; +const validateRow = (row: Payment, tokenList: TokenMap, callback: RowValidateCallback) => { + const warnings = [...areAddressesValid(row), ...isAmountPositive(row), ...isDecimalValid(row, tokenList)]; callback(null, warnings.length === 0, warnings.join(";")); }; @@ -106,20 +89,15 @@ const areAddressesValid = (row: Payment): string[] => { }; const isAmountPositive = (row: Payment): string[] => - row.amount.isGreaterThan(0) - ? [] - : ["Only positive amounts possible: " + row.amount.toFixed()]; + row.amount.isGreaterThan(0) ? [] : ["Only positive amounts possible: " + row.amount.toFixed()]; const isDecimalValid = (row: Payment, tokenList: TokenMap): string[] => { if (row.tokenAddress == null || row.tokenAddress === "") { return []; } else { const decimals = - tokenList.get( - utils.isAddress(row.tokenAddress) - ? utils.getAddress(row.tokenAddress) - : row.tokenAddress - )?.decimals || row.decimals; + tokenList.get(utils.isAddress(row.tokenAddress) ? utils.getAddress(row.tokenAddress) : row.tokenAddress) + ?.decimals || row.decimals; return decimals >= 0 ? [] : ["Invalid decimals: " + decimals]; } }; diff --git a/src/transfers.ts b/src/transfers.ts index 14fab70..a9da133 100644 --- a/src/transfers.ts +++ b/src/transfers.ts @@ -5,10 +5,7 @@ import { TokenMap } from "./hooks/tokenList"; import { Payment } from "./parser"; import { toWei } from "./utils"; -export function buildTransfers( - transferData: Payment[], - tokenList: TokenMap -): Transaction[] { +export function buildTransfers(transferData: Payment[], tokenList: TokenMap): Transaction[] { const txList: Transaction[] = transferData.map((transfer) => { if (transfer.tokenAddress === null) { // Native asset transfer @@ -19,16 +16,12 @@ export function buildTransfers( }; } else { // ERC20 transfer - const decimals = - tokenList.get(transfer.tokenAddress)?.decimals || transfer.decimals; + const decimals = tokenList.get(transfer.tokenAddress)?.decimals || transfer.decimals; const amountData = toWei(transfer.amount, decimals); return { to: transfer.tokenAddress, value: "0", - data: erc20Interface.encodeFunctionData("transfer", [ - transfer.receiver, - amountData.toFixed(), - ]), + data: erc20Interface.encodeFunctionData("transfer", [transfer.receiver, amountData.toFixed()]), }; } }); diff --git a/src/utils.ts b/src/utils.ts index 2899cc8..e8e11a1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -12,10 +12,7 @@ export const TWO = new BigNumber(2); export const TEN = new BigNumber(10); export const MAX_U256 = TWO.pow(255).minus(1); -export function toWei( - amount: string | number | BigNumber, - decimals: number -): BigNumber { +export function toWei(amount: string | number | BigNumber, decimals: number): BigNumber { let res = TEN.pow(decimals).multipliedBy(amount); if (res.decimalPlaces() > 0) { // TODO - reinstate this warning by passing along with return content @@ -40,10 +37,7 @@ export type SummaryEntry = { }; export const transfersToSummary = (transfers: Payment[]) => { - return transfers.reduce((previousValue, currentValue): Map< - string, - SummaryEntry - > => { + return transfers.reduce((previousValue, currentValue): Map => { let tokenSummary = previousValue.get(currentValue.tokenAddress); if (typeof tokenSummary === "undefined") { tokenSummary = { @@ -68,16 +62,13 @@ export const checkAllBalances = async ( summary: Map, web3Provider: ethers.providers.Web3Provider, safe: SafeInfo, - tokenList: TokenMap + tokenList: TokenMap, ): Promise => { const insufficientTokens: InsufficientBalanceInfo[] = []; for (const { tokenAddress, amount, decimals } of summary.values()) { if (tokenAddress === null) { // Check ETH Balance - const tokenBalance = await web3Provider.getBalance( - safe.safeAddress, - "latest" - ); + const tokenBalance = await web3Provider.getBalance(safe.safeAddress, "latest"); if (!isSufficientBalance(tokenBalance, amount, 18)) { insufficientTokens.push({ token: "ETH", @@ -85,24 +76,13 @@ export const checkAllBalances = async ( }); } } else { - const erc20Contract = erc20Instance( - utils.getAddress(tokenAddress), - web3Provider - ); - const tokenBalance = await erc20Contract - .balanceOf(safe.safeAddress) - .catch((reason) => { - console.error(reason); - return ethers.BigNumber.from(-1); - }); + const erc20Contract = erc20Instance(utils.getAddress(tokenAddress), web3Provider); + const tokenBalance = await erc20Contract.balanceOf(safe.safeAddress).catch((reason) => { + console.error(reason); + return ethers.BigNumber.from(-1); + }); const tokenInfo = tokenList.get(tokenAddress); - if ( - !isSufficientBalance( - tokenBalance, - amount, - tokenInfo?.decimals || decimals - ) - ) { + if (!isSufficientBalance(tokenBalance, amount, tokenInfo?.decimals || decimals)) { insufficientTokens.push({ token: tokenInfo?.symbol || tokenAddress, transferAmount: amount.toFixed(), @@ -113,11 +93,7 @@ export const checkAllBalances = async ( return insufficientTokens; }; -const isSufficientBalance = ( - tokenBalance: ethers.BigNumber, - transferAmount: BigNumber, - decimals: number -) => { +const isSufficientBalance = (tokenBalance: ethers.BigNumber, transferAmount: BigNumber, decimals: number) => { const tokenBalanceNumber = new BigNumber(tokenBalance.toString()); const transferAmountInWei = toWei(transferAmount, decimals); return tokenBalanceNumber.gte(transferAmountInWei); diff --git a/yarn.lock b/yarn.lock index 6b48bcb..cb90f22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2301,9 +2301,9 @@ integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== "@types/node@*": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.3.tgz#ee09fcaac513576474c327da5818d421b98db88a" - integrity sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ== + version "15.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" + integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== "@types/node@^14.0.1", "@types/node@^14.14.45": version "14.14.45" @@ -2449,12 +2449,12 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.1.1", "@typescript-eslint/eslint-plugin@^4.5.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" - integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.24.0.tgz#03801ffc25b2af9d08f3dc9bccfc0b7ce3780d0f" + integrity sha512-qbCgkPM7DWTsYQGjx9RTuQGswi+bEt0isqDBeo+CKV0953zqI0Tp7CZ7Fi9ipgFA6mcQqF4NOVNwS/f2r6xShw== dependencies: - "@typescript-eslint/experimental-utils" "4.23.0" - "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/experimental-utils" "4.24.0" + "@typescript-eslint/scope-manager" "4.24.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -2462,15 +2462,15 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.23.0", "@typescript-eslint/experimental-utils@^4.0.1": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" - integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== +"@typescript-eslint/experimental-utils@4.24.0", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.24.0.tgz#c23ead9de44b99c3a5fd925c33a106b00165e172" + integrity sha512-IwTT2VNDKH1h8RZseMH4CcYBz6lTvRoOLDuuqNZZoThvfHEhOiZPQCow+5El3PtyxJ1iDr6UXZwYtE3yZQjhcw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" + "@typescript-eslint/scope-manager" "4.24.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/typescript-estree" "4.24.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" @@ -2486,32 +2486,32 @@ eslint-utils "^2.0.0" "@typescript-eslint/parser@^4.1.1", "@typescript-eslint/parser@^4.5.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" - integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.24.0.tgz#2e5f1cc78ffefe43bfac7e5659309a92b09a51bd" + integrity sha512-dj1ZIh/4QKeECLb2f/QjRwMmDArcwc2WorWPRlB8UNTZlY1KpTVsbX7e3ZZdphfRw29aTFUSNuGB8w9X5sS97w== dependencies: - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" + "@typescript-eslint/scope-manager" "4.24.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/typescript-estree" "4.24.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" - integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== +"@typescript-eslint/scope-manager@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.24.0.tgz#38088216f0eaf235fa30ed8cabf6948ec734f359" + integrity sha512-9+WYJGDnuC9VtYLqBhcSuM7du75fyCS/ypC8c5g7Sdw7pGL4NDTbeH38eJPfzIydCHZDoOgjloxSAA3+4l/zsA== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/visitor-keys" "4.24.0" "@typescript-eslint/types@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== -"@typescript-eslint/types@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" - integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== +"@typescript-eslint/types@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.24.0.tgz#6d0cca2048cbda4e265e0c4db9c2a62aaad8228c" + integrity sha512-tkZUBgDQKdvfs8L47LaqxojKDE+mIUmOzdz7r+u+U54l3GDkTpEbQ1Jp3cNqqAU9vMUCBA1fitsIhm7yN0vx9Q== "@typescript-eslint/typescript-estree@3.10.1": version "3.10.1" @@ -2527,13 +2527,13 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" - integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== +"@typescript-eslint/typescript-estree@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.24.0.tgz#b49249679a98014d8b03e8d4b70864b950e3c90f" + integrity sha512-kBDitL/by/HK7g8CYLT7aKpAwlR8doshfWz8d71j97n5kUa5caHWvY0RvEUEanL/EqBJoANev8Xc/mQ6LLwXGA== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types" "4.24.0" + "@typescript-eslint/visitor-keys" "4.24.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -2547,12 +2547,12 @@ dependencies: eslint-visitor-keys "^1.1.0" -"@typescript-eslint/visitor-keys@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" - integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== +"@typescript-eslint/visitor-keys@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.24.0.tgz#a8fafdc76cad4e04a681a945fbbac4e35e98e297" + integrity sha512-4ox1sjmGHIxjEDBnMCtWFFhErXtKA1Ec0sBpuz0fqf3P+g3JFGyTxxbF06byw0FRsPnnbq44cKivH7Ks1/0s6g== dependencies: - "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/types" "4.24.0" eslint-visitor-keys "^2.0.0" "@uniswap/token-lists@^1.0.0-beta.24": @@ -2813,9 +2813,9 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^8.0.1: - version "8.3.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.3.0.tgz#25ee7348e32cdc4a1dbb38256bf6bdc451dd577c" - integrity sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q== + version "8.4.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.4.0.tgz#48984fdb2ce225cab15795f0772a8d85669075e4" + integrity sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -4771,9 +4771,9 @@ detect-newline@^3.0.0: integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== detect-node@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.5.tgz#9d270aa7eaa5af0b72c4c9d9b814e7f4ce738b79" - integrity sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== detect-port-alt@1.1.6: version "1.1.6" @@ -4962,9 +4962,9 @@ ejs@^2.6.1: integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.723: - version "1.3.727" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" - integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== + version "1.3.728" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz#dbedd6373f595ae10a13d146b66bece4c1afa5bd" + integrity sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA== elliptic@6.5.4, elliptic@^6.5.3: version "6.5.4" @@ -5222,7 +5222,7 @@ eslint-plugin-jest@^24.1.0: dependencies: "@typescript-eslint/experimental-utils" "^4.0.1" -eslint-plugin-jsx-a11y@^6.3.1, eslint-plugin-jsx-a11y@^6.4.1: +eslint-plugin-jsx-a11y@^6.3.1: version "6.4.1" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd" integrity sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg== @@ -8447,9 +8447,9 @@ node-notifier@^8.0.0: which "^2.0.2" node-releases@^1.1.61, node-releases@^1.1.71: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" @@ -11120,9 +11120,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== + version "3.0.8" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.8.tgz#eb1e97ad99b11bf3f82a3b71a0472dd9a00f2ecf" + integrity sha512-NDgA96EnaLSvtbM7trJj+t1LUR3pirkDCcz9nOUlPb5DMBGsH7oES6C3hs3j7R9oHEa1EMvReS/BUAIT5Tcr0g== spdy-transport@^3.0.0: version "3.0.0" @@ -11506,9 +11506,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== table@^6.0.4: - version "6.7.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.0.tgz#26274751f0ee099c547f6cb91d3eff0d61d155b2" - integrity sha512-SAM+5p6V99gYiiy2gT5ArdzgM1dLDed0nkrWmG6Fry/bUS/m9x83BwpJUOf1Qj/x2qJd+thL6IkIx7qPGRxqBw== + version "6.7.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" + integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== dependencies: ajv "^8.0.1" lodash.clonedeep "^4.5.0"