Explore Seaport Gas Optimization with Laika
As you might know, OpenSea just released the new SeaPort, a brand new NFT Marketplace that is efficient in terms of gas-saving (and more).
This article will be about what Seaport does for their gas optimization as well as how you can poke Seaport smart contracts using Laika.
My inspiration for this article came from gist posted by ecmendenhall here. I liked the way they (OpenSea) did for their smart contracts optimization so I figured let’s take some part of it to be article!
In this article, we’re going to talk a little bit about Ethereum Calldata. If you never heard of it before I advise you quickly check the article below here before continuing here:
Get to know Ethereum Calldata
This article explain what Ethereum Calldata is and how it’s structured.
And some of the Assembly here:
The smart contract that we’re going to look at today is going to be Seaport smart contract. And to be more specific we’re going to look at how they implement their transfer on their _performERC20Transfer() function. So let’s look into it!
As you can see that it is fairly long here. To make it easier to look at, I’m going to summarize what they do before we’re going to look at it.
They simply optimize their token transfer by instead of calling a function from another smart contract the ordinary way we use in Solidity they turn it into assembly and perform the call process by themself. (As you can see from lines 10–199)
Their smart contract consists of a few parts where they have to
- Constructing Ethereum Call data
- Call it on another smart contract
- Handle Errors
- Clean up
Let’s look into it part by part!
- Constructing Ethereum Calldata
The code above is from lines 11–22. What they do is just load new memory using mload and call that memPointer.
After that things were fairly simple. They add numbers of values into that memory by storing them using mstore function.
After this finish, the memPointer would be storing something similar to
(Function Signature) + (from arg) + (to arg) + (amount arg)
then we’re ready to go to the next part.
2. Call it on another smart contract
For this part, we use the calldata we construct on the above to call to another smart contract. Hereafter the contract is called we store the data into the variable callStatus. and then check if the call is a success by using a beautiful trick on lines 37–46
3. Handle Errors
Alright, this is the part where it’s the longest. But the reason that it’s quite long is that there are a number of cases where the call could go wrong not because it was complicated.
To make this article small I will show you just how we could add a revert message using Solidity. (If you are interesting in a more complicated situation I advise you look deeply into the codebase)
// Otherwise revert with error about token not having code: mstore(NoContract_error_sig_ptr, NoContract_error_signature) mstore(NoContract_error_token_ptr, token) revert(NoContract_error_sig_ptr, NoContract_error_length)
This is from lines 137–140. As you can see it’s quite similar to what we do at 1. Constructing Ethereum Calldata. What we’re doing here was to store the data into some memory and then just call revert() to let it revert the transaction.
4. Clean up
// Restore the original free memory pointer. mstore(FreeMemoryPointerSlot, memPointer)// Restore the zero slot to zero.
To clean up the memory there were simply just 2 lines of code (from lines 195–199)
We clean up the memory since there might be some function that runs after this function that wants to use a memory.
Alright! So this is just a small walkthrough of _performERC20Transfer() there were several examples like this in Seaport smart contract which really fun to look at. And if you want to look at their smart contracts Laika would be really helpful when you want to try to look at some functions.
Let’s see how we could use Laika to help poke their smart contract!
But first let's get where the smart contracts are you can look where they deploy their smart contracts here: https://github.com/ProjectOpenSea/seaport
(I will use Rinkeby in this case)
To import their smart contract in Laika is fairly simple just click on the New button here.
Select Chain Explorer
Copy and Paste the address, Pick your desired chain. and then hit import button
Done! Here’s a list of requests that you can try playing with their smart contracts.
Another really cool feature in Laika is that it can auto-generate the Ethereum Call Data for you. This would be really convenient if you want to try to optimize your smart contract using Assembly