I want to create something like a Crypto exchange, in which I need to generate different unique deposit addresses for each user, so they can send funds to their accounts while I can keep track of how much each user deposited. This is fairly easy in other cryptos such as bitcoin. However, it seems this is impossible in Ethereum (or maybe I am wrong).

The main problem here is that if I generate multiple private keys, or use an HD wallet and drive multiple private keys from it, funds sent to eath of these wallets need to be signed with different keys in order to be spent thus I have to pay a huge amount of transaction fee (once per account) if I try to send all the funds to one big wallet for maintenance.

So I was wondering if I can generate multiple deposit addresses all corresponding to one account in Ethereum so each user can send funds to a different address therefore, I can track which user sent how much and when I want to spend these funds, I can sign the transaction with one private key and pay transaction fee only once (spend all the ethers together)? And if this is totally impossible to have multiple deposit addresses corresponding to one account, then what can I do?

PS: I have seen several questions like this one in this StackExchange but none of the answers actually answered my question. So please hesitate before marking this as a duplicate

PS2: I am not looking for a solution using smart contracts, because that would require my users to do depositing using special wallets that support web3 like metamask, I am more interested to know how other exchanges such as Binance handle this, so users can deposite from any kind of wallet!!!!


1 Answer 1


You're trying to apply a model that is appropriate for Bitcoin mainly due to its limitations. It will be an awkward strategy for Ethereum that overlooks it's strengths.

Bitcoin: Not especially programmable, but easy to sweep funds from multiple accounts. Ethereum: Especially programmable, but not so easy to sweep funds from multiple accounts.

Ethereum also has unique security considerations and it's important to confirm that your client has all the capabilities your contract assumes they have, such as the ability to form a transaction and sign a withdrawal request. That is NOT the case if they are sending from another exchange, so you need to prevent it. You probably need registration/KYC in any case, so confirm they have full control of the accounts they will use, and then inform the contract they are "approved" to send money.

  1. You can use a single deposit account - all money together, so no need to sweep.
  2. You can know who sent it and emit events, record it etc. - all accounting requirements.
  3. It is programmable - whatever happens next.

You should confirm that your users can sign a transaction. This will ensure they can work with any contract interface you show them in the future.

  1. Have them sign a message and send it to you, off-chain (free). Confirm their address with ecrecover.
  2. Now you know who is who. Not from where the money arrived. By where it came from. No reason a user can't confirm multiple accounts (step 1) but it is unwise to accept funds from unknown senders.

For brevity, I will skip over withdrawal functionality because you will probably transfer the funds to operating accounts as soon as they are received and you might have a completely different contract where you send funds to be withdrawn - or send to the user based on a web site interaction.

This is not presented as a battle-ready production contract, but more like an simple example to show the approach.

pragma solidity 0.5.16;

contract Deposit {

    address payable public wallet;

    mapping(address => bool) public approved;

    event LogApproved(address approver, address approved);
    event LogDeposit(address sender, uint amount);

    function authorize(address allowed) public {
        require(msg.sender == wallet, "Only the owner can do this, e.g. from their admin server.");
        emit LogApproved(msg.sender, allowed);
        approved[allowed] = true;

    function deposit() public payable {
        require(approved[msg.sender], "Unauthorized sender. Please register.");
        emit LogDeposit(msg.sender, msg.value);  // <=== there's your accounting

The accounting concern is immortalized on the blockchain and is inspectable by an external client, e.g. nodejs. The money goes to your wallet account and you can carry on from there.

Hope it helps.

  • Thank you for your detailed answer. although this seems a good approach, I believe crypto exchanges such as Binance don't use this approach. since sending ether to Binance happens like sending BTC to it. So, actually I was wondering how they solve this problem for ether.
    – F_IVI_L
    Commented Feb 1, 2020 at 22:45
  • 1
    You can certainly do it by 1) generate an account, 2) watch for money and 3) send the money forward. The server will have the signing keys for accounts it creates. The server will send from "my account", balance of account and pay for gas. Commented Feb 2, 2020 at 0:00
  • @RobHitchens. The approach you described last suits my needs. However there is an issue I see, forwarding costs gas. It works, when ETH is sent to deposit addresses. What if ERC20 token is sent and deposit address doesn't have any ETH to pay with gas for forwarding?
    – Sabine
    Commented May 18, 2021 at 22:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.