Using ABI Encoding for Smart Contract Optimization on Celo

Using ABI Encoding for Smart Contract Optimization on Celo https://celo.academy/uploads/default/optimized/2X/7/7d900bbbd9d5055d0c176dbacd2142656a4bfe3b_2_1024x576.jpeg
none 0.0 0

Introduction

Given the rapid changes occurring within blockchain technology, honing smart contract optimization is paramount. This piece aims to investigate a specific optimization technique - employing ABI encoding within Celos’ blockchain. By understanding how to implement ABI encoding and decoding. We can expedite transaction speeds and reduce complexities in inter-contract communication.

Prerequisites

To successfully follow along with this tutorial, you need to understand the following:

  • Programming language Solidity.
  • Smart contracts and how they are used.
  • Ethereum and its gas computation system.
  • Basic knowledge of how the Ethereum Virtual Machine (EVM) works.

Requirements

  1. Solidity compiler (version 0.8.17 or later) should be installed.
  2. An Integrated Development Environment (IDE) for Solidity such as Remix IDE is needed.
  3. Access to test accounts on the Celo Testnet for deploying and interacting with smart contracts.

Recognizing ABI’s Function in Smart Contracts

The Application Binary Interface, or ABI, serves as a kind of link between blockchain-based smart contracts. Its main job is to convert higher-level languages into machine code that the Ethereum Virtual Machine (EVM) can understand and use. To enable inter-contract communication, the smart contract language Solidity provides a set of methods for encoding and decoding data in the ABI format.

Two contracts, AbiEncode and AbiDecode, will be used to demonstrate the ideas.

ABI Encoding

The AbiEncode contract is intended to show several ABI encoding methods in Solidity. Let’s examine the snippet of code:

AbiEncode.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IERC20 {
    function transfer(address, uint) external;
}

contract Token {
    function transfer(address, uint) external {}
}

// AbiEncode Contract
contract AbiEncode {
    function test(address _contract, bytes calldata data) external {
        (bool ok, ) = _contract.call(data);
        require(ok, "call failed");
    }

    function encodeWithSignature(
        address to,
        uint amount
    ) external pure returns (bytes memory) {
        return abi.encodeWithSignature("transfer(address,uint256)", to, amount);
    }

    function encodeWithSelector(
        address to,
        uint amount
    ) external pure returns (bytes memory) {
        return abi.encodeWithSelector(IERC20.transfer.selector, to, amount);
    }

    function encodeCall(address to, uint amount) external pure returns (bytes memory) {
        return abi.encodeCall(IERC20.transfer, (to, amount));
    }
}

Let’s go through each of the functions:

test()

function test(address _contract, bytes calldata data) external {
(bool ok, ) = _contract.call(data);
require(ok, "call failed");
}

Three distinct encoding techniques — encodeWithSignature, encodeWithSelector, and encodeCall —are displayed in the AbiEncode contract. The IERC20 transfer function can be called using any of the three approaches.

A contract is called using the low-level call function by the test function. The encoded data and the contract address are supplied as parameters. The error “call failed” will be thrown if the call is unsuccessful.

encodeWithSignature()

function encodeWithSignature(
    address to,
    uint amount
) external pure returns (bytes memory) {
    return abi.encodeWithSignature("transfer(address,uint256)", to, amount);
}

The byte array created by the encodeWithSignature function, which can be used with call, contains the function signature and arguments. The arguments are given separately and the function signature is passed as a string. This approach is simple to use but could be error-prone if the signature string is entered incorrectly because it uses a human-readable signature.

encodeWithSelector()

function encodeWithSelector(
    address to,
    uint amount
) external pure returns (bytes memory) {
    return abi.encodeWithSelector(IERC20.transfer.selector, to, amount);
}

The function selector is utilized by the encodeWithSelector function rather than the complete signature. The function selector, which is the first four bytes of the function signature’s Keccak-256 hash, is frequently used to refer to a function. The selector for a function defined in an interface can be easily obtained using the .selector property.

encodeCall()

function encodeCall(address to, uint amount) external pure returns (bytes memory) {
    return abi.encodeCall(IERC20.transfer, (to, amount));
}

Lastly, Solidity 0.8.4’s new abi.encodeCall method is used by the encodeCall function. It requires a function pointer to use.

ABI Decoding

To demonstrate how encoded data can be converted back into the original types, the AbiDecode contract is used. Examining the code now:

AbiDecode.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

// AbiDecode Contract
contract AbiDecode {
    struct MyStruct {
        string name;
        uint[2] nums;
    }

    function encode(
        uint x,
        address addr,
        uint[] calldata arr,
        MyStruct calldata myStruct
    ) external pure returns (bytes memory) {
        return abi.encode(x, addr, arr, myStruct);
    }

    function decode(
        bytes calldata data
    )
        external
        pure
        returns (uint x, address addr, uint[] memory arr, MyStruct memory myStruct)
    {
        (x, addr, arr, myStruct) = abi.decode(data, (uint, address, uint[], MyStruct));
    }
}

In order to demonstrate the ABI encoding and decoding procedures in Solidity, the AbiDecode contract highlights two essential operations, encode and decode. A MyStruct structure made up of a string and an array of two uints is also defined in the contract to show how complicated data types can be encoded and decoded.

encode Function

function encode(
    uint x,
    address addr,
    uint[] calldata arr,
    MyStruct calldata myStruct
) external pure returns (bytes memory) {
    return abi.encode(x, addr, arr, myStruct);
}

The parameters passed to the encode function are a uint, an address, an array of uints, and an instance of MyStruct. It then combines these various data types into a single bytes array for transmission using abi.encode. The concatenated byte representations of the arguments are contained in an array of packed, ABI-encoded bytes created by the abi.encode method. When we wish to construct a hash of structured data, notably for signing or validating signatures, we frequently utilize this function.

decode Function

function decode(
    bytes calldata data
)
    external
    pure
    returns (uint x, address addr, uint[] memory arr, MyStruct memory myStruct)
{
    (x, addr, arr, myStruct) = abi.decode(data, (uint, address, uint[], MyStruct));
}

The decode function, on the other hand, takes a bytes array and unpacks it into the original data types. The function abi.decode, which accepts two arguments—the data to be decoded and a tuple indicating the types to which the data should be encoded—enables this procedure. The function then designates x, addr, arr, and myStruct with the corresponding values that it produces.

Compile and deploy the smart contracts on Celo

Finally, compile and deploy both the AbiEncode.sol and AbiDecode.sol smart contracts, and test them out. For comprehensive instructions on utilizing the Remix IDE to deploy smart contracts to the Celo network, refer to this article.

When we deploy and test the Token and AbiEncode smart contracts in the AbiEncode.sol file, we’ll observe that the encodeWithSignature, encodeWithSelector, and encodeCall functions take in the address of the Token contract and an ETH amount to transfer and return a byte code of the transaction data which can be decoded back. This process of representing or returning data as bytes reduces gas fees and increases the efficiency of our smart contract since computers understand byte codes.

Conclusion

In the Celo ecosystem, ABI encoding and decoding are effective techniques for enhancing interactions between smart contracts. To fully utilize the advantages of these techniques, it is crucial to comprehend and work around their drawbacks, such as the lack of type safety in abi.encodeWithSignature and abi.encodeWithSelector.

Next Steps

  • Create your own Celo smart contracts with ABI encoding and decoding.
  • Investigate further smart contract optimization methods.
  • Learn more about ABI, how it works with the EVM, and how it affects the optimization of smart contracts.

About the Author

Israel Okunaya is an ace writer with a flair for simplifying complexities and a knack for storytelling. He leverages over four years of experience to meet the most demanding writing needs in different niches, especially food and travel, blockchain, and marketing. He sees blockchain as a fascinating yet tricky affair. So, he is given to simplifying its complexities with text and video tutorials.

References

  1. Solidity Documentation: ABI Encoding and Decoding Functions
  2. Celo Documentation
  3. ABI Encode and Decode code
  4. Github Codebase
7 Likes

Congratulations on your proposal being chosen as a standout this week at Celo Academy! As you prepare your tutorial, please ensure you follow our guidelines found here: Celo Sage Guidelines and Best Practices. Thank you for helping improve the experience of the developers learning at Celo Academy.

Note: Please update this description to include Celo and ensure that your tutorial focuses on helping developers build on Celo.

3 Likes

I’ve made the required changes to the topic and proposal.

3 Likes

@Southpaw i’ll be reviewing this in 1 - 2 days

1 Like

Looking forward to what the next steps will be.

2 Likes

@Southpaw Great job with the tutorial

Just one thing. Can you explain each function in your contracts individually, so your readers can understand the inner workings of each function.

See this for reference

How To Build A Multi Signature Wallet Contract That Requires Multiple Approvals For Transactions On Celo - Technical Tutorials - Celo Academy

Waiting to hear from you. Thanks.

1 Like

I am on it already.

2 Likes

Also the link to the repository points to remix not github

1 Like

Yes. I used Remix not Github

1 Like

I have fixed all the suggested corrections. You can check now @4undRaiser

1 Like

still missing a github repo link @Southpaw

I used remix instead of GitHub as my codebase. I believe the rule requires including your codebase. It doesn’t require using Github necessarily.
My codebase in this case was Remix. I hope you understand me.

Hi @Southpaw

Good to see this topic being treated. If you really want to make this tutorial very useful, you should discuss the benefits and disadvantages of using the ABI methods.

Note: These methods are often used in low-level interactions and they have serious consequences when not used properly or not knowing when to use them. Since the topic is short, I’d suggest you elaborate more on it to make it encompassing. Discussing everything about ABI methods in this post would be very helpful and not too bad.

6 Likes

I have done that in a previous article. Thanks

@4undRaiser What will the next step be, please?

@Southpaw i understand you vividly but I think a GitHub repository is the preferred means for hosting your code for the celo academy. So I’ll be tagging @Celo_Academy to hear otherwise. Thanks

Do you mind share with me?

4 Likes

@4undRaiser I already added the GitHub repo link. I believe we can proceed now

good job you can move to publish @Southpaw

1 Like

The step-by-step explanations, accompanied by practical examples, make it easy for developers to grasp the intricacies of ABI encoding and implement it effectively in their projects. Your expertise and clear communication style make this tutorial an invaluable resource for anyone seeking to take their smart contract optimization skills to the next level. Thank you for sharing your knowledge and contributing to the advancement of smart contract development on Celo!"

1 Like