Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas optimization #6

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open

Gas optimization #6

wants to merge 15 commits into from

Conversation

juniormp
Copy link

@juniormp juniormp commented Mar 31, 2023

GrtPool

Before gas optimization.

image

After gas optimization.

image

Savings

Methods

setIsActive: 33873 - 33735 = 138 gas
setOffer: 165779 - 165628 = 151 gas
depositETHAndAcceptOffer: 196657 - 173566 = 23091 gas
approve: 59656 - 46234 = 13422 gas
mint: 59656 - 59656 = 0 gas

Deployment

GrtPool: 1979188 - 1734279 = 244909 gas
ERC20Example: 669119 - 669119 = 0 gas

GrtOffer

before gas optimization.

image

before gas optimization.

image

Savings

Methods

setChainIdOffer: 35765 - 35729 = 36 gas
setIsActive: 33873 - 33735 = 138 gas
setMaxPriceLimit: 37303 - 37245 = 58 gas
setMinPriceLimit: 37344 - 37286 = 58 gas
setOffer: 165780 - 165629 = 151 gas
setTokenOffer: 36136 - 36069 = 67 gas
approve: 39127 - 39127 = 0 gas
mint: 55998 - 55998 = 0 gas

Deployment

GrtPool: 1734279 - 1734279 = 0 gas
ERC20Example: 669119 - 669119 = 0 gas

hardhat.config.ts Outdated Show resolved Hide resolved
@@ -54,10 +57,10 @@ contract GrtLiquidityWallet is OwnableUpgradeable, UUPSUpgradeable {
address to,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tcoratger do you think it's valid to check if the address is zero for token and to params?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp Yes we can do that to be sure that there is no mistake on this side!

  • No payment with a wrong token (zero address) is valid.
  • No payment to a zero address. It is unlikely that Lisa gave a payment address 0 but it is always good to check it: in this case the procedure cancels.

if(destAddr == address(0))
revert ZeroAddressIsNotAllowed();
if(!_offers[offerId].isActive)
revert OfferInactive();
(bool sent, ) = address(this).call{value: msg.value}("");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can move this call to the bottom of the function to protect against reentrancy ?
And also to follow Solidity security Guidelines with Checks-Effects-Interactions pattern.
https://docs.soliditylang.org/en/v0.6.11/security-considerations.html

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp I understand your point here but I don't think it applies in this case.

You point to the fact that an attacker could call this function recursively and since the rest of the calculations would not yet be finished, he could then call the function ten times.

  • First, in any case, the funds are transferred to our smart contract and not to an external account, so this attack would be rather beneficial for us :)
  • Then, I think it is important to keep this call here because this allows us to first make the deposit on our contract and then the following calculations (in the event that the deposit fails everything is canceled afterwards). Whereas if we position this call at the end, it is possible that all the calculations are done then that the deposit fails at the end and in this case it can cause a bug on our side.

@SAPikachu Maybe you have a better opinion here?

@tcoratger
Copy link
Collaborator

@juniormp Can you have a look at all the revert functions. Here are the explanations: we want to make a platform easy to use for non-developers. This must therefore include the most explicit and readable error messages possible for uninitiated users.

In this context, your revert functions don't contain explicit messages. It would be preferable to add something like this example:

This is for our final user to read a proper sentence rather that a function name like TransferedAmountMustBePositive. What do you think?

@juniormp
Copy link
Author

juniormp commented Apr 18, 2023

@juniormp Can you have a look at all the revert functions. Here are the explanations: we want to make a platform easy to use for non-developers. This must therefore include the most explicit and readable error messages possible for uninitiated users.

In this context, your revert functions don't contain explicit messages. It would be preferable to add something like this example:

Link: https://solidity-by-example.org/error/#:~:text=revert(%22Input%20must%20be%20greater%20than%2010%22)%3B
This is for our final user to read a proper sentence rather that a function name like TransferedAmountMustBePositive. What do you think?

@tcoratger makes sense.
So we can comeback with the require check ?

@tcoratger
Copy link
Collaborator

@tcoratger makes sense. So we can comeback with te require check ?

@juniormp Not obviously, there is also this option with an explicit message (to be seen in terms of transaction costs):

On my side, I'm more used to seeing require.

@juniormp
Copy link
Author

@juniormp Not obviously, there is also this option with an explicit message (to be seen in terms of transaction costs):

Link: https://solidity-by-example.org/error/#:~:text=revert(%22Input%20must%20be%20greater%20than%2010%22)%3B
On my side, I'm more used to seeing require.

I think the same. Therequire statement is the most common.

@juniormp juniormp force-pushed the gas-optimization branch 2 times, most recently from 4d8bdc7 to 6b9a6ed Compare April 18, 2023 23:35
"Grindery wallet: insufficient balance."
);
if(address(this).balance < amount)
revert InsufficientBalance();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp Like for the other revert, it would be better to use a message in the form of a string that can be read directly by an average user who is not necessarily familiar with Solidity, like this example, WDT?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp Can you have a look at this part when you have time so that we can merge before the audit (17 April)?

@@ -54,10 +57,10 @@ contract GrtLiquidityWallet is OwnableUpgradeable, UUPSUpgradeable {
address to,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp Yes we can do that to be sure that there is no mistake on this side!

  • No payment with a wrong token (zero address) is valid.
  • No payment to a zero address. It is unlikely that Lisa gave a payment address 0 but it is always good to check it: in this case the procedure cancels.

if(destAddr == address(0))
revert ZeroAddressIsNotAllowed();
if(!_offers[offerId].isActive)
revert OfferInactive();
(bool sent, ) = address(this).call{value: msg.value}("");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp I understand your point here but I don't think it applies in this case.

You point to the fact that an attacker could call this function recursively and since the rest of the calculations would not yet be finished, he could then call the function ten times.

  • First, in any case, the funds are transferred to our smart contract and not to an external account, so this attack would be rather beneficial for us :)
  • Then, I think it is important to keep this call here because this allows us to first make the deposit on our contract and then the following calculations (in the event that the deposit fails everything is canceled afterwards). Whereas if we position this call at the end, it is possible that all the calculations are done then that the deposit fails at the end and in this case it can cause a bug on our side.

@SAPikachu Maybe you have a better opinion here?

@@ -25,56 +25,53 @@ contract GrtOffer is GrtOfferUtils {
);
event LogSetStatusOffer(bytes32 indexed _idOffer, bool indexed _isActive);

function setChainIdOffer(bytes32 offerId, uint256 chainId) external {
modifier isOwner(Offer memory offer) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp A comment for detail but can you just change the name of this function to make it more explicit? Owner is already associated with a condition of the contract ownable and so this can be confusing when reading :)

emit LogSetChainIdOffer(offerId, chainId);
}

function setTokenOffer(bytes32 offerId, address token) external {
function setTokenOffer(bytes32 offerId, address token) external isOwner(_offers[offerId]) {
require(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juniormp If we have the isOwner modifier, is there a reason to keep the require? I think that's a duplication, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants