-
Notifications
You must be signed in to change notification settings - Fork 21
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
Conversation
…uctor and allowance check
@@ -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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); | |||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
*/
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
*/
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
if rest of the things are fine you can approve this and I can merge. |
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