Solving DApps problem with Merkle drop

Dec 27th, 2022
8 min read

There are now additional Blockchain use cases due to the proliferation of DApps or decentralized applications, such as DeFi, NFT, or GameFi. According to its use case, each approach has advantages and disadvantages, so anyone considering creating DApps should be aware of the following:

  • Transparency: The system is transparent and can be checked by anyone
  • Privacy: Because every piece of data on the blockchain is transparent and available to everyone, we may need to consider how sensitive the user’s data is. Therefore, there is a tradeoff between transparency and privacy.
  • Security: Because it is about money and transparency. Making them vulnerable to various attacks
  • Transaction Fee: High transaction costs. The value is relatively high in a blockchain transaction like an Ethereum transaction. Besides, storing blockchain data also requires a lot of money to save.

Don’t worry if you’ve read this and haven’t heard of Web 3.0 or Blockchain. I advise you to read this blog first to understand what Web 3.0 is and how significant it is in the modern world.

Welcome to the world of Web 3.0

Web 3.0, dubbed the next state for the internet, is being talked about more and more as the days go by. So what is it exactly?

So, what challenges do we encounter in the field of blockchain technology, and how do we overcome them? What factors ought we consider?

Simple Problems, Simple Solutions

Give a specific problem as an example. Let’s say you start as a DApp team in Ethereum and later decide to hire a group of 7–10 additional members.Suppose ETH was worth $100 at the time, and the average wage for a team member was $4000. You send 40 ETH to each team member at the end of the month. Gas might cost $1 for each person. The choice of people is not at all wrong.

Gas cost = ~$8
Time cost = ~30 minutes

Time flies, and the answer has been altered.

Once your DApp is widely used, your team expands to 100 individuals earning a range of incomes. Additionally, the gas expense rises to $20 for each transaction. You consult the programming team because paying the team members who must send money one at a time is starting to fail. Our staff will improve the functionality of the tools used to manage your money transfer.

After a while, the team’s developers began to come up with several suggestions. There are 3 intriguing concepts.

1. Multi-Transfer Smart Contract


It is a technique for transferring coins to several destinations at once by making a call to a smart contract with the specified money and destination.

"0xEcA5035aAcBe8cfcdE45348Ad4bF0659a2da2Be5", 1000 "0xD1Cd56B0212dA4F653c66cBDaa0cBfd7Bac0A305", 500 "0x67C48F490b4bB87D93466Ff6aB4a46C851E0c040", 1500 "0xe379DC0CaE3FACA163C0CF5563F9e6A76bC3045c", 1000 "0xf12CF93685EFbcD890C921918F4b1BA479eE8f9a", 1000 "0x48B23fa9aC54a8574C4568A3F6347fB82E53DF9e", 1000 "0x169C198de1D0385AAC19A10fa29d3b00C154e9bB", 1000 "0x856bf248DF77f1FFf8125F7bbD42eF282135A8e9", 1000 "0x933aA63bdE10435479cAcE74D56e4F9d2363EFc2", 1000 "0xF2c28A054B76c69de9Fa2522903452f652eeA357", 1000 ...

Calculate Transaction Fee (Gas cost)

If receiver = 1000
Sender calls multi-transfer for transferring money to receivers
= ~$2/receiver = $2000
Gas cost = ~$2000


So this idea has some points that we need to think about it

  • All funds must be kept in the sender’s wallet prior to the transfer.
  • Transferring using a smart contract requires your consent. Allowance is the maximum amount a smart contract may take from your account. Therefore, if the contract is changed or you approve the incorrect one, it might result in you losing all of your money.


  • Simple and fast to implement
  • This approach is easy for sender and receiver to use.


  • Because the transaction charge grows according to the Receiver amount, it cannot be scaled.

2. Vault Smart Contract


To store all of the money, we developed a smart contract called “Vault.” Then the sender configures the Vault such that those who are eligible to claim funds can do so and in what amounts.


  1. Transfer funds from the sender or source for storage in the Vault smart contract.
  2. To specify which addresses and how much they can claim from the vault, the sender sends the configuration to the vault.
  3. To claim the money, the receiver calls the claimed method on the smart contract.

Calculate Transaction Fee (Gas cost)

If receiver = 1000
Sender set the configuration in Vault = ~$1/receiver = $1000
Receivers claim on smart contract = ~$1
Gas cost = ~$1001

If you see that the cost to the sender is lower but the entire cost of gas, which includes all of the money paid by the recipient, remains the same,


If the sender's private key is lost, the attacker will be able to alter the settings and steal all of the money.


  • The cost of sender gas is greatly lowered
  • The responsibility to keep an eye out for money transfers from the sender to the smart contract


  • Now the receiver has to pay a small gas cost
  • Setting setup consumes the greatest gas and continues to do so based on the receiver amount

3. Vault Smart Contract with Signature


Similar in idea to the second, however, this proposal modifies the smart contract’s set up to store the signature off-chain. If the sender signs the signature, which is a hash of the recipient’s address and the amount of the claim, the smart contract will permit the recipient to withdraw the money.


  1. Transfer funds from the sender or source for storage in the Vault smart contract.
  2. The sender creates a signature from the data which is the hashed of the receiver address and claim amount.
  3. delivering the signature in some way to the recipient. It may be sent by API or messaging.
  4. The recipient uses their signature to get their smart contract money.

Calculate Transaction Fee (Gas cost)

If receiver = 1000
Sender set the owner signature address = ~$1
Receivers claim on smart contract = ~$1
Gas cost = ~$2

As you can see, this method has a minimal gas cost as the store configuration data is no longer on the blockchain, but it still requires the recipient to complete a transaction in order to collect their money.


  • The second concept still has the same security flaw in that if the sender's private key is lost, all of the money might be lost.
  • A user cannot utilize a smart contract to collect money if the server that offers a signature is unavailable.


  • Compared to all solutions, the cost of gas is the lowest.
  • If sender and recipient numbers expand in the future, scaling will be easiest.


  • The need to deploy or set up a location where the recipient can visit to obtain a signature may result in an extra expense.

Making a decision

Each of the three solutions may be used to handle the problem of transferring money once we have discussed their advantages and disadvantages, but there are certain trade-offs between them.

  • Transaction Fee (Gas cost)
  • Security
  • How simple it is to implement and use

Choose the Easiest or the Most Difficult

Deciding a direction to take is never easy. I thus have an idea for you. If you need something quick and inexpensive, pick the easy option; if you want something durable and maintenance, pick the toughest. Since you may immediately reject the simple answer.

The third idea, “Vault Smart Contract with Signature”, is what our team chose as the solution. Then this blog’s subsequent part will outline this method in more depth.

Merkle Proof and ECDSA Signature

ECDSA Signature

Elliptic Curve Digital Signature Algorithm is the full name of the algorithm. In order to produce Private Key and Public Key for making a transaction in the blockchain, cryptography is thus made using an algorithm. We will thus demonstrate how to utilize ECDSA in our approach.


Data + Private Key = Signature

Using the previous formula Data and a private key are combined to generate a signature. This is a “sign signature” action.


Data + Signature = Public Key

On the other side, we can recover it to obtain the public key if we have the data and signature.


The idea we selected is implemented in a code following this section.

Step1: Sender must sign a message that includes the recipient’s address and a claim for payment.
Step2: Sender must deliver a signature from the previous stage to the recipient. It may also be transmitted over a message service or API.
Step3: Receiver brings the signature to claim money from a smart contract that implements a method that using to verify the signature.
1import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
3contract SalaryVault {
4    function claimMoney(
5        uint256 walletAddress,
6        uint256 claimAmount,
7        bytes memory signature
8    ) public returns (uint256) {
9        require(
10            _verify(_hash(walletAddress, claimAmount), signature),
11            'Invalid signature'
12        );
13        IERC20(USDT).safeTransfer(walletAddress, claimAmount);
15        markClaim(walletAddress,signature);
16        // Indicate that the signature was used to avoid repeated claims using the same signature.
18        return claimAmount;
19    }
21    function _verify(bytes32 digest, bytes memory signature)
22        internal
23        view
24        returns (bool)
25    {
26        address signerAddress = ECDSA.recover(digest, signature);
27        return ownerAddress == signerAddress;
28    }
30    function _hash(uint256 walletAddress, uint256 claimAmount)
31        internal
32        view
33        returns (bytes32)
34    {
35        return
36            _hashTypedDataV4(
37                keccak256(
38                    abi.encode(
39                        keccak256(
40                            'cheque(address walletAddress,uint256 claimAmount)'
41                        ),
42                        walletAddress,
43                        claimAmount
44                    )
45                )
46            );
47    }

Finally, you can implement a smart contract that allows the sender to deposit money and wait for the receiver to come to pick them up later. However, the approach in the next part is improved.

Merkle Proof

Merkle Proof is a list of hashes. It serves as proof that the leaf is a component of the Merkle tree.

Merkle Tree

Hash tree or the Merkle tree It is a data structure in the form of a tree, with each node being a hash of a data block and each parent being a hash of their child. The Merkle root was the highest parent node.


So by this feature of Merkle Proof, we can use this to improve verify method

How to use Merkle Proof

For instance, suppose the whitelisted address hashing tree is the Merkle Tree. You are therefore on the whitelist; how can we ensure that you are valid?

What do you need to prove?

  • The Leaf. In this case, your leaf represents your address and amount.
  • Neighbour nodes in the route to the Merkle root that hashes with your leaf
1const { MerkleTree } = require(‘merkletreejs’);
2const keccak256 = require(‘keccak256’);
4const yourAddress = "0xEcA5035aAcBe8cfcdE45348Ad4bF0659a2da2Be5"
6// hash 0xEcA5035aAcBe8cfcdE45348Ad4bF0659a2da2Be5,1000 -> 57d3f90cd1a10c1aef739e6a57fc399eb22734fec7b2a179ab09f45c16e073bb
7const hash = (address, amount) => keccak256(`${address},${amount}`)
9const users = [
10    { address: yourAddress, amount: 1000 },
11    { address: "0xD1Cd56B0212dA4F653c66cBDaa0cBfd7Bac0A305", amount: 1500 },
12    { address: "0x67C48F490b4bB87D93466Ff6aB4a46C851E0c040", amount: 500 },
13    { address: "0xe379DC0CaE3FACA163C0CF5563F9e6A76bC3045c", amount: 1000 },
14    { address: "0xf12CF93685EFbcD890C921918F4b1BA479eE8f9a", amount: 1000 },
15    { address: "0x48B23fa9aC54a8574C4568A3F6347fB82E53DF9e", amount: 1000 },
16    { address: "0x169C198de1D0385AAC19A10fa29d3b00C154e9bB", amount: 1000 },
17    { address: "0x856bf248DF77f1FFf8125F7bbD42eF282135A8e9", amount: 1000 },
18    { address: "0x933aA63bdE10435479cAcE74D56e4F9d2363EFc2", amount: 1000 },
19    { address: "0xF2c28A054B76c69de9Fa2522903452f652eeA357", amount: 1000 },
22const leafs = => hash(user.address, user.amount));
24const merkleTree = new MerkleTree(leafs, keccak256, { sortPairs: true });
26const yourLeaf = hash(yourAddress, 1000) // 57d3f90cd1a10c1aef739e6a57fc399eb22734fec7b2a179ab09f45c16e073bb
28const proofs = merkleTree.getHexProof(yourLeaf); 
29// ["9345f8168675ebe882ba58cfaa791d4cad65a07a18e58e93191c1e3813facf84","0xEcA5035aAcBe8cfcdE45348Ad4bF0659a2da2Be5,1500",...]

Then What?

In order to verify that the user is a part of the Merkle Tree, you must now save the root and add logic to your vault smart contract code.

1import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
2import '@openzeppelin/contracts/utils/Strings.sol';
4contract SalaryVault {
5    bytes32 merkleRoot;
7    function setRoot(bytes32 _merkleRoot) public {
8        merkleRoot = _merkleRoot;
9    }
11    function claimMoney(
12        uint256 walletAddress,
13        uint256 claimAmount,
14        bytes32[] memory proof
15    ) public returns (uint256) {
16        bytes32 userLeaf = getLeaf(walletAddress, claimAmount);
18        require(_verify(merkleRoot, userLeaf, proof), 'Invalid signature');
20        IERC20(USDT).safeTransfer(walletAddress, claimAmount);
22        markClaim(walletAddress, claimAmount);
23        // Indicate that the signature was used to avoid repeated claims using the same signature.
25        return claimAmount;
26    }
28    function getLeaf(address userAddress, uint256 amount)
29        internal
30        returns (bytes32)
31    {
32        return
33            keccak256(abi.encodePacked(userAddress) + Strings.toString(amount));
34    }
36    function _verify(
37        bytes32 root,
38        bytes32 leaf,
39        bytes32[] memory proof
40    ) internal view returns (bool) {
41        return MerkleProof.verify(proof, root, leaf);
42    }


After a lengthy reading, I’ll summarize what you’ve read in bullet points.

  • Characteristics of DApp and Blockchain
  • The simple way to transfer money in Blockchain
  • Identify a solution to the scale problem.
  • How to implement Merkle Proof and ECDSA in Solidity smart contract

Thank you for reading, see you later in the next article!

author's profile image

Senior Software Engineer