Disclaimer
The following article is for educational purposes only. It is not advisable to use the contract in the example for anything other than what is presented in the article.
As an investor or a developer, you’ll find that keeping track of DeFi projects is not as simple as it may sound. There are many reasons to feel restless when you don’t know how they are behaving, so you decide that you need some sort of app for that. While you’ve mastered the ways of the chart and data analysis, you are having trouble getting data. If that’s the case, welcome to the second of four articles on Snapshots with C#.
The main goal for this series is to get you acquainted with the Nethereum library that we’ll be using for said purpose and then develop our very own snapshot tool. After reading this second article, you’ll be able to understand how contract calls are made and what can you do with Nethereum. From there, you can pick any of our two articles which correspond to:
- Developing a snapshot unit
- Analyzing the snapshot data
Requirements C#, JavaScript, and D3.JS (optional)
Stack MongoDB, ASP.NET MVC
Nugets MongoDriver, Nethereum
Difficulty 3/5
Repository https://github.com/labs-agap2it/SnapshotExample
Where do we start?
Our goal is to pass on knowledge. It’s easier to do so when most of the work that’s not related to the topic is already done. This way we can really focus on doing what we do best, problem-solving!
In our repository, you’ll find a solution that has the web application running with mock data. Through these three articles, we will change that solution to actually do some analysis on a real contract. Every step we take has a new release so, if you think you made a mistake and you can’t find it anywhere, just load the right release and start from there. If you want to make your own app and adapt our project to it, you can do it as well since we will write our code focusing on a modular and reusable approach.
Let’s take a look at our project. It’s a simple MVC Web application with .NET Core. This way, even if you’re using a Linux distribution, you can follow along as well. If you are new to the MVC pattern, it stands for Model-View-Controller, with each component having a specific role in the application. The Controller receives requests, changes the Model used to represent data, and renders Views that present that data to the user.
Our web application has a home controller and a snapshot controller. We’ll use the snapshot controller. In it, you’ll find several functions. Both Index and TakeSnapshot display pages with the request form and the status of the snapshot respectively.
There are two libraries in the solution as well. The DataManagement, that connects us to a MongoDB instance, and the Crypto.Eth.Snapshot that executes the snapshot. Most of our work will be done in this library and in the SnapshotController.
For the most part, our code will interact with Nethereum, a set of modular libraries that empower .NET development on EVM-like blockchains. It has several utilities besides RPC callers and is constantly in development with an active community.
Our development is divided into three steps. The first one, in this article, is a set of validations to check if the address is valid if it is associated with a contract and a token. The next article deals with the snapshot. We’ll be building a base for any snapshot and then write a specification to find token-related transactions. Lastly, in the fourth article, we’ll take the data we collected and change it according to a series of analyses.
Contract and Token Address Validation
An address is a virtual identity that can operate in the blockchain. It may be a wallet, a contract, or even a token. We can make a simple check to avoid executing an incorrect or needless snapshot. We could check if the address has a Hex prefix (0x), if it has a valid length (40 characters) and if every character is hexadecimal. Or we could just use Nethereum’s AddressUtil.
public bool IsValidAddress(string address) { var addressUtil = new Nethereum.Util.AddressUtil(); return addressUtil.IsValidEthereumAddressHexFormat(address); }
To the naked eye, wallet addresses and contract addresses are indistinguishable. Looking at its structure, we notice that it has a code field containing executable byte code. Therefore, it is not a contract if the code is 0x.
public async Task<bool> IsContractAddress(string address) { try { var web3 = new Web3(_rpcUrl); var code = await web3.Eth.GetCode.SendRequestAsync(address); return code != "0x"; } catch { return false; } }
If the address is a contract, then there’s a chance that it is also a token. We can use an ABI (Application Binary Interface) with the right functions to execute this validation. In this case, the totalSupply function would suffice. If the execution throws an exception, it’s guaranteed that the address is not a token.
public async Task<bool> IsTokenAddress(string address) { var web3 = new Web3(_rpcUrl); var abi = @"[{""inputs"": [], ""name"": ""totalSupply"", ""outputs"": [ {""internalType"": ""uint256"", ""name"": """", ""type"": ""uint256"" } ],""stateMutability"": ""view"", ""type"": ""function"" }]"; var contract = web3.Eth.GetContract(abi, address); try { var totalSupplyFunction = contract.GetFunction("totalSupply"); var totalSupply = await totalSupplyFunction.CallAsync<BigInteger>(); return totalSupply != 0; } catch { return false; } }
We can change the code in our Snapshot controller to call both functions and test it to see if it works. This exercise introduces you to the use of Nethereum. While the GetCode function exists regardless of the address, some, like totalSupply, require an ABI to instantiate a contract. You’ll need to specify the function name you are calling to use the SendRequest method. On a side note, as it is common practice with RPC calls, these methods don’t know what arguments to expect, so you need to know them beforehand.
If you want to test these changes, you can change the GetAddressInfo with the following code in the SnapshotController.
public async Task<AddressInfo> GetAddressInfo(string address) { bool isContract = false; bool isToken = false; var isValidAddress = _addressUtils.IsValidAddress(address); if(isValidAddress) { isContract = await _addressUtils.IsContractAddress(address); if(isContract) { isToken = await _addressUtils.IsTokenAddress(address); } } return new AddressInfo() { IsAddress = isValidAddress, IsContract = isContract, IsToken = isToken}; }
You can test it with different types of addresses.
By now, you’ve learned how to do contract calls with Nethereum, and should be able to find most utility classes it offers. How about we keep this going? In the next article, we are going to build our snapshot.