Are you getting close to retirement age? If not, perhaps you have a parent who has recently retired or is planning to. If either of these situations apply, this week’s episode is for you.
My guest this week is Vivien Sharon, a Toronto-based realtor who specializes in working with Boomers, helping them downsize from home to condo living. Having downsized her own home, Vivien has the experience needed to guide clients through the entire process. She’s even written a book on the subject.
You can find the show notes for this episode at https://maplemoney.com/135
Our sponsor, Wealthsimple, believes that financial independence should be available to anyone. That’s why they have no account minimums, meaning that you can get started investing for as little as one dollar. Don’t delay any longer; invest online by visiting https://maplemoney.com/wealthsimple today.
Are you getting close to retirement age? If not, perhaps you have a parent who has recently retired or is planning to. If either of these situations apply, this week’s episode is for you.
My guest this week is Vivien Sharon, a Toronto-based realtor who specializes in working with Boomers, helping them downsize from home to condo living. Having downsized her own home, Vivien has the experience needed to guide clients through the entire process. She’s even written a book on the subject.
You can find the show notes for this episode at https://maplemoney.com/135
Our sponsor, Wealthsimple, believes that financial independence should be available to anyone. That’s why they have no account minimums, meaning that you can get started investing for as little as one dollar. Don’t delay any longer; invest online by visiting https://maplemoney.com/wealthsimple today.
On November 22, 2016(opens in a new tab)↗ the Spurious Dragon hard-fork introduced EIP-170(opens in a new tab)↗ which added a smart contract size limit of 24.576 kb. For you as a Solidity developer this means when you add more and more functionality to your contract, at some point you will reach the limit and when deploying will see the error:
This limit was introduced to prevent denial-of-service (DOS) attacks. Any call to a contract is relatively cheap gas-wise. However, the impact of a contract call for Ethereum nodes increases disproportionately depending on the called contract code's size (reading the code from disk, pre-processing the code, adding data to the Merkle proof). Whenever you have such a situation where the attacker requires few resources to cause a lot of work for others, you get the potential for DOS attacks.
Originally this was less of a problem because one natural contract size limit is the block gas limit. Obviously, a contract must be deployed within a transaction that holds all of the contract's bytecode. If you include only that one transaction into a block, you can use up all that gas, but it's not infinite. Since the London Upgrade, the block gas limit has been able to vary between 15M and 30M units depending on network demand.
Unfortunately, there is no easy way of getting the bytecode size of your contracts. A great tool to help you that is the truffle-contract-size(opens in a new tab)↗ plugin if you're using Truffle.
This will help you figure out how your changes are affecting the total contract sizes.
In the following we will look at some methods ordered by their potential impact. Think about it in the terms of weight-loss. The best strategy for someone to hit their target weight (in our case 24kb) is to focus on the big impact methods first. In most cases just fixing your diet will get you there, but sometimes you need a little bit more. Then you might add some exercise (medium impact) or even supplements (small impact).
This should always be your first approach. How can you separate the contract into multiple smaller ones? It generally forces you to come up with a good architecture for your contracts. Smaller contracts are always preferred from a code readability perspective. For splitting contracts, ask yourself:
One simple way to move functionality code away from the storage is using a library(opens in a new tab)↗. Don't declare the library functions as internal as those will be added to the contract(opens in a new tab)↗ directly during compilation. But if you use public functions, then those will be in fact in a separate library contract. Consider using for(opens in a new tab)↗ to make the use of libraries more convenient.
A more advanced strategy would be proxy system. Libraries use in the back which simply executes another contract's function with the state of the calling contract. Check out this blog post(opens in a new tab)↗ to learn more about proxy systems. They give you more functionality, e.g., they enable upgradability, but they also add a lot of complexity. I wouldn't add those only to reduce contract sizes unless it's your only option for whatever reason.
This one should be obvious. Functions increase a contract size quite a bit.
A simple change like this:
1function get(uint id) returns (address,address) {
2 MyStruct memory myStruct = myStructs[id];
3 return (myStruct.addr1, myStruct.addr2);
4}
5
1function get(uint id) returns (address,address) {
2 return (myStructs[id].addr1, myStructs[id].addr2);
3}
4
makes a difference of 0.28kb. Chances are you can find many similar situations in your contracts and those can really add up to significant amounts.
Long revert messages and in particular many different revert messages can bloat up the contract. Instead use short error codes and decode them in your contract. A long message could be become much shorter:
1require(msg.sender == owner, "Only the owner of this contract can call this function");
2
3
1require(msg.sender == owner, "OW1");
2
Custom errors have been introduced in Solidity 0.8.4(opens in a new tab)↗. They are a great way to reduce the size of your contracts, because they are ABI-encoded as selectors (just like functions are).
1error Unauthorized();
2
3if (msg.sender != owner) {
4 revert Unauthorized();
5}
6
You can also change the optimizer settings. The default value of 200 means that it's trying to optimize the bytecode as if a function is called 200 times. If you change it to 1, you basically tell the optimizer to optimize for the case of running each function only once. An optimized function for running only one time means it is optimized for the deployment itself. Be aware that this increases the gas costs for running the functions, so you may not want to do it.
If you are using the ABIEncoderV2(opens in a new tab)↗, it can help to not pass structs to a function. Instead of passing the parameter as a struct...
1function get(uint id) returns (address,address) {
2 return _get(myStruct);
3}
4
5function _get(MyStruct memory myStruct) private view returns(address,address) {
6 return (myStruct.addr1, myStruct.addr2);
7}
8
1function get(uint id) returns(address,address) {
2 return _get(myStructs[id].addr1, myStructs[id].addr2);
3}
4
5function _get(address addr1, address addr2) private view returns(address,address) {
6 return (addr1, addr2);
7}
8
... pass the required parameters directly. In this example we saved another 0.1kb.
Modifiers, especially when used intensely, could have a significant impact on the contract size. Consider removing them and instead use functions.
1modifier checkStuff() {}
2
3function doSomething() checkStuff {}
4
1function checkStuff() private {}
2
3function doSomething() { checkStuff(); }
4
Those tips should help you to significantly reduce the contract size. Once again, I cannot stress enough, always focus on splitting contracts if possible for the biggest impact.
Последнее редактирование: , Invalid DateTime