Transfers and Approval of ERC-20 Tokens from a Solidity Smart Contract

·

## Introduction  

In our previous tutorial, we explored **[the anatomy of an ERC-20 token in Solidity](https://ethereum.org/en/developers/tutorials/understand-the-erc-20-token-smart-contract/)** on the Ethereum blockchain. This guide will demonstrate how to **interact with ERC-20 tokens** using a **Solidity smart contract**, focusing on **transfers, approvals, and decentralized exchange (DEX) mechanisms**.  

### Key Concepts Covered:  
- **Smart Contracts**  
- **ERC-20 Tokens**  
- **Solidity Programming**  
- **Decentralized Exchange (DEX) Logic**  

---

## Setting Up the ERC-20 Token  

Below is the foundational **ERC20Basic** contract, which implements the **IERC20 interface**:  

pragma solidity ^0.8.0;

interface IERC20 {

function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);

function transfer(address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);

}

contract ERC20Basic is IERC20 {

string public constant name = "ERC20Basic";
string public constant symbol = "ERC";
uint8 public constant decimals = 18;

mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;

uint256 totalSupply_ = 10 ether;

constructor() {
    balances[msg.sender] = totalSupply_;
}

function totalSupply() public override view returns (uint256) {
    return totalSupply_;
}

function balanceOf(address tokenOwner) public override view returns (uint256) {
    return balances[tokenOwner];
}

function transfer(address receiver, uint256 numTokens) public override returns (bool) {
    require(numTokens <= balances[msg.sender]);
    balances[msg.sender] -= numTokens;
    balances[receiver] += numTokens;
    emit Transfer(msg.sender, receiver, numTokens);
    return true;
}

function approve(address delegate, uint256 numTokens) public override returns (bool) {
    allowed[msg.sender][delegate] = numTokens;
    emit Approval(msg.sender, delegate, numTokens);
    return true;
}

function allowance(address owner, address delegate) public override view returns (uint256) {
    return allowed[owner][delegate];
}

function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
    require(numTokens <= balances[owner]);
    require(numTokens <= allowed[owner][msg.sender]);
    
    balances[owner] -= numTokens;
    allowed[owner][msg.sender] -= numTokens;
    balances[buyer] += numTokens;
    emit Transfer(owner, buyer, numTokens);
    return true;
}

}


---

## Building a Decentralized Exchange (DEX)  

Our **DEX contract** will:  
1. **Exchange Ether for Tokens** (`buy` function).  
2. **Exchange Tokens for Ether** (`sell` function).  

### DEX Smart Contract Structure  

contract DEX {

IERC20 public token;

event Bought(uint256 amount);
event Sold(uint256 amount);

constructor() {
    token = new ERC20Basic();
}

function buy() payable public {
    uint256 amountTobuy = msg.value;
    uint256 dexBalance = token.balanceOf(address(this));
    require(amountTobuy > 0, "You need to send some Ether");
    require(amountTobuy <= dexBalance, "Not enough tokens in reserve");
    token.transfer(msg.sender, amountTobuy);
    emit Bought(amountTobuy);
}

function sell(uint256 amount) public {
    require(amount > 0, "You need to sell at least some tokens");
    uint256 allowance = token.allowance(msg.sender, address(this));
    require(allowance >= amount, "Check the token allowance");
    token.transferFrom(msg.sender, address(this), amount);
    payable(msg.sender).transfer(amount);
    emit Sold(amount);
}

}


---

## Key Functions Explained  

### 1. **Buying Tokens with Ether**  
- **User sends Ether** to the contract.  
- The contract **transfers tokens** equivalent to the Ether sent (1 Wei = 1 Token).  
- **Requirements**:  
  - Ether value must be > 0.  
  - Contract must have sufficient token reserves.  

👉 [Learn more about ERC-20 token standards](https://www.okx.com/join/BLOCKSTAR)  

### 2. **Selling Tokens for Ether**  
- **User must approve** the DEX to spend their tokens.  
- The contract **transfers tokens** from the user and sends Ether in return.  
- **Requirements**:  
  - Token amount must be > 0.  
  - User must have sufficient allowance.  

---

## FAQ Section  

### **Q1: How do I approve token spending for the DEX?**  
**A**: Call the `approve` function on the token contract, specifying the DEX address and amount.  

token.approve(dexAddress, amount);


### **Q2: Why does my transaction fail when selling tokens?**  
**A**: Ensure you’ve approved the DEX to spend your tokens and have sufficient balance.  

### **Q3: Can I customize the exchange rate?**  
**A**: Yes! Modify the `buy`/`sell` functions to implement dynamic pricing (e.g., 1 Token = 0.01 Ether).  

---

## Conclusion  

This tutorial covered:  
- **ERC-20 token transfers** (`transfer` and `transferFrom`).  
- **Approval mechanisms** (`approve`).  
- **Building a simple DEX** in Solidity.  

👉 [Explore advanced DEX development strategies](https://www.okx.com/join/BLOCKSTAR)  

For further learning:  
- **[Waiting for Transaction Confirmation](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/)**  
- **[Decoding Event Logs](https://ethereumdev.io/how-to-decode-event-logs-in-javascript-using-abi-decoder/)**