In previous versions of CW20 Basic (ideally before version v1.0.1), the Transfer, Send, Mint, and Burn messages prohibit zero-amount token transacts.

If the amount transacted is zero, an InvalidZeroAmount error is returned.

pub fn execute_transfer(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    recipient: String,
    amount: Uint128,
) -> Result<Response, ContractError> {
    if amount == Uint128::zero() {
        return Err(ContractError::InvalidZeroAmount {});
    }

Source: https://github.com/CosmWasm/cw-plus/blob/v1.0.1/contracts/cw20-base/src/contract.rs#L239-L248

This caused some bugs to happen in the past. For example, in the Astroport Core Updates audit, there was a bug where all swaps would fail when the fee was set to the maximum value.

This is because it tries to send zero CW20 amount, which will get caught by the InvalidZeroAmount error above. However, this is not a severe bug, as the contract owner can simply update the fee configurations to recover from this.

Screenshot 2023-07-11 at 4.08.27 PM.png

Source: Issue 7 in https://github.com/oak-security/audit-reports/blob/master/Astroport/2023-02-10 Audit Report - Astroport Core Updates v1.0.pdf

Out of curiosity, I went to check whether the Ethereum ecosystem (ERC20 tokens) also reverts on zero-value transfers.

That’s how I bumped the https://github.com/d-xo/weird-erc20 repo, which contains a list of ERC20 tokens that behaves differently. To my surprise, only one of them actually reverts on zero-amount transfers. Most ERC20 tokens actually allow zero-amount transfers, so why isn’t this the case in the Cosmos ecosystem?

I looked into the CW20 Basic codebase, which contains the TransferFrom, BurnFrom, and SendFrom messages. These messages have the same functionalities as the Transfer, Send, and Burn messages, with the difference of using allowances.

ExecuteMsg::TransferFrom {
            owner,
            recipient,
            amount,
        } => execute_transfer_from(deps, env, info, owner, recipient, amount),
ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount),
ExecuteMsg::SendFrom {
            owner,
            contract,
            amount,
            msg,
        } => execute_send_from(deps, env, info, owner, contract, amount, msg),

Source: https://github.com/CosmWasm/cw-plus/blob/v1.0.1/contracts/cw20-base/src/contract.rs#L215-L226

The allowance concept is similar to how a parent gives their child an allowance to buy toys. For example, Alice allows Bob to use 5 tokens from her account. This is typically performed through the IncreaseAllowance message.

To use the allowance, Bob calls the TransferFrom or SendFrom message to transfer the tokens out or uses the BurnFrom message to burn all 5 tokens, reducing the total supply.