-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
154 lines (123 loc) · 5.42 KB
/
Copy pathapp.js
File metadata and controls
154 lines (123 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Contract Configuration
const BATCH_PAYMENT_ADDRESS = "0xc1AD5414f3dE089F47A00736Bf5990cAC7aC05e5";
const ERC20_ABI = [
"function decimals() view returns (uint8)",
"function approve(address spender, uint256 amount) public returns (bool)",
"function allowance(address owner, address spender) public view returns (uint256)",
"function balanceOf(address account) view returns (uint256)"
];
const BATCH_ABI = [
"function batchTransfer(address tokenAddress, address[] recipients, uint256[] amounts) external",
"function fee() view returns (uint256)"
];
// State
let provider, signer, userAddress;
let batchContract;
// DOM Elements
const connectBtn = document.getElementById('connectBtn');
const tokenAddressInput = document.getElementById('tokenAddress');
const csvInput = document.getElementById('csvInput');
const totalTokensDisplay = document.getElementById('totalTokens');
const approveBtn = document.getElementById('approveBtn');
const payBtn = document.getElementById('payBtn');
const statusDiv = document.getElementById('status');
// Helper: Log Status
function setStatus(msg, type = 'info') {
statusDiv.innerText = msg;
statusDiv.style.borderLeftColor = type === 'error' ? '#ef4444' : '#6366f1';
console.log(`[${type.toUpperCase()}] ${msg}`);
}
// 1. Connect Wallet
connectBtn.addEventListener('click', async () => {
if (!window.ethereum) return setStatus("Please install MetaMask!", 'error');
try {
provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
signer = provider.getSigner();
userAddress = await signer.getAddress();
// Update UI
connectBtn.innerText = `${userAddress.substring(0,6)}...${userAddress.substring(38)}`;
connectBtn.classList.add('connected');
approveBtn.disabled = false;
// Init Contract
batchContract = new ethers.Contract(BATCH_PAYMENT_ADDRESS, BATCH_ABI, signer);
setStatus("Wallet connected. Ready to configure.");
} catch (err) {
setStatus("Connection failed: " + err.message, 'error');
}
});
// 2. Parse Input & Calculate Total
function parseData() {
const raw = csvInput.value.trim();
if (!raw) return { recipients: [], amounts: [], totalStr: "0" };
const lines = raw.split('\n');
const recipients = [];
const amounts = [];
let total = ethers.BigNumber.from(0);
// We assume 18 decimals by default for the preview, real decimals fetched later
lines.forEach(line => {
const parts = line.split(',');
if (parts.length >= 2) {
const addr = parts[0].trim();
const val = parts[1].trim();
if (ethers.utils.isAddress(addr) && !isNaN(val)) {
recipients.push(addr);
amounts.push(val); // Keep as string first
}
}
});
return { recipients, amounts };
}
// Update Total Preview
csvInput.addEventListener('input', () => {
const { amounts } = parseData();
const sum = amounts.reduce((a, b) => a + parseFloat(b || 0), 0);
totalTokensDisplay.innerText = sum;
});
// 3. Approve Tokens
approveBtn.addEventListener('click', async () => {
const tokenAddr = tokenAddressInput.value.trim();
if (!ethers.utils.isAddress(tokenAddr)) return setStatus("Invalid Token Address", 'error');
const { recipients, amounts } = parseData();
if (recipients.length === 0) return setStatus("No valid recipients", 'error');
try {
setStatus("Checking Token...");
const tokenContract = new ethers.Contract(tokenAddr, ERC20_ABI, signer);
const decimals = await tokenContract.decimals();
// Calculate Total Wei
let totalWei = ethers.BigNumber.from(0);
const amountsWei = amounts.map(a => {
const val = ethers.utils.parseUnits(a, decimals);
totalWei = totalWei.add(val);
return val;
});
// Get Fee
const fee = await batchContract.fee();
const totalRequired = totalWei.add(fee);
setStatus(`Approving ${ethers.utils.formatUnits(totalRequired, decimals)} tokens...`);
const tx = await tokenContract.approve(BATCH_PAYMENT_ADDRESS, totalRequired);
setStatus("Approving... Waiting for confirmation");
await tx.wait();
setStatus("Approved! You can now Batch Pay.");
payBtn.disabled = false;
// Store parsed data for next step to avoid re-parsing
window.parsedBatchData = { recipients, amountsWei, tokenAddr };
} catch (err) {
setStatus("Approval failed: " + err.message, 'error');
}
});
// 4. Batch Pay
payBtn.addEventListener('click', async () => {
if (!window.parsedBatchData) return setStatus("Please Approve first", 'error');
const { tokenAddr, recipients, amountsWei } = window.parsedBatchData;
try {
setStatus("Sending transaction...");
const tx = await batchContract.batchTransfer(tokenAddr, recipients, amountsWei);
setStatus(`Transaction sent: ${tx.hash}. Waiting...`);
await tx.wait();
setStatus("Batch Payment Successful! 🚀");
payBtn.disabled = true;
} catch (err) {
setStatus("Payment failed: " + err.message, 'error');
}
});