DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: medium
Invalid

Users of smart wallets may not be able to interact with transfering plots

Summary

The current implementation of the transferPlot function in the Beanstalk protocol does not adequately consider users with smart contract wallets or account abstraction wallets.

Specifically, the function's address validation logic does not account for these types of wallets, which could lead to issues with transfers involving smart contract addresses.

Vulnerability Details

The transferPlot function is responsible for transferring plots from one user to another within the Beanstalk protocol. However, the function's parameter verification
and allowance checks, specifically at line 64 of the MarketplaceFacet.sol contract, do not account for the unique behaviors of smart contract wallets or account abstraction wallets.

function transferPlot(
address sender,
address recipient,
uint256 fieldId,
uint256 index,
uint256 start,
uint256 end
) external payable fundsSafu noNetFlow noSupplyChange nonReentrant {
require(
sender != address(0) && recipient != address(0),
"Field: Transfer to/from 0 address."
);
uint256 transferAmount = validatePlotAndReturnPods(fieldId, sender, index, start, end);
if (
LibTractor._user() != sender &&
allowancePods(sender, LibTractor._user(), fieldId) != type(uint256).max
) {
decrementAllowancePods(sender, LibTractor._user(), fieldId, transferAmount);
}
if (s.sys.podListings[fieldId][index] != bytes32(0)) {
LibMarket._cancelPodListing(sender, fieldId, index);
}
_transferPlot(sender, recipient, fieldId, index, start, transferAmount);
}

Impact


Consider a scenario where a user is using a smart contract wallet that implements custom logic for transaction execution and allowance checks. The current implementation of the transferPlot function does not handle this correctly due to the following reasons:

The function only checks if the sender and recipient are non-zero addresses. It does not account for the fact that these addresses could be smart contracts with specific execution logic.

The function performs allowance checks using standard ERC-20 logic, which may not be compatible with smart contract wallets that use different mechanisms for approving and executing transactions.

Tools Used

Manual Review

Recommendations

Below is the updated transferPlot function with additional considerations for smart contract wallets and account abstraction.

By incorporating these changes, the transferPlot function will be better equipped to handle transfers involving smart contract wallets and account abstraction wallets, ensuring compatibility and security across different wallet types and cross-chain scenarios.

function transferPlot(
address sender,
address recipient,
uint256 fieldId,
uint256 index,
uint256 start,
uint256 end
) external payable fundsSafu noNetFlow noSupplyChange nonReentrant {
require(
sender != address(0) && recipient != address(0),
"Field: Transfer to/from 0 address."
);
// Check if sender and recipient are contracts
bool senderIsContract = isContract(sender);
bool recipientIsContract = isContract(recipient);
// Handle allowances differently for contract wallets
if (senderIsContract) {
require(
checkContractAllowance(sender, msg.sender, fieldId, validatePlotAndReturnPods(fieldId, sender, index, start, end)),
"Field: Insufficient contract allowance."
);
} else {
uint256 transferAmount = validatePlotAndReturnPods(fieldId, sender, index, start, end);
if (
LibTractor._user() != sender &&
allowancePods(sender, LibTractor._user(), fieldId) != type(uint256).max
) {
decrementAllowancePods(sender, LibTractor._user(), fieldId, transferAmount);
}
}
if (s.sys.podListings[fieldId][index] != bytes32(0)) {
LibMarket._cancelPodListing(sender, fieldId, index);
}
_transferPlot(sender, recipient, fieldId, index, start, validatePlotAndReturnPods(fieldId, sender, index, start, end));
}
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function checkContractAllowance(
address owner,
address spender,
uint256 fieldId,
uint256 amount
) internal view returns (bool) {
// Implement custom logic to check contract wallet allowances
// This could involve calling a specific function on the contract
// to check if the transfer is allowed.
// For simplicity, this example assumes a function exists to check this.
return IContractWallet(owner).isAllowed(spender, fieldId, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.