Implementing Cryptocurrency Payments with Ethereum
Blockchain technology is developing rapidly fast. Over the past years, we have noticed a growing interest in blockchain. Companies are looking more and more favorably at the distributed ledger and trying to incorporate blockchain and cryptocurrency into business.
This article will analyze different approaches for handling cryptocurrency payments with Ether and examine available solutions in terms of costs.
Before we start, let’s define the essential criteria our solution has to meet.
● Firstly, we have to be able to match incoming payments with placed orders. It’s seemingly apparent; however, payment transactions will be executed directly on the blockchain, and by default, the transaction doesn’t carry any additional information such as, e.g., order number.
● Secondly, we will try to minimize the maintenance and withdrawal costs for merchants and payment fees for users.
Now, let’s have a quick look at the account-based model used in the Ethereum blockchain. The model represents assets as balances within accounts, similar to bank accounts. Every account is defined by a pair of keys, a private key, and a public key. Accounts are indexed on the blockchain by their address derived from the public key. When a user creates an Ether wallet and receives transactions, the address is saved in the blockchain and broadcasted to all network nodes.
A transaction in the account-based model triggers nodes to decrement the balance of the sender’s address and increment the balance of the receiver’s address. The sender’s balance is reduced by transaction amount and fee, which the sender has to pay to the miners to calculate the new block.
Let’s have a look at how a simplified payment process could look:
- A user places the order on the merchant’s website
- The payment service generates the payment address and presents it to the user
- The user sends a payment from his crypto wallet directly to the blockchain node
- Then the payment service has to monitor the blockchain node for incoming payments. Payment and order have to match.
A single private key can control one account (one address) only. The consequence is that the payment service has to generate a new account for each order to match orders and payments when pulling transactions from the blockchain node. This approach makes the withdrawal process quite expensive for merchants. When withdrawing, the merchant has to send a transaction from each account that received payment.
Every withdrawal transaction requires a fee. In the Ethereum ecosystem, native currency (Ether) transfer always consumes 21,000 units of GAS. The GAS price changes depending on the supply and demand for the network’s computational power to process transactions. The chart below presents the historical prices for Ether transfers from May 2021 to April 2022.
The price fluctuation is between 1 and 25 USD, which means that the merchant will have to pay from 1 to 25 USD for each transaction, making the withdrawal process quite expensive.
Let’s look at another approach where we will use smart contracts. A “smart contract” is a program that runs on the Ethereum blockchain, and resides at a specific address similar to regular Ethereum accounts. We can use a smart contract for receiving payments. The sample contract for receiving and then matching payments and orders may look like this:
The contract contains a mapping structure that assigns orders to payments. The assignment takes place in the Pay function. All orders are recorded to the blockchain and stored in a mapping structure. A contract account has an address assigned and can receive payments just like a regular account. In this example, we’re using the withdraw method to claim payments from the contract address. The merchant that owns the contract can withdraw funds in a single transaction and pay the fee only once.
However, users that are sending payments using the Pay method will have to pay an additional fee for storing data in the blockchain. Ethereum has three places to store data: storage, memory, and stack. Storage is for permanent data, which sticks to the contract and is the most expensive. Memory and stack are temporary storage and exist only during contract execution. The Implemented Pay method uses permanent storage and consumes 44,025 units of GAS, making transactions two times more expensive for the users than the simple Ether transfer from the previous example.
Let’s think about how the contract can be improved. We have to broadcast information about incoming payments and order numbers, but we don’t have to save it in the smart contract storage. We can use Ethereum logs and events that facilitate communication between the contract and client applications. During transaction processing, the smart contract can emit an event and write logs to the blockchain that the external application can later access. Writing logs is much cheaper than saving data in contract storage. The modified code of the PaymentReceiver contract may look like this:
The mapping collection from the previous example was replaced by the PaymentReceived event, which is emitted on Pay function execution. The corresponding logs are written to the blockchain whenever an event is emitted. Logs are designed to be a type of storage that costs significantly less GAS than storing data in contracts. The modified Pay method consumes 23,536 units of GAS, and it’s just a little more expensive than sending a payment to a regular account (as we did in the first example) which consumed 21,000 units of GAS. We didn’t modify the withdraw function; therefore, the merchant will withdraw funds in a single transaction and pay the fee only once, as in the previous example. Thanks to this approach, we optimized the transaction costs for both merchants and users.
Logs and events can be an interesting alternative for storing data in the Ethereum blockchain.
Words by Daniel Borowski, Engineering Lead (Blockchain) at Altimetrik Poland