Skip to content

Commit

Permalink
Fixes #357 and #358 (#359)
Browse files Browse the repository at this point in the history
* Fixes #357 and #358

* Ignores tokens without address in token lists
* Allows to use amount or value column header

Other changes
* test for column header backward compatibility
* fixes bug for native asset drain transfers
* renames value => amount for default column headers
* generated transfers (drain) use amount instead of value

Co-authored-by: schmanu <[email protected]>
  • Loading branch information
schmanu and schmanu authored Feb 11, 2022
1 parent e49a4f5 commit 848bc3b
Show file tree
Hide file tree
Showing 14 changed files with 67 additions and 46 deletions.
2 changes: 1 addition & 1 deletion public/sample.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
token_type,token_address,receiver,value,id
token_type,token_address,receiver,amount,id
erc20,0x6810e776880c02933d47db1b9fc05908e5386b96,0x1000000000000000000000000000000000000000,0.0001
erc20,0x6b175474e89094c44da98b954eedeac495271d0f,0x2000000000000000000000000000000000000000,0.0001
native,,0x3000000000000000000000000000000000000000,0.0001
Expand Down
60 changes: 40 additions & 20 deletions src/__tests__/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ chai.use(chaiAsPromised);
* @param rows array of row-arrays
*/
const csvStringFromRows = (...rows: string[][]): string => {
const headerRow = "token_type,token_address,receiver,value,id";
const headerRow = "token_type,token_address,receiver,amount,id";
return [headerRow, ...rows.map((row) => row.join(","))].join("\n");
};

Expand Down Expand Up @@ -197,7 +197,7 @@ 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/values possible: -1");
expect(warningNegativeAmount.lineNo).to.equal(1);

expect(warningTokenNotFound.message.toLowerCase()).to.equal(
Expand Down Expand Up @@ -270,31 +270,31 @@ describe("Parsing CSVs ", () => {
payment as CollectibleTransfer[];
expect(transferErc721AndAddress.receiver).to.equal(validReceiverAddress);
expect(transferErc721AndAddress.tokenAddress).to.equal(testData.addresses.dummyErc721Address);
expect(transferErc721AndAddress.value).to.be.undefined;
expect(transferErc721AndAddress.amount).to.be.undefined;
expect(transferErc721AndAddress.tokenId.isEqualTo(new BigNumber(1))).to.be.true;
expect(transferErc721AndAddress.receiverEnsName).to.be.null;

expect(transferErc721AndENS.receiver).to.equal(testData.addresses.receiver2);
expect(transferErc721AndENS.tokenAddress).to.equal(testData.addresses.dummyErc721Address);
expect(transferErc721AndENS.tokenId.isEqualTo(new BigNumber(69))).to.be.true;
expect(transferErc721AndENS.value).to.be.undefined;
expect(transferErc721AndENS.amount).to.be.undefined;
expect(transferErc721AndENS.receiverEnsName).to.equal("receiver2.eth");

expect(transferErc1155AndAddress.receiver).to.equal(validReceiverAddress);
expect(transferErc1155AndAddress.tokenAddress.toLowerCase()).to.equal(
testData.addresses.dummyErc1155Address.toLowerCase(),
);
expect(transferErc1155AndAddress.value).not.to.be.undefined;
expect(transferErc1155AndAddress.value?.isEqualTo(new BigNumber(69))).to.be.true;
expect(transferErc1155AndAddress.amount).not.to.be.undefined;
expect(transferErc1155AndAddress.amount?.isEqualTo(new BigNumber(69))).to.be.true;
expect(transferErc1155AndAddress.tokenId.isEqualTo(new BigNumber(420))).to.be.true;
expect(transferErc1155AndAddress.receiverEnsName).to.be.null;

expect(transferErc1155AndENS.receiver).to.equal(testData.addresses.receiver3);
expect(transferErc1155AndENS.tokenAddress.toLowerCase()).to.equal(
testData.addresses.dummyErc1155Address.toLowerCase(),
);
expect(transferErc1155AndENS.value).not.to.be.undefined;
expect(transferErc1155AndENS.value?.isEqualTo(new BigNumber(9))).to.be.true;
expect(transferErc1155AndENS.amount).not.to.be.undefined;
expect(transferErc1155AndENS.amount?.isEqualTo(new BigNumber(9))).to.be.true;
expect(transferErc1155AndENS.tokenId.isEqualTo(new BigNumber(99))).to.be.true;
expect(transferErc1155AndENS.receiverEnsName).to.equal("receiver3.eth");
});
Expand Down Expand Up @@ -424,19 +424,39 @@ describe("Parsing CSVs ", () => {
expect(warningErc721WithInvalidReceiver.message).to.equal("Invalid Receiver Address: 0xwhoopsie");
});

it("fallback to erc20 without token_type", async () => {
const missingTokenType = ["", listedToken.address, validReceiverAddress, "15"];
describe("Support backward compatibility", () => {
it("fallback to erc20 without token_type", async () => {
const missingTokenType = ["", listedToken.address, validReceiverAddress, "15"];

const [payment, warnings] = await CSVParser.parseCSV(
csvStringFromRows(missingTokenType),
mockTokenInfoProvider,
mockCollectibleTokenInfoProvider,
mockEnsResolver,
);
expect(warnings).to.be.empty;
expect(payment).to.have.length(1);
const [erc20Transfer] = payment as AssetTransfer[];
const [payment, warnings] = await CSVParser.parseCSV(
csvStringFromRows(missingTokenType),
mockTokenInfoProvider,
mockCollectibleTokenInfoProvider,
mockEnsResolver,
);
expect(warnings).to.be.empty;
expect(payment).to.have.length(1);
const [erc20Transfer] = payment as AssetTransfer[];

expect(erc20Transfer.token_type).to.equal("erc20");
});

it("allow value instead of amount column", async () => {
const nativeTransfer = ["native", listedToken.address, validReceiverAddress, "15"];
const headerRow = "token_type,token_address,receiver,value,id";
const csvString = [headerRow, nativeTransfer.join(",")].join("\n");

const [payment, warnings] = await CSVParser.parseCSV(
csvString,
mockTokenInfoProvider,
mockCollectibleTokenInfoProvider,
mockEnsResolver,
);
expect(warnings).to.be.empty;
expect(payment).to.have.length(1);
const [nativeTransferData] = payment as AssetTransfer[];

expect(erc20Transfer.token_type).to.equal("erc20");
expect(nativeTransferData.amount.isEqualTo(new BigNumber(15))).to.be.true;
});
});
});
6 changes: 3 additions & 3 deletions src/__tests__/transfers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ describe("Build Transfers:", () => {
});

describe("Mixed", () => {
it("works with arbitrary amount strings on listed, unlisted and native transfers", () => {
it("works with arbitrary value strings on listed, unlisted and native transfers", () => {
const mixedAmount = new BigNumber("123456.000000789");
const mixedPayments: AssetTransfer[] = [
// Listed ERC20
Expand Down Expand Up @@ -209,7 +209,7 @@ describe("Build Transfers:", () => {
const payment: AssetTransfer = {
token_type: "erc20",
receiver,
amount: amount,
amount,
tokenAddress: crappyToken.address,
decimals: crappyToken.decimals,
symbol: "BTC",
Expand Down Expand Up @@ -243,7 +243,7 @@ describe("Build Transfers:", () => {
receiverEnsName: null,
tokenAddress: testData.addresses.dummyErc1155Address,
tokenName: "Test MultiToken",
value: new BigNumber("69"),
amount: new BigNumber("69"),
tokenId: new BigNumber("420"),
hasMetaData: false,
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/CSVForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface CSVFormProps {

export const CSVForm = (props: CSVFormProps): JSX.Element => {
const { updateTransferTable, setParsing } = props;
const [csvText, setCsvText] = useState<string>("token_type,token_address,receiver,value,id");
const [csvText, setCsvText] = useState<string>("token_type,token_address,receiver,amount,id");

const { setCodeWarnings, setMessages } = useContext(MessageContext);

Expand Down Expand Up @@ -133,7 +133,7 @@ export const CSVForm = (props: CSVFormProps): JSX.Element => {
a CSV file in a single transaction.
</Text>
<Text size="lg">
Upload, edit or paste your asset transfer CSV <br /> (token_type,token_address,receiver,value,id)
Upload, edit or paste your asset transfer CSV <br /> (token_type,token_address,receiver,amount,id)
</Text>

<CSVEditor csvText={csvText} onChange={onChangeTextHandler} />
Expand Down
1 change: 0 additions & 1 deletion src/components/CSVUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const CSVUpload = (props: CSVUploadProps): JSX.Element => {
const onDrop = useCallback(
(acceptedFiles: File[]) => {
acceptedFiles.forEach((file) => {
console.log("Received Filename", file.name);
const reader = new FileReader();
reader.onload = function (evt) {
if (!evt.target) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/FAQModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const FAQModal: () => JSX.Element = () => {
</li>
<li>
<code>
<b>value</b>
<b>amount</b>
</code>
: the amount of token to be transferred. This can be left blank for erc721 transfers.
</li>
Expand Down
4 changes: 2 additions & 2 deletions src/components/GenerateTransfersMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ export const GenerateTransfersMenu = (props: GenerateTransfersMenuProps): JSX.El
const error = drainAddress ? invalidNetworkError || invalidAddressError : "";

const generateDrainTransfers = () => {
let drainCSV = "token_type,token_address,receiver,value,id,";
let drainCSV = "token_type,token_address,receiver,amount,id,";
if (drainAddress) {
assetBalance?.forEach((asset) => {
if (asset.token === null && asset.tokenAddress === null) {
const decimalBalance = fromWei(new BigNumber(asset.balance), 18);
// The API returns zero balances for the native token.
if (!decimalBalance.isZero) {
if (!decimalBalance.isZero()) {
drainCSV += `\nnative,,${drainAddress},${decimalBalance},`;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/components/assets/CollectiblesTransferTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const Row = memo((props: RowProps) => {
</div>
<Receiver receiverAddress={row.receiver} receiverEnsName={row.receiverEnsName} />
<div style={{ flex: "1", padding: 16, minWidth: 80 }}>
<Text size="md">{row.value?.toFixed()}</Text>
<Text size="md">{row.amount?.toFixed()}</Text>
</div>
<div style={{ flex: "1", padding: 16, minWidth: 80 }}>
<Text size="md">{row.tokenId.toFixed()}</Text>
Expand Down
1 change: 0 additions & 1 deletion src/contexts/MessageContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export const MessageContextProvider = (props: MessageContextProviderProps) => {
const [showMessages, setShowMessages] = useState(false);

const removeMessage = (messageToRemove: Message | CodeWarning) => {
console.log("Removing Message");
setMessages(messages.filter((message) => message.message !== messageToRemove.message));
};

Expand Down
4 changes: 3 additions & 1 deletion src/hooks/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export type TokenMap = Map<string | null, MinimalTokenInfo>;
function tokenMap(tokenList: TokenInfo[]): TokenMap {
const res: TokenMap = new Map<string, MinimalTokenInfo>();
for (const token of tokenList) {
res.set(utils.getAddress(token.address), token);
if (token.address) {
res.set(utils.getAddress(token.address), token);
}
}
return res;
}
Expand Down
3 changes: 2 additions & 1 deletion src/parser/csvParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface CollectibleTransfer {
tokenAddress: string;
tokenName?: string;
tokenId: BigNumber;
value?: BigNumber;
amount?: BigNumber;
receiverEnsName: string | null;
hasMetaData: boolean;
}
Expand All @@ -50,6 +50,7 @@ export type CSVRow = {
token_address: string;
receiver: string;
value?: string;
amount?: string;
id?: string;
};

Expand Down
8 changes: 4 additions & 4 deletions src/parser/transformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface PreCollectibleTransfer {
tokenId: BigNumber;
tokenAddress: string;
tokenType: "nft";
value?: BigNumber;
amount?: BigNumber;
}

export const transform = (
Expand Down Expand Up @@ -83,7 +83,7 @@ export const transformAsset = (
const prePayment: PrePayment = {
// avoids errors from getAddress. Invalid addresses are later caught in validateRow
tokenAddress: transformERC20TokenAddress(row.token_address),
amount: new BigNumber(row.value ?? ""),
amount: new BigNumber(row.amount ?? row.value ?? ""),
receiver: normalizeAddress(trimMatchingNetwork(row.receiver, selectedChainShortname)),
tokenType: row.token_type,
};
Expand Down Expand Up @@ -161,7 +161,7 @@ export const transformCollectible = (
tokenId: new BigNumber(row.id ?? ""),
receiver: normalizeAddress(row.receiver),
tokenType: row.token_type,
value: new BigNumber(row.value ?? ""),
amount: new BigNumber(row.amount ?? ""),
};

toCollectibleTransfer(prePayment, erc721InfoProvider, ensResolver)
Expand Down Expand Up @@ -206,7 +206,7 @@ const toCollectibleTransfer = async (
tokenId: preCollectible.tokenId,
tokenAddress: preCollectible.tokenAddress,
receiverEnsName,
value: preCollectible.value,
amount: preCollectible.amount,
token_type: "erc1155",
hasMetaData: tokenInfo.hasMetaInfo,
};
Expand Down
10 changes: 5 additions & 5 deletions src/parser/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const areAddressesValid = (row: Transfer): string[] => {
};

const isAmountPositive = (row: AssetTransfer): string[] =>
row.amount.isGreaterThan(0) ? [] : ["Only positive amounts possible: " + row.amount.toFixed()];
row.amount.isGreaterThan(0) ? [] : ["Only positive amounts/values possible: " + row.amount.toFixed()];

const isAssetTokenValid = (row: AssetTransfer): string[] =>
row.decimals === -1 && row.symbol === "TOKEN_NOT_FOUND" ? [`No token contract was found at ${row.tokenAddress}`] : [];
Expand All @@ -69,11 +69,11 @@ const isTokenIdInteger = (row: CollectibleTransfer): string[] =>
row.tokenId.isInteger() ? [] : [`Token IDs must be integer numbers: ${row.tokenId.toFixed()}`];

const isTokenValueInteger = (row: CollectibleTransfer): string[] =>
!row.value || row.value.isNaN() || row.value.isInteger()
!row.amount || row.amount.isNaN() || row.amount.isInteger()
? []
: [`Value of ERC1155 must be an integer: ${row.value.toFixed()}`];
: [`Value of ERC1155 must be an integer: ${row.amount.toFixed()}`];

const isTokenValueValid = (row: CollectibleTransfer): string[] =>
row.token_type === "erc721" || (typeof row.value !== "undefined" && row.value.isGreaterThan(0))
row.token_type === "erc721" || (typeof row.amount !== "undefined" && row.amount.isGreaterThan(0))
? []
: [`ERC1155 Tokens need a defined value > 0: ${row.value?.toFixed()}`];
: [`ERC1155 Tokens need a defined value > 0: ${row.amount?.toFixed()}`];
6 changes: 3 additions & 3 deletions src/transfers/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ export function buildAssetTransfers(transferData: AssetTransfer[]): BaseTransact
} else {
// ERC20 transfer
const decimals = transfer.decimals;
const amountData = toWei(transfer.amount, decimals);
const valueData = toWei(transfer.amount, decimals);
return {
to: transfer.tokenAddress,
value: "0",
data: erc20Interface.encodeFunctionData("transfer", [transfer.receiver, amountData.toFixed()]),
data: erc20Interface.encodeFunctionData("transfer", [transfer.receiver, valueData.toFixed()]),
};
}
});
Expand All @@ -51,7 +51,7 @@ export function buildCollectibleTransfers(transferData: CollectibleTransfer[]):
transfer.from,
transfer.receiver,
transfer.tokenId.toFixed(),
transfer.value?.toFixed() ?? "0",
transfer.amount?.toFixed() ?? "0",
ethers.utils.hexlify("0x00"),
]),
};
Expand Down

0 comments on commit 848bc3b

Please sign in to comment.