Platform: Code4rena
Start Date: 23/11/2022
Pot Size: $24,500 CANTO
Total HM: 5
Participants: 37
Period: 5 days
Judge: berndartmueller
Total Solo HM: 2
Id: 185
League: ETH
Rank: 5/37
Findings: 1
Award: $1,978.12
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: Ruhum
Also found by: hihen, ronnyx2017
12248.4231 CANTO - $1,978.12
https://github.com/code-423n4/2022-11-canto/blob/main/CIP-001/src/Turnstile.sol#L86-L101 https://github.com/code-423n4/2022-11-canto/blob/main/Canto/x/csr/keeper/evm_hooks.go#L51
For any given tx, the fees are sent to its recipient (To
). Anybody can register an address using the Turnstile contract. Thus, a user is able to create a proxy contract with which they execute other smart contracts. That way, the fees are sent to their own contract instead of the actual application they are using. People who use smart contract wallets don't even have to bother with setting up a proxy structure. They just add their own wallet to the Turnstile contract.
Also, there might be a possibility of someone setting up a proxy for high-usage contracts where the fees are sent back to the caller. So for contract $X$, we create $X'$ which calls $X$ for the caller. Since $X'$ is the recipient of the tx, it gets the gas refund. To incentivize the user to use $X'$ instead of $X$, $X'$ sends a percentage of the refund to the caller. The feasibility both technically and economically depends on the contract that is attacked. But, theoretically, it's possible.
The incentive to take it for yourself instead of giving it to the app is pretty high. Since this causes a loss of funds for the app I rate it as HIGH.
Registering an address is permissionless:
function register(address _recipient) public onlyUnregistered returns (uint256 tokenId) { address smartContract = msg.sender; if (_recipient == address(0)) revert InvalidRecipient(); tokenId = _tokenIdTracker.current(); _mint(_recipient, tokenId); _tokenIdTracker.increment(); emit Register(smartContract, _recipient, tokenId); feeRecipient[smartContract] = NftData({ tokenId: tokenId, registered: true }); }
Fees are sent to the recipient of the tx:
func (h Hooks) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error { // Check if the csr module has been enabled params := h.k.GetParams(ctx) if !params.EnableCsr { return nil } // Check and process turnstile events if applicable h.processEvents(ctx, receipt) contract := msg.To() if contract == nil { return nil } // ...
none
It's pretty difficult to fix this properly. The ideal solution is to distribute fees according to each contract's gas usage. That will be a little more complicated to implement. Also, you have to keep an eye on whether it incentivizes developers to make their contracts less efficient. Another solution is to make this feature permissioned so that only select contracts are allowed to participate. For example, you could say that an address has to be triggered $X$ amount of times before it is eligible for gas refunds.
#0 - c4-judge
2022-11-30T09:34:39Z
berndartmueller marked the issue as primary issue
#1 - abhipingle
2022-12-08T09:05:31Z
In my view this isn't an issue and is well understood to be a drawback of the simpler method we chose.
#2 - c4-sponsor
2022-12-17T16:56:20Z
tkkwon1998 marked the issue as sponsor acknowledged
#3 - tkkwon1998
2022-12-17T16:57:46Z
We acknowledge this as true, but it's a drawback that was discussed during the design of CSR.
#4 - c4-judge
2023-01-02T12:06:55Z
berndartmueller marked the issue as selected for report