Skip to content

Commit

Permalink
Merge branch 'dev' into add-hover-effect-for-admin-links
Browse files Browse the repository at this point in the history
  • Loading branch information
SeanM322 committed May 13, 2024
2 parents 09a05f9 + 9dc8bce commit 2b3a8c9
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 12 deletions.
230 changes: 230 additions & 0 deletions rair-front/src/components/adminViews/BatchERC20Transfer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { useCallback, useState } from 'react';
import Dropzone from 'react-dropzone';
import { useSelector } from 'react-redux';
import { parseUnits } from 'ethers/lib/utils';

import { RootState } from '../../ducks';
import { ColorStoreType } from '../../ducks/colors/colorStore.types';
import { ContractsInitialType } from '../../ducks/contracts/contracts.types';
import useSwal from '../../hooks/useSwal';
import useWeb3Tx from '../../hooks/useWeb3Tx';
import csvParser from '../../utils/csvParser';
import InputField from '../common/InputField';

const structure = [
{
label: 'Name',
defaultValue: '',
disabled: false,
type: 'text'
},
{
label: 'Address',
defaultValue: '',
disabled: false,
type: 'text'
},
{
label: 'Amount',
defaultValue: 0,
disabled: false,
type: 'number'
},
{
label: 'Status',
defaultValue: 'Ready',
disabled: true,
type: 'text'
},
{
label: 'TxHash',
defaultValue: '',
disabled: true,
type: 'text'
}
];

type RowData = {
[key: string]: string;
};

const BatchERC20Transfer = () => {
const [data, setData] = useState<RowData[]>([]);
const { textColor, primaryColor, primaryButtonColor, secondaryButtonColor } =
useSelector<RootState, ColorStoreType>((store) => store.colorStore);
const { erc777Instance } = useSelector<RootState, ContractsInitialType>(
(store) => store.contractStore
);
const { web3TxHandler } = useWeb3Tx();
const reactSwal = useSwal();
const updateRowData = useCallback(
(key: string, index: number, value: string) => {
const aux = [...data];
if (aux[index]) {
aux[index][key] = value;
}
setData(aux);
},
[data]
);

const addRow = useCallback(() => {
const aux = [...data];
const newData = {};
structure.forEach((data) => {
newData[data.label] = data.defaultValue;
});
aux.push(newData);
setData(aux);
}, [data]);

const onCSVDrop = useCallback((file: File[]) => {
csvParser(file[0], (data) => {
setData(
data.map((item) => {
const { Name, Address, Amount } = item;
return { Name, Address, Amount, Status: 'Ready', TxHash: '' };
})
);
});
}, []);

const transferProcess = useCallback(async () => {
if (!erc777Instance) {
reactSwal.fire('No ERC20 connected');
return;
}
const aux = [...data];
for await (const dataItem of aux) {
if (dataItem.Status !== 'Ready' || !dataItem.Amount) {
continue;
}
reactSwal.fire({
icon: 'info',
title: 'Preparing transaction',
html: `Sending ${dataItem.Amount} tokens to ${dataItem.Name} (${dataItem.Address})`,
showConfirmButton: false
});
const txHash = await web3TxHandler(erc777Instance, 'transfer', [
dataItem.Address,
parseUnits(dataItem.Amount, 18)
]);
if (txHash) {
dataItem.TxHash = txHash;
dataItem.Status = 'Complete';
await reactSwal.fire({
icon: 'success',
title: 'Transaction complete',
html: `Transaction id: ${txHash}`
});
} else {
await reactSwal.fire({
icon: 'error',
title: 'An error has ocurred',
html: 'The process will stop'
});
break;
}
}
setData(aux);
}, [erc777Instance, data, web3TxHandler, reactSwal]);

const deleteRow = useCallback(
(index: number) => {
const aux = [...data];
aux.splice(index, 1);
setData(aux);
},
[data]
);

return (
<div className="w-100 row">
<h5>ERC20 tool</h5>
<Dropzone onDrop={onCSVDrop}>
{({ getRootProps, getInputProps, isDragActive }) => (
<section>
<div
{...getRootProps()}
style={{
border: `dashed 1px color-mix(in srgb, ${primaryColor}, #888888)`,
position: 'relative'
}}
className="w-100 h-100 rounded-rair col-6 text-center mb-3 p-3">
<input {...getInputProps()} />
<br />
{isDragActive ? (
<>Drop the CSV file here ...</>
) : (
<>Drag and drop or click to upload the CSV file</>
)}
</div>
</section>
)}
</Dropzone>
<table className="table table-dark table-responsive">
<thead>
<tr>
<th>
<button
onClick={addRow}
className="rair-button btn"
style={{
color: textColor,
background: secondaryButtonColor
}}>
<i className="fa fa-plus" />
</button>
</th>
{structure.map((data, index) => {
return <th key={index}>{data.label}</th>;
})}
</tr>
</thead>
<tbody>
{data.map((data, index) => {
return (
<tr key={index}>
<td>
<button
onClick={() => deleteRow(index)}
className="btn btn-danger">
<i className="fa fa-trash" />
</button>
</td>
{Object.keys(data).map((key, keyIndex) => {
console.info(key, keyIndex, structure[keyIndex]);
return (
<th key={keyIndex}>
<InputField
disabled={structure[keyIndex]?.disabled}
customClass="form-control w-100"
type={structure[keyIndex].type}
getter={data[key]}
setter={(value) => updateRowData(key, index, value)}
/>
</th>
);
})}
</tr>
);
})}
</tbody>
</table>
<div className="row w-100">
<div className="col-12 col-md-9" />
<button
onClick={transferProcess}
className="rair-button btn col-12 col-md-3"
style={{
color: textColor,
background: primaryButtonColor
}}>
Start transfers
</button>
</div>
</div>
);
};

export default BatchERC20Transfer;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import axios from 'axios';
import { utils } from 'ethers';
import { isAddress } from 'ethers/lib/utils';

import BatchERC20Transfer from './BatchERC20Transfer';

import { diamondFactoryAbi } from '../../contracts';
import { RootState } from '../../ducks';
import { ColorStoreType } from '../../ducks/colors/colorStore.types';
Expand Down Expand Up @@ -301,6 +303,7 @@ const ImportExternalContract = () => {
})}
</tbody>
</table>
<BatchERC20Transfer />
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion rair-front/src/hooks/useWeb3Tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const useWeb3Tx = () => {
if (transactionReceipt && transactionReceipt.blockNumber) {
handleReceipt(transactionReceipt.transactionHash, options?.callback);
}
return true;
return transactionReceipt.transactionHash;
}
return paramsValidation;
},
Expand Down
24 changes: 14 additions & 10 deletions rair-node/bin/api/auth/auth.Service.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,20 @@ module.exports = {
return next(new AppError('User not found.', 404));
}

// Check OFAC blocklist
// Read the file content
const content = fs.readFileSync(
'./bin/integrations/ofac/sanctioned_addresses_ETH.json',
'utf8',
);
const ofacBlocklist = JSON.parse(content).map((address) => address.toLowerCase());
if (ofacBlocklist.includes(ethAddress)) {
await User.findByIdAndUpdate(userData._id, { $set: { blocked: true } });
userData.blocked = true;
try {
// Check OFAC blocklist
// Read the file content
const content = fs.readFileSync(
'./bin/integrations/ofac/sanctioned_addresses_ETH.json',
'utf8',
);
const ofacBlocklist = JSON.parse(content).map((address) => address.toLowerCase());
if (ofacBlocklist.includes(ethAddress)) {
await User.findByIdAndUpdate(userData._id, { $set: { blocked: true } });
userData.blocked = true;
}
} catch (error) {
log.error("Cannot read OFAC list");
}
if (userData.blocked) {
log.error(`Blocked user tried to login: ${ethAddress}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import json

FEATURE_TYPE_TEXT = "Digital Currency Address - "
NAMESPACE = {'sdn': 'http://www.un.org/sanctions/1.0'}
NAMESPACE = {'sdn': 'https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/ADVANCED_XML'}

# List of assets that have been sanctioned by the OFAC.
# Possible assets be seen by grepping the sdn_advanced.xml file for "Digital Currency Address".
Expand Down

0 comments on commit 2b3a8c9

Please sign in to comment.