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

code optimisation #2

Merged
merged 10 commits into from
May 11, 2023
Merged

code optimisation #2

merged 10 commits into from
May 11, 2023

Conversation

livingrockrises
Copy link
Contributor

@livingrockrises livingrockrises commented May 7, 2023

  • Remove pack() function and move everything to own implementation of getHash()

  • refactor this.exchangeRate -> exchangeRate by making function external -> public

  • aid contract FeeManager which might be helpful in future

  • dev notes : todo / review that should be checked on final stage (early stage of audits)

  • not using userOp.gasPrice() as bundlers would reject this (because it touches GASPRICE and BASEFEE opcodes)
    NOTE: postOp may still use calculation based on above op-codes as max priority would be truth for accurate calcs
    updated context accordingly so we still have needful information

  • commented allowance check and validateConstructor to open a discussion around this
    EDIT: removed above

  • Added a test case for undeployed wallet

@@ -68,18 +51,12 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
// receiver of withdrawn fee tokens
address public feeReceiver;

// review offsets and define more if needed
// others in between if we just do concat and not abi.encode
uint256 private constant VALID_PND_OFFSET = 21;
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is PND offset. Defined this in line comment above this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok done

@@ -112,7 +89,7 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
address indexed _actor
);

event TokenPaymasterOperation(address indexed sender, address indexed token, uint256 cost);
event TokenPaymasterOperation(address indexed sender, address indexed token, uint256 charge, uint256 premium);
Copy link
Collaborator

Choose a reason for hiding this comment

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

In Constructor, _transferOwnership(_owner) is already done in BasePaymaster.
No need to do this again in this constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@@ -112,7 +89,7 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
address indexed _actor
);

event TokenPaymasterOperation(address indexed sender, address indexed token, uint256 cost);
event TokenPaymasterOperation(address indexed sender, address indexed token, uint256 charge, uint256 premium);

Copy link
Collaborator

Choose a reason for hiding this comment

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

In setFeeReceiver() method, we are saving feeReceiver address in a slot variable.

assembly {
    sstore(feeReceiver.slot, _newFeeReceiver)
}

But in constructor we are saving directly in variable.

feeReceiver = address(this);

Same is happening for oracleAggregator as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I din't get this. We can make this consistent, is that what you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can use assembly in the constructor

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes need to make this consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this change is already done, pushed in last commit.

@@ -181,7 +158,7 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
emit NewFeeTokenSupported(_token, msg.sender);
}

function setUnaccountedEPGasOverhead(uint256 value) external onlyOwner {
function setUnaccountedEPGasOverhead(uint256 value) external payable onlyOwner {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this made payable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All the methods that are not likely to be called (for example onlyOwner ones) will save gas if they are payable, because this removes the verification if there's value provided with the call

@@ -181,7 +158,7 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
emit NewFeeTokenSupported(_token, msg.sender);
}

function setUnaccountedEPGasOverhead(uint256 value) external onlyOwner {
function setUnaccountedEPGasOverhead(uint256 value) external payable onlyOwner {
uint256 oldValue = UNACCOUNTED_COST;
UNACCOUNTED_COST = value;
emit EPGasOverheadChanged(oldValue, value);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Update this comment

from

/**
  * add a deposit for this paymaster, used for paying for transaction fees
  */

to

/**
  * Add a deposit in native currency for this paymaster, used for paying for transaction fees. 
  * This is ideally done by the entity who is managing the received ERC20 gas tokens.
  */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok done

gasPriceUserOp = Math.min(maxFeePerGas, maxPriorityFeePerGas + block.basefee);
}
}

uint256 actualTokenCost = ((actualGasCost + (UNACCOUNTED_COST * gasPriceUserOp)) * effectiveExchangeRate) / 1e18;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should add the fee also during actualTokenCost calculation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tomarsachin2271 fee is in token terms, not gas execution cost or like fee multiplier even.

in below line check, no of tokens being transferred is actualTokenCost + fee


// solhint-disable no-inline-assembly

enum Operation {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is this enum used?

Copy link
Contributor Author

@livingrockrises livingrockrises May 10, 2023

Choose a reason for hiding this comment

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

within the contract where Exec could be used.

this is utils contract which is not used directly in token paymaster. and won't be part of the audit

feeToken.safeTransferFrom(account, feeReceiver, actualTokenCost + fee);
emit TokenPaymasterOperation(account, address(feeToken), actualTokenCost + fee);
emit TokenPaymasterOperation(account, address(feeToken), actualTokenCost, fee);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also emit effectiveExchangePrice , userOpHash, priceSource in the event for better tracking purpose.

If possible add another field which have value 'actualTokenCost + fee' also to simplify the accounting if it is to be used off-chain.

Need to pass userOpHash in the context from validatePaymasterUserOp method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so what all we should emit?

userOpHash will anyways be emitted in UserOperationEvent (sender as well)

I am also emitting actualTokenCost and fee separately, do you want us to emit actualTokenCost, fee, and actualTokenCost + fee ?

and should all of these be in same event (I can index at max 3 values - all indexed is ideal)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes we need to also emit UserOpHash in this event, coz we want to co-relate the transaction when we update these transactions in the database in paymaster service.
Eg. When signing request comes to paymaster service, we save the data in db with userOpHash as primary key.
Now when we track the TokenPaymasterOperation event, then it becomes hard to corelate it with userOpHash already present in the database. Now we have to get all the logs of same transactino and try to search UserOperation event (which can be multiple btw in same transaction) and then try to corelate it with this event.

That's why its important to emit userOpHash also in this event, so we can corelate this event with actual transaction data in database.

Also its fine if there are more than 3 events, it can be decoded using the ABI, not a big issue.

So in all we need to emit
userOpHash, (actualTokenCost+fee), fee, account, tokenAddress, effectiveExchangePrice, priceSource

Where account, tokenAddress, actualTokenCost+fee are indexed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, let me make single event and add more fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -34,31 +37,11 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
using UserOperationLib for UserOperation;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Contract level comment can be made more clear and simple.


/**
 * A token-based paymaster that allows user to pay gas fee in ERC20 tokens. The paymaster owner chooses which tokens to accept.
 * The payment manager (usually the owner) first deposits native gas into the EntryPoint. Then, for each transaction, it takes the gas fee from the user's ERC20 token balance. The manager must convert these collected tokens back to native gas and deposit it into the EntryPoint to keep the system running.
 * It is an extension of VerifyingPaymaster which trusts external signer to authorize the transaction, but also with an ability to withdraw tokens.
 * 
 * The validatePaymasterUserOp function does not interact with external contracts but uses an externally provided exchange rate.
 * Based on the exchangeRate and requiredPrefund amount, the validation method checks if the user's account has enough token balance. This is done by only looking at the referenced storage.
 * All Withdrawn tokens are sent to a dynamic fee receiver address.
 * 
 * Optionally a safe guard deposit may be used in future versions.
 */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -315,7 +277,7 @@ contract BiconomyTokenPaymaster is BasePaymaster, ReentrancyGuard, TokenPaymaste
UserOperation calldata userOp,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Below line can be changed as paymasterAndData now contains more data.

The paymaster data is expected to be the paymaster and a signature over the entire request parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@livingrockrises
Copy link
Contributor Author

if rest of the things are fine you can approve this and I can merge.

@tomarsachin2271 tomarsachin2271 merged commit 126c9e0 into master May 11, 2023
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