diff --git a/.gitignore b/.gitignore index 92ed6a37..d1db59b8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ bin spamoor.db* go.lib.mod go.lib.sum +*.abi +*.bin diff --git a/README.md b/README.md index b91643b5..2cdcfae3 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ RPC hosts support additional configuration parameters through URL prefixes: Spamoor provides multiple scenarios for different transaction types: + | Scenario | Description | |----------|-------------| | [`eoatx`](./scenarios/eoatx/README.md) | **EOA Transactions**
Send standard EOA transactions with configurable amounts and targets | diff --git a/scenarios/scenarios.go b/scenarios/scenarios.go index b6e9c9ce..8eae67c5 100644 --- a/scenarios/scenarios.go +++ b/scenarios/scenarios.go @@ -16,6 +16,10 @@ import ( "github.com/ethpandaops/spamoor/scenarios/gasburnertx" "github.com/ethpandaops/spamoor/scenarios/geastx" "github.com/ethpandaops/spamoor/scenarios/setcodetx" + sbcontractdeploy "github.com/ethpandaops/spamoor/scenarios/statebloat/contract_deploy" + sbeoadelegation "github.com/ethpandaops/spamoor/scenarios/statebloat/eoa_delegation" + sberc20maxtransfers "github.com/ethpandaops/spamoor/scenarios/statebloat/erc20_max_transfers" + sbrandsstore "github.com/ethpandaops/spamoor/scenarios/statebloat/rand_sstore" "github.com/ethpandaops/spamoor/scenarios/storagespam" uniswapswaps "github.com/ethpandaops/spamoor/scenarios/uniswap-swaps" "github.com/ethpandaops/spamoor/scenarios/wallets" @@ -39,6 +43,10 @@ var ScenarioDescriptors = []*scenario.Descriptor{ &gasburnertx.ScenarioDescriptor, &geastx.ScenarioDescriptor, &setcodetx.ScenarioDescriptor, + &sbcontractdeploy.ScenarioDescriptor, + &sbeoadelegation.ScenarioDescriptor, + &sberc20maxtransfers.ScenarioDescriptor, + &sbrandsstore.ScenarioDescriptor, &storagespam.ScenarioDescriptor, &uniswapswaps.ScenarioDescriptor, &wallets.ScenarioDescriptor, diff --git a/scenarios/statebloat/README.md b/scenarios/statebloat/README.md new file mode 100644 index 00000000..28d23d77 --- /dev/null +++ b/scenarios/statebloat/README.md @@ -0,0 +1,35 @@ +# State Bloat Scenarios + +This directory contains scenarios designed to test different vectors of state growth on Ethereum. Each scenario focuses on a specific method of increasing the state size while minimizing ETH cost. + +## Available Scenarios + +1. `contract-deploy` - Deploys 24kB contracts (EIP-170 limit) +2. `delegate-flag` - Adds delegate flags to funded EOAs (EIP-7702) +3. `fund-eoa` - Funds fresh EOAs with minimal ETH +4. `empty-auth` - Creates EIP-7702 authorizations for empty addresses +5. `storage-slots` - Fills new storage slots in contracts + +## Testing + +These scenarios can be tested using Anvil (Foundry's local Ethereum node) or any other EVM-compatible testnet. For local testing: + +```bash +# Start Anvil +anvil + +# Run a scenario (example) +spamoor statebloat/contract-deploy [flags] +``` + +Each scenario directory contains its own README with specific configuration options and testing instructions. + +## Gas Efficiency Comparison + +| Rank | Scenario | Gas/Byte | Max Units in 30M Gas Block | +| ---- | --------------- | -------- | -------------------------- | +| 1 | Contract Deploy | ~202 | 6 deployments | +| 2 | Delegate Flag | ~232 | 960 tuples | +| 3 | Fund EOA | ~267 | 1000 accounts | +| 4 | Empty Auth | ~289 | 767 tuples | +| 5 | Storage Slots | 625 | 1500 slots | \ No newline at end of file diff --git a/scenarios/statebloat/contract_deploy/README.md b/scenarios/statebloat/contract_deploy/README.md new file mode 100644 index 00000000..26b2f56b --- /dev/null +++ b/scenarios/statebloat/contract_deploy/README.md @@ -0,0 +1,58 @@ +# 🏭 Contract Deployment State Bloat + +This scenario deploys contracts that are exactly 24kB in size (EIP-170 limit) to maximize state growth while minimizing gas cost. + +## How it Works + +1. Generates a contract with exactly 24,576 bytes of runtime code +2. Deploys the contract using CREATE with a salt that makes the bytecode unique +3. Uses batch-based deployment: + - Calculates how many contracts fit in one block based on gas limits + - Sends a batch of transactions that fit within the block gas limit + - Waits for a new block to be mined + - Repeats the process ("bombards" the RPC after each block) +4. Each deployment adds: + - 24,576 bytes of runtime code + - Account trie node + - Total state growth: ~24.7kB per deployment + +## β›½ Gas Cost Breakdown + +- 32,000 gas for CREATE +- 20,000 gas for new account +- 200 gas per byte for code deposit (24,576 bytes) +- **Total: 4,967,200 gas per deployment** + +## Batch Strategy + +The scenario automatically calculates how many contracts can fit in one block: +- Default block gas limit: 30,000,000 gas +- Gas per contract: 4,967,200 gas +- Contracts per batch: ~6 contracts per block + +This ensures optimal utilization of block space while maintaining predictable transaction inclusion patterns. + +## πŸš€ Usage + +### Build +```bash +go build -o bin/spamoor cmd/spamoor/main.go +``` + +### Run +```bash +./bin/spamoor --privkey --rpchost http://localhost:8545 contract-deploy [flags] +``` + +#### Key Flags +- `--max-transactions` - Total number of contracts to deploy (0 = infinite, default: 0) +- `--max-wallets` - Max child wallets to use (0 = root wallet only, default: 0) +- `--basefee` - Base fee per gas in gwei (default: 10) +- `--tipfee` - Tip fee per gas in gwei (default: 2) + +#### Example with Anvil node +```bash +./bin/spamoor --privkey ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ + --rpchost http://localhost:8545 contract-deploy \ + --max-transactions 0 +``` \ No newline at end of file diff --git a/scenarios/statebloat/contract_deploy/contract/.gitignore b/scenarios/statebloat/contract_deploy/contract/.gitignore new file mode 100644 index 00000000..f9096e53 --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract/.gitignore @@ -0,0 +1,3 @@ +*.output.json +*.bin +*.abi \ No newline at end of file diff --git a/scenarios/statebloat/contract_deploy/contract/README.md b/scenarios/statebloat/contract_deploy/contract/README.md new file mode 100644 index 00000000..de0d8758 --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract/README.md @@ -0,0 +1,35 @@ +# Contract Compilation + +This directory contains the Solidity contract and its compiled artifacts. To compile the contract and generate Go bindings: + +1. Install solc (Solidity compiler): +```bash +# On macOS +brew install solidity + +# On Ubuntu/Debian +sudo add-apt-repository ppa:ethereum/ethereum +sudo apt-get update +sudo apt-get install solc +``` + +2. Install abigen (ABI generator): +```bash +go install github.com/ethereum/go-ethereum/cmd/abigen@latest +``` + +3. Compile the contract: +```bash +solc --abi StateBloatToken.sol -o . --overwrite +solc --bin StateBloatToken.sol -o . --overwrite +``` + +4. Generate Go bindings: +```bash +abigen --bin=StateBloatToken.bin --abi=StateBloatToken.abi --pkg=contract --out=StateBloatToken.go +``` + +The generated files will be: +- `StateBloatToken.abi` - Contract ABI +- `StateBloatToken.bin` - Contract bytecode +- `StateBloatToken.go` - Go bindings \ No newline at end of file diff --git a/scenarios/statebloat/contract_deploy/contract/StateBloatToken.go b/scenarios/statebloat/contract_deploy/contract/StateBloatToken.go new file mode 100644 index 00000000..3021968a --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract/StateBloatToken.go @@ -0,0 +1,3360 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// StateBloatTokenMetaData contains all meta data concerning the StateBloatToken contract. +var StateBloatTokenMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_salt\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"PADDING_DATA\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy1\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy10\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy11\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy12\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy13\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy14\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy15\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy16\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy17\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy18\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy19\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy2\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy20\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy21\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy22\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy23\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy24\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy25\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy26\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy27\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy28\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy29\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy3\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy30\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy31\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy32\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy33\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy34\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy35\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy36\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy37\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy38\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy39\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy4\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy40\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy41\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy42\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy43\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy44\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy45\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy46\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy47\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy48\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy49\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy5\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy50\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy51\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy52\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy53\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy54\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy55\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy56\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy57\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy58\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy59\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy6\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy60\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy61\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy62\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy63\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy64\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy65\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy66\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy67\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy68\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy69\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy7\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy8\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy9\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"salt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom1\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom10\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom11\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom12\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom13\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom14\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom15\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom16\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom17\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom18\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom19\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom2\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom3\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom4\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom5\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom6\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom7\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom8\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom9\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a060405234801562000010575f80fd5b50604051620016f1380380620016f1833981016040819052620000339162000118565b60408051808201909152601181527029ba30ba3290213637b0ba102a37b5b2b760791b60208201525f90620000699082620001ce565b5060408051808201909152600381526214d09560ea1b6020820152600190620000939082620001ce565b506002805460ff191660129081179091556080829052620000b690600a620003a9565b620000c590620f4240620003c0565b6003819055335f81815260046020908152604080832085905551938452919290917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a350620003da565b5f6020828403121562000129575f80fd5b5051919050565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200015957607f821691505b6020821081036200017857634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620001c957805f5260205f20601f840160051c81016020851015620001a55750805b601f840160051c820191505b81811015620001c6575f8155600101620001b1565b50505b505050565b81516001600160401b03811115620001ea57620001ea62000130565b6200020281620001fb845462000144565b846200017e565b602080601f83116001811462000238575f8415620002205750858301515b5f19600386901b1c1916600185901b17855562000292565b5f85815260208120601f198616915b82811015620002685788860151825594840194600190910190840162000247565b50858210156200028657878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b600181815b80851115620002ee57815f1904821115620002d257620002d26200029a565b80851615620002e057918102915b93841c9390800290620002b3565b509250929050565b5f826200030657506001620003a3565b816200031457505f620003a3565b81600181146200032d5760028114620003385762000358565b6001915050620003a3565b60ff8411156200034c576200034c6200029a565b50506001821b620003a3565b5060208310610133831016604e8410600b84101617156200037d575081810a620003a3565b620003898383620002ae565b805f19048211156200039f576200039f6200029a565b0290505b92915050565b5f620003b960ff841683620002f6565b9392505050565b8082028115828204841417620003a357620003a36200029a565b6080516112fe620003f35f395f61089001526112fe5ff3fe608060405234801561000f575f80fd5b5060043610610630575f3560e01c8063657b6ef711610333578063ad8f4221116101b3578063d9bb3174116100fe578063ee1682b6116100a9578063f8716f1411610084578063f8716f141461093d578063faf35ced14610634578063fe7d599614610944578063ffbf04691461094b575f80fd5b8063ee1682b614610927578063f26c779b1461092f578063f5f5738114610936575f80fd5b8063e2d27530116100d9578063e2d2753014610906578063e8c927b31461090d578063eb4329c814610914575f80fd5b8063d9bb3174146108ce578063dc1d8a9b146108d5578063dd62ed3e146108dc575f80fd5b8063bfa0b1331161015e578063cfd6686311610139578063cfd66863146108b9578063d101dcd0146108c0578063d7419469146108c7575f80fd5b8063bfa0b1331461088b578063c2be97e314610634578063c958d4bf146108b2575f80fd5b8063b66dd7501161018e578063b66dd7501461087d578063b9a6d64514610884578063bb9bfe0614610634575f80fd5b8063ad8f42211461086f578063b1802b9a14610634578063b2bb360e14610876575f80fd5b80637dffdc321161027e57806395d89b4111610229578063a891d4d411610204578063a891d4d41461084e578063a9059cbb14610855578063aaa7af7014610868578063acc5aee914610634575f80fd5b806395d89b41146108385780639c5dfe73146108405780639df61a2514610847575f80fd5b80638619d607116102595780638619d6071461082a5780638789ca67146106345780638f4a840614610831575f80fd5b80637dffdc32146108155780637e5449371461081c5780637f34d94b14610823575f80fd5b806374f83d02116102de5780637a319c18116102b95780637a319c18146108075780637c66673e146106345780637c72ed0d1461080e575f80fd5b806374f83d02146107f257806377c0209e146107f9578063792c7f3e14610800575f80fd5b80636c12ed281161030e5780636c12ed281461063457806370a08231146107cc57806374e73fd3146107eb575f80fd5b8063657b6ef714610707578063672151fe146107be5780636abceacd146107c5575f80fd5b80633125f37a116104be5780634a2e93c611610409578063552a1b56116103b457806361b970eb1161038f57806361b970eb146107a2578063639ec53a146107a957806365473174146107b05780636578534c146107b7575f80fd5b8063552a1b561461063457806358b6a9bd146107945780635af92c051461079b575f80fd5b80634f5e5557116103e45780634f5e5557146107865780634f7bd75a1461063457806354c279201461078d575f80fd5b80634a2e93c6146107715780634b3c7f5f146107785780634e1dbb821461077f575f80fd5b80633ea117ce1161046957806342937dbd1161044457806342937dbd1461076357806343a6b92d1461076a57806344050a2814610634575f80fd5b80633ea117ce1461074e5780634128a85d14610755578063418b18161461075c575f80fd5b806339e0bd121161049957806339e0bd12146106345780633a131990146106345780633b6be45914610747575f80fd5b80633125f37a1461071a578063313ce5671461072157806334517f0b14610740575f80fd5b80631b17c65c1161057e57806321ecd7a3116105295780632545d8b7116105045780632545d8b7146106f95780632787325b14610700578063291c3bd714610707575f80fd5b806321ecd7a3146106eb578063239af2a5146106f257806323b872dd14610634575f80fd5b80631eaa7c52116105595780631eaa7c52146106d65780631f449589146106dd5780631fd298ec146106e4575f80fd5b80631b17c65c146106345780631bbffe6f146106345780631d527cde146106cf575f80fd5b806312901b42116105de57806318160ddd116105b957806318160ddd146106b857806319cf6a91146106c15780631a97f18e146106c8575f80fd5b806312901b42146106a357806313ebb5ec146106aa57806316a3045b146106b1575f80fd5b8063095ea7b31161060e578063095ea7b3146106825780630cb7a9e7146106955780631215a3ab1461069c575f80fd5b80630460faf61461063457806306fdde031461065c5780630717b16114610671575b5f80fd5b610647610642366004610fd2565b610952565b60405190151581526020015b60405180910390f35b610664610ba7565b604051610653919061100b565b602f5b604051908152602001610653565b610647610690366004611075565b610c32565b601b610674565b6005610674565b601a610674565b601d610674565b6033610674565b61067460035481565b6008610674565b6003610674565b6002610674565b6010610674565b6041610674565b6035610674565b602e610674565b602c610674565b6001610674565b6045610674565b610647610715366004610fd2565b610cab565b6034610674565b60025461072e9060ff1681565b60405160ff9091168152602001610653565b603c610674565b6004610674565b600c610674565b6006610674565b601c610674565b6032610674565b6023610674565b6011610674565b6021610674565b601f610674565b6026610674565b603a610674565b6009610674565b602a610674565b6014610674565b6025610674565b6013610674565b6043610674565b600d610674565b6017610674565b6106746107da36600461109d565b60046020525f908152604090205481565b6016610674565b6039610674565b603b610674565b6036610674565b603f610674565b602b610674565b6037610674565b600a610674565b602d610674565b6030610674565b600b610674565b610664610dd2565b6015610674565b6029610674565b6038610674565b610647610863366004611075565b610ddf565b6028610674565b6040610674565b6018610674565b600f610674565b6044610674565b6106747f000000000000000000000000000000000000000000000000000000000000000081565b6024610674565b601e610674565b6031610674565b6019610674565b6042610674565b6027610674565b6106746108ea3660046110bd565b600560209081525f928352604080842090915290825290205481565b603d610674565b6022610674565b610647610922366004610fd2565b610efd565b610664610f8b565b6007610674565b603e610674565b6020610674565b600e610674565b6012610674565b73ffffffffffffffffffffffffffffffffffffffff83165f908152600460205260408120548211156109e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e73756666696369656e742062616c616e636500000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff84165f908152600560209081526040808320338452909152902054821115610a7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e73756666696369656e7420616c6c6f77616e63650000000000000000000060448201526064016109dc565b73ffffffffffffffffffffffffffffffffffffffff84165f9081526004602052604081208054849290610ab290849061111b565b909155505073ffffffffffffffffffffffffffffffffffffffff83165f9081526004602052604081208054849290610aeb90849061112e565b909155505073ffffffffffffffffffffffffffffffffffffffff84165f90815260056020908152604080832033845290915281208054849290610b2f90849061111b565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b9591815260200190565b60405180910390a35060019392505050565b5f8054610bb390611141565b80601f0160208091040260200160405190810160405280929190818152602001828054610bdf90611141565b8015610c2a5780601f10610c0157610100808354040283529160200191610c2a565b820191905f5260205f20905b815481529060010190602001808311610c0d57829003601f168201915b505050505081565b335f81815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610c999086815260200190565b60405180910390a35060015b92915050565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260046020526040812054821115610d39576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f410000000000000000000000000000000000000000000000000000000000000060448201526064016109dc565b73ffffffffffffffffffffffffffffffffffffffff84165f908152600560209081526040808320338452909152902054821115610a7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f420000000000000000000000000000000000000000000000000000000000000060448201526064016109dc565b60018054610bb390611141565b335f90815260046020526040812054821115610e57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e73756666696369656e742062616c616e636500000000000000000000000060448201526064016109dc565b335f9081526004602052604081208054849290610e7590849061111b565b909155505073ffffffffffffffffffffffffffffffffffffffff83165f9081526004602052604081208054849290610eae90849061112e565b909155505060405182815273ffffffffffffffffffffffffffffffffffffffff84169033907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610c99565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260046020526040812054821115610a7e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600160248201527f410000000000000000000000000000000000000000000000000000000000000060448201526064016109dc565b6040518061016001604052806101368152602001611193610136913981565b803573ffffffffffffffffffffffffffffffffffffffff81168114610fcd575f80fd5b919050565b5f805f60608486031215610fe4575f80fd5b610fed84610faa565b9250610ffb60208501610faa565b9150604084013590509250925092565b5f602080835283518060208501525f5b818110156110375785810183015185820160400152820161101b565b505f6040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b5f8060408385031215611086575f80fd5b61108f83610faa565b946020939093013593505050565b5f602082840312156110ad575f80fd5b6110b682610faa565b9392505050565b5f80604083850312156110ce575f80fd5b6110d783610faa565b91506110e560208401610faa565b90509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610ca557610ca56110ee565b80820180821115610ca557610ca56110ee565b600181811c9082168061115557607f821691505b60208210810361118c577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5091905056fe41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141a2646970667358221220c70035169e4d38ee509e85ab2f5566c500caae43b66c105f4552e44745217d5f64736f6c63430008160033", +} + +// StateBloatTokenABI is the input ABI used to generate the binding from. +// Deprecated: Use StateBloatTokenMetaData.ABI instead. +var StateBloatTokenABI = StateBloatTokenMetaData.ABI + +// StateBloatTokenBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use StateBloatTokenMetaData.Bin instead. +var StateBloatTokenBin = StateBloatTokenMetaData.Bin + +// DeployStateBloatToken deploys a new Ethereum contract, binding an instance of StateBloatToken to it. +func DeployStateBloatToken(auth *bind.TransactOpts, backend bind.ContractBackend, _salt *big.Int) (common.Address, *types.Transaction, *StateBloatToken, error) { + parsed, err := StateBloatTokenMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(StateBloatTokenBin), backend, _salt) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &StateBloatToken{StateBloatTokenCaller: StateBloatTokenCaller{contract: contract}, StateBloatTokenTransactor: StateBloatTokenTransactor{contract: contract}, StateBloatTokenFilterer: StateBloatTokenFilterer{contract: contract}}, nil +} + +// StateBloatToken is an auto generated Go binding around an Ethereum contract. +type StateBloatToken struct { + StateBloatTokenCaller // Read-only binding to the contract + StateBloatTokenTransactor // Write-only binding to the contract + StateBloatTokenFilterer // Log filterer for contract events +} + +// StateBloatTokenCaller is an auto generated read-only Go binding around an Ethereum contract. +type StateBloatTokenCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// StateBloatTokenTransactor is an auto generated write-only Go binding around an Ethereum contract. +type StateBloatTokenTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// StateBloatTokenFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type StateBloatTokenFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// StateBloatTokenSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type StateBloatTokenSession struct { + Contract *StateBloatToken // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// StateBloatTokenCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type StateBloatTokenCallerSession struct { + Contract *StateBloatTokenCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// StateBloatTokenTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type StateBloatTokenTransactorSession struct { + Contract *StateBloatTokenTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// StateBloatTokenRaw is an auto generated low-level Go binding around an Ethereum contract. +type StateBloatTokenRaw struct { + Contract *StateBloatToken // Generic contract binding to access the raw methods on +} + +// StateBloatTokenCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type StateBloatTokenCallerRaw struct { + Contract *StateBloatTokenCaller // Generic read-only contract binding to access the raw methods on +} + +// StateBloatTokenTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type StateBloatTokenTransactorRaw struct { + Contract *StateBloatTokenTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewStateBloatToken creates a new instance of StateBloatToken, bound to a specific deployed contract. +func NewStateBloatToken(address common.Address, backend bind.ContractBackend) (*StateBloatToken, error) { + contract, err := bindStateBloatToken(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &StateBloatToken{StateBloatTokenCaller: StateBloatTokenCaller{contract: contract}, StateBloatTokenTransactor: StateBloatTokenTransactor{contract: contract}, StateBloatTokenFilterer: StateBloatTokenFilterer{contract: contract}}, nil +} + +// NewStateBloatTokenCaller creates a new read-only instance of StateBloatToken, bound to a specific deployed contract. +func NewStateBloatTokenCaller(address common.Address, caller bind.ContractCaller) (*StateBloatTokenCaller, error) { + contract, err := bindStateBloatToken(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &StateBloatTokenCaller{contract: contract}, nil +} + +// NewStateBloatTokenTransactor creates a new write-only instance of StateBloatToken, bound to a specific deployed contract. +func NewStateBloatTokenTransactor(address common.Address, transactor bind.ContractTransactor) (*StateBloatTokenTransactor, error) { + contract, err := bindStateBloatToken(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &StateBloatTokenTransactor{contract: contract}, nil +} + +// NewStateBloatTokenFilterer creates a new log filterer instance of StateBloatToken, bound to a specific deployed contract. +func NewStateBloatTokenFilterer(address common.Address, filterer bind.ContractFilterer) (*StateBloatTokenFilterer, error) { + contract, err := bindStateBloatToken(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &StateBloatTokenFilterer{contract: contract}, nil +} + +// bindStateBloatToken binds a generic wrapper to an already deployed contract. +func bindStateBloatToken(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := StateBloatTokenMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_StateBloatToken *StateBloatTokenRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _StateBloatToken.Contract.StateBloatTokenCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_StateBloatToken *StateBloatTokenRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _StateBloatToken.Contract.StateBloatTokenTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_StateBloatToken *StateBloatTokenRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _StateBloatToken.Contract.StateBloatTokenTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_StateBloatToken *StateBloatTokenCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _StateBloatToken.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_StateBloatToken *StateBloatTokenTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _StateBloatToken.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_StateBloatToken *StateBloatTokenTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _StateBloatToken.Contract.contract.Transact(opts, method, params...) +} + +// PADDINGDATA is a free data retrieval call binding the contract method 0xee1682b6. +// +// Solidity: function PADDING_DATA() view returns(string) +func (_StateBloatToken *StateBloatTokenCaller) PADDINGDATA(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "PADDING_DATA") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// PADDINGDATA is a free data retrieval call binding the contract method 0xee1682b6. +// +// Solidity: function PADDING_DATA() view returns(string) +func (_StateBloatToken *StateBloatTokenSession) PADDINGDATA() (string, error) { + return _StateBloatToken.Contract.PADDINGDATA(&_StateBloatToken.CallOpts) +} + +// PADDINGDATA is a free data retrieval call binding the contract method 0xee1682b6. +// +// Solidity: function PADDING_DATA() view returns(string) +func (_StateBloatToken *StateBloatTokenCallerSession) PADDINGDATA() (string, error) { + return _StateBloatToken.Contract.PADDINGDATA(&_StateBloatToken.CallOpts) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Allowance(opts *bind.CallOpts, arg0 common.Address, arg1 common.Address) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "allowance", arg0, arg1) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _StateBloatToken.Contract.Allowance(&_StateBloatToken.CallOpts, arg0, arg1) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address , address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Allowance(arg0 common.Address, arg1 common.Address) (*big.Int, error) { + return _StateBloatToken.Contract.Allowance(&_StateBloatToken.CallOpts, arg0, arg1) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) BalanceOf(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "balanceOf", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _StateBloatToken.Contract.BalanceOf(&_StateBloatToken.CallOpts, arg0) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address ) view returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) BalanceOf(arg0 common.Address) (*big.Int, error) { + return _StateBloatToken.Contract.BalanceOf(&_StateBloatToken.CallOpts, arg0) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_StateBloatToken *StateBloatTokenCaller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_StateBloatToken *StateBloatTokenSession) Decimals() (uint8, error) { + return _StateBloatToken.Contract.Decimals(&_StateBloatToken.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_StateBloatToken *StateBloatTokenCallerSession) Decimals() (uint8, error) { + return _StateBloatToken.Contract.Decimals(&_StateBloatToken.CallOpts) +} + +// Dummy1 is a free data retrieval call binding the contract method 0x2545d8b7. +// +// Solidity: function dummy1() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy1(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy1") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy1 is a free data retrieval call binding the contract method 0x2545d8b7. +// +// Solidity: function dummy1() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy1() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy1(&_StateBloatToken.CallOpts) +} + +// Dummy1 is a free data retrieval call binding the contract method 0x2545d8b7. +// +// Solidity: function dummy1() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy1() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy1(&_StateBloatToken.CallOpts) +} + +// Dummy10 is a free data retrieval call binding the contract method 0x7e544937. +// +// Solidity: function dummy10() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy10(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy10") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy10 is a free data retrieval call binding the contract method 0x7e544937. +// +// Solidity: function dummy10() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy10() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy10(&_StateBloatToken.CallOpts) +} + +// Dummy10 is a free data retrieval call binding the contract method 0x7e544937. +// +// Solidity: function dummy10() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy10() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy10(&_StateBloatToken.CallOpts) +} + +// Dummy11 is a free data retrieval call binding the contract method 0x8f4a8406. +// +// Solidity: function dummy11() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy11(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy11") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy11 is a free data retrieval call binding the contract method 0x8f4a8406. +// +// Solidity: function dummy11() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy11() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy11(&_StateBloatToken.CallOpts) +} + +// Dummy11 is a free data retrieval call binding the contract method 0x8f4a8406. +// +// Solidity: function dummy11() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy11() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy11(&_StateBloatToken.CallOpts) +} + +// Dummy12 is a free data retrieval call binding the contract method 0x3ea117ce. +// +// Solidity: function dummy12() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy12(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy12") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy12 is a free data retrieval call binding the contract method 0x3ea117ce. +// +// Solidity: function dummy12() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy12() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy12(&_StateBloatToken.CallOpts) +} + +// Dummy12 is a free data retrieval call binding the contract method 0x3ea117ce. +// +// Solidity: function dummy12() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy12() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy12(&_StateBloatToken.CallOpts) +} + +// Dummy13 is a free data retrieval call binding the contract method 0x672151fe. +// +// Solidity: function dummy13() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy13(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy13") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy13 is a free data retrieval call binding the contract method 0x672151fe. +// +// Solidity: function dummy13() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy13() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy13(&_StateBloatToken.CallOpts) +} + +// Dummy13 is a free data retrieval call binding the contract method 0x672151fe. +// +// Solidity: function dummy13() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy13() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy13(&_StateBloatToken.CallOpts) +} + +// Dummy14 is a free data retrieval call binding the contract method 0xfe7d5996. +// +// Solidity: function dummy14() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy14(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy14") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy14 is a free data retrieval call binding the contract method 0xfe7d5996. +// +// Solidity: function dummy14() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy14() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy14(&_StateBloatToken.CallOpts) +} + +// Dummy14 is a free data retrieval call binding the contract method 0xfe7d5996. +// +// Solidity: function dummy14() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy14() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy14(&_StateBloatToken.CallOpts) +} + +// Dummy15 is a free data retrieval call binding the contract method 0xb66dd750. +// +// Solidity: function dummy15() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy15(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy15") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy15 is a free data retrieval call binding the contract method 0xb66dd750. +// +// Solidity: function dummy15() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy15() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy15(&_StateBloatToken.CallOpts) +} + +// Dummy15 is a free data retrieval call binding the contract method 0xb66dd750. +// +// Solidity: function dummy15() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy15() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy15(&_StateBloatToken.CallOpts) +} + +// Dummy16 is a free data retrieval call binding the contract method 0x1eaa7c52. +// +// Solidity: function dummy16() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy16(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy16") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy16 is a free data retrieval call binding the contract method 0x1eaa7c52. +// +// Solidity: function dummy16() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy16() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy16(&_StateBloatToken.CallOpts) +} + +// Dummy16 is a free data retrieval call binding the contract method 0x1eaa7c52. +// +// Solidity: function dummy16() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy16() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy16(&_StateBloatToken.CallOpts) +} + +// Dummy17 is a free data retrieval call binding the contract method 0x4a2e93c6. +// +// Solidity: function dummy17() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy17(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy17") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy17 is a free data retrieval call binding the contract method 0x4a2e93c6. +// +// Solidity: function dummy17() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy17() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy17(&_StateBloatToken.CallOpts) +} + +// Dummy17 is a free data retrieval call binding the contract method 0x4a2e93c6. +// +// Solidity: function dummy17() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy17() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy17(&_StateBloatToken.CallOpts) +} + +// Dummy18 is a free data retrieval call binding the contract method 0xffbf0469. +// +// Solidity: function dummy18() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy18(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy18") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy18 is a free data retrieval call binding the contract method 0xffbf0469. +// +// Solidity: function dummy18() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy18() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy18(&_StateBloatToken.CallOpts) +} + +// Dummy18 is a free data retrieval call binding the contract method 0xffbf0469. +// +// Solidity: function dummy18() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy18() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy18(&_StateBloatToken.CallOpts) +} + +// Dummy19 is a free data retrieval call binding the contract method 0x65473174. +// +// Solidity: function dummy19() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy19(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy19") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy19 is a free data retrieval call binding the contract method 0x65473174. +// +// Solidity: function dummy19() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy19() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy19(&_StateBloatToken.CallOpts) +} + +// Dummy19 is a free data retrieval call binding the contract method 0x65473174. +// +// Solidity: function dummy19() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy19() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy19(&_StateBloatToken.CallOpts) +} + +// Dummy2 is a free data retrieval call binding the contract method 0x1d527cde. +// +// Solidity: function dummy2() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy2(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy2") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy2 is a free data retrieval call binding the contract method 0x1d527cde. +// +// Solidity: function dummy2() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy2() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy2(&_StateBloatToken.CallOpts) +} + +// Dummy2 is a free data retrieval call binding the contract method 0x1d527cde. +// +// Solidity: function dummy2() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy2() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy2(&_StateBloatToken.CallOpts) +} + +// Dummy20 is a free data retrieval call binding the contract method 0x61b970eb. +// +// Solidity: function dummy20() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy20(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy20") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy20 is a free data retrieval call binding the contract method 0x61b970eb. +// +// Solidity: function dummy20() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy20() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy20(&_StateBloatToken.CallOpts) +} + +// Dummy20 is a free data retrieval call binding the contract method 0x61b970eb. +// +// Solidity: function dummy20() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy20() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy20(&_StateBloatToken.CallOpts) +} + +// Dummy21 is a free data retrieval call binding the contract method 0x9c5dfe73. +// +// Solidity: function dummy21() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy21(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy21") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy21 is a free data retrieval call binding the contract method 0x9c5dfe73. +// +// Solidity: function dummy21() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy21() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy21(&_StateBloatToken.CallOpts) +} + +// Dummy21 is a free data retrieval call binding the contract method 0x9c5dfe73. +// +// Solidity: function dummy21() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy21() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy21(&_StateBloatToken.CallOpts) +} + +// Dummy22 is a free data retrieval call binding the contract method 0x74e73fd3. +// +// Solidity: function dummy22() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy22(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy22") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy22 is a free data retrieval call binding the contract method 0x74e73fd3. +// +// Solidity: function dummy22() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy22() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy22(&_StateBloatToken.CallOpts) +} + +// Dummy22 is a free data retrieval call binding the contract method 0x74e73fd3. +// +// Solidity: function dummy22() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy22() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy22(&_StateBloatToken.CallOpts) +} + +// Dummy23 is a free data retrieval call binding the contract method 0x6abceacd. +// +// Solidity: function dummy23() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy23(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy23") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy23 is a free data retrieval call binding the contract method 0x6abceacd. +// +// Solidity: function dummy23() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy23() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy23(&_StateBloatToken.CallOpts) +} + +// Dummy23 is a free data retrieval call binding the contract method 0x6abceacd. +// +// Solidity: function dummy23() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy23() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy23(&_StateBloatToken.CallOpts) +} + +// Dummy24 is a free data retrieval call binding the contract method 0xb2bb360e. +// +// Solidity: function dummy24() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy24(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy24") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy24 is a free data retrieval call binding the contract method 0xb2bb360e. +// +// Solidity: function dummy24() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy24() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy24(&_StateBloatToken.CallOpts) +} + +// Dummy24 is a free data retrieval call binding the contract method 0xb2bb360e. +// +// Solidity: function dummy24() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy24() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy24(&_StateBloatToken.CallOpts) +} + +// Dummy25 is a free data retrieval call binding the contract method 0xd7419469. +// +// Solidity: function dummy25() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy25(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy25") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy25 is a free data retrieval call binding the contract method 0xd7419469. +// +// Solidity: function dummy25() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy25() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy25(&_StateBloatToken.CallOpts) +} + +// Dummy25 is a free data retrieval call binding the contract method 0xd7419469. +// +// Solidity: function dummy25() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy25() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy25(&_StateBloatToken.CallOpts) +} + +// Dummy26 is a free data retrieval call binding the contract method 0x12901b42. +// +// Solidity: function dummy26() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy26(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy26") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy26 is a free data retrieval call binding the contract method 0x12901b42. +// +// Solidity: function dummy26() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy26() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy26(&_StateBloatToken.CallOpts) +} + +// Dummy26 is a free data retrieval call binding the contract method 0x12901b42. +// +// Solidity: function dummy26() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy26() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy26(&_StateBloatToken.CallOpts) +} + +// Dummy27 is a free data retrieval call binding the contract method 0x0cb7a9e7. +// +// Solidity: function dummy27() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy27(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy27") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy27 is a free data retrieval call binding the contract method 0x0cb7a9e7. +// +// Solidity: function dummy27() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy27() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy27(&_StateBloatToken.CallOpts) +} + +// Dummy27 is a free data retrieval call binding the contract method 0x0cb7a9e7. +// +// Solidity: function dummy27() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy27() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy27(&_StateBloatToken.CallOpts) +} + +// Dummy28 is a free data retrieval call binding the contract method 0x418b1816. +// +// Solidity: function dummy28() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy28(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy28") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy28 is a free data retrieval call binding the contract method 0x418b1816. +// +// Solidity: function dummy28() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy28() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy28(&_StateBloatToken.CallOpts) +} + +// Dummy28 is a free data retrieval call binding the contract method 0x418b1816. +// +// Solidity: function dummy28() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy28() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy28(&_StateBloatToken.CallOpts) +} + +// Dummy29 is a free data retrieval call binding the contract method 0x13ebb5ec. +// +// Solidity: function dummy29() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy29(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy29") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy29 is a free data retrieval call binding the contract method 0x13ebb5ec. +// +// Solidity: function dummy29() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy29() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy29(&_StateBloatToken.CallOpts) +} + +// Dummy29 is a free data retrieval call binding the contract method 0x13ebb5ec. +// +// Solidity: function dummy29() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy29() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy29(&_StateBloatToken.CallOpts) +} + +// Dummy3 is a free data retrieval call binding the contract method 0x1a97f18e. +// +// Solidity: function dummy3() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy3(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy3") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy3 is a free data retrieval call binding the contract method 0x1a97f18e. +// +// Solidity: function dummy3() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy3() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy3(&_StateBloatToken.CallOpts) +} + +// Dummy3 is a free data retrieval call binding the contract method 0x1a97f18e. +// +// Solidity: function dummy3() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy3() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy3(&_StateBloatToken.CallOpts) +} + +// Dummy30 is a free data retrieval call binding the contract method 0xcfd66863. +// +// Solidity: function dummy30() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy30(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy30") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy30 is a free data retrieval call binding the contract method 0xcfd66863. +// +// Solidity: function dummy30() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy30() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy30(&_StateBloatToken.CallOpts) +} + +// Dummy30 is a free data retrieval call binding the contract method 0xcfd66863. +// +// Solidity: function dummy30() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy30() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy30(&_StateBloatToken.CallOpts) +} + +// Dummy31 is a free data retrieval call binding the contract method 0x4e1dbb82. +// +// Solidity: function dummy31() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy31(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy31") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy31 is a free data retrieval call binding the contract method 0x4e1dbb82. +// +// Solidity: function dummy31() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy31() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy31(&_StateBloatToken.CallOpts) +} + +// Dummy31 is a free data retrieval call binding the contract method 0x4e1dbb82. +// +// Solidity: function dummy31() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy31() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy31(&_StateBloatToken.CallOpts) +} + +// Dummy32 is a free data retrieval call binding the contract method 0xf8716f14. +// +// Solidity: function dummy32() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy32(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy32") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy32 is a free data retrieval call binding the contract method 0xf8716f14. +// +// Solidity: function dummy32() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy32() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy32(&_StateBloatToken.CallOpts) +} + +// Dummy32 is a free data retrieval call binding the contract method 0xf8716f14. +// +// Solidity: function dummy32() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy32() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy32(&_StateBloatToken.CallOpts) +} + +// Dummy33 is a free data retrieval call binding the contract method 0x4b3c7f5f. +// +// Solidity: function dummy33() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy33(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy33") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy33 is a free data retrieval call binding the contract method 0x4b3c7f5f. +// +// Solidity: function dummy33() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy33() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy33(&_StateBloatToken.CallOpts) +} + +// Dummy33 is a free data retrieval call binding the contract method 0x4b3c7f5f. +// +// Solidity: function dummy33() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy33() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy33(&_StateBloatToken.CallOpts) +} + +// Dummy34 is a free data retrieval call binding the contract method 0xe8c927b3. +// +// Solidity: function dummy34() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy34(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy34") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy34 is a free data retrieval call binding the contract method 0xe8c927b3. +// +// Solidity: function dummy34() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy34() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy34(&_StateBloatToken.CallOpts) +} + +// Dummy34 is a free data retrieval call binding the contract method 0xe8c927b3. +// +// Solidity: function dummy34() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy34() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy34(&_StateBloatToken.CallOpts) +} + +// Dummy35 is a free data retrieval call binding the contract method 0x43a6b92d. +// +// Solidity: function dummy35() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy35(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy35") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy35 is a free data retrieval call binding the contract method 0x43a6b92d. +// +// Solidity: function dummy35() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy35() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy35(&_StateBloatToken.CallOpts) +} + +// Dummy35 is a free data retrieval call binding the contract method 0x43a6b92d. +// +// Solidity: function dummy35() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy35() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy35(&_StateBloatToken.CallOpts) +} + +// Dummy36 is a free data retrieval call binding the contract method 0xc958d4bf. +// +// Solidity: function dummy36() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy36(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy36") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy36 is a free data retrieval call binding the contract method 0xc958d4bf. +// +// Solidity: function dummy36() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy36() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy36(&_StateBloatToken.CallOpts) +} + +// Dummy36 is a free data retrieval call binding the contract method 0xc958d4bf. +// +// Solidity: function dummy36() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy36() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy36(&_StateBloatToken.CallOpts) +} + +// Dummy37 is a free data retrieval call binding the contract method 0x639ec53a. +// +// Solidity: function dummy37() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy37(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy37") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy37 is a free data retrieval call binding the contract method 0x639ec53a. +// +// Solidity: function dummy37() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy37() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy37(&_StateBloatToken.CallOpts) +} + +// Dummy37 is a free data retrieval call binding the contract method 0x639ec53a. +// +// Solidity: function dummy37() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy37() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy37(&_StateBloatToken.CallOpts) +} + +// Dummy38 is a free data retrieval call binding the contract method 0x4f5e5557. +// +// Solidity: function dummy38() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy38(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy38") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy38 is a free data retrieval call binding the contract method 0x4f5e5557. +// +// Solidity: function dummy38() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy38() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy38(&_StateBloatToken.CallOpts) +} + +// Dummy38 is a free data retrieval call binding the contract method 0x4f5e5557. +// +// Solidity: function dummy38() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy38() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy38(&_StateBloatToken.CallOpts) +} + +// Dummy39 is a free data retrieval call binding the contract method 0xdc1d8a9b. +// +// Solidity: function dummy39() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy39(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy39") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy39 is a free data retrieval call binding the contract method 0xdc1d8a9b. +// +// Solidity: function dummy39() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy39() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy39(&_StateBloatToken.CallOpts) +} + +// Dummy39 is a free data retrieval call binding the contract method 0xdc1d8a9b. +// +// Solidity: function dummy39() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy39() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy39(&_StateBloatToken.CallOpts) +} + +// Dummy4 is a free data retrieval call binding the contract method 0x3b6be459. +// +// Solidity: function dummy4() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy4(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy4") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy4 is a free data retrieval call binding the contract method 0x3b6be459. +// +// Solidity: function dummy4() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy4() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy4(&_StateBloatToken.CallOpts) +} + +// Dummy4 is a free data retrieval call binding the contract method 0x3b6be459. +// +// Solidity: function dummy4() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy4() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy4(&_StateBloatToken.CallOpts) +} + +// Dummy40 is a free data retrieval call binding the contract method 0xaaa7af70. +// +// Solidity: function dummy40() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy40(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy40") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy40 is a free data retrieval call binding the contract method 0xaaa7af70. +// +// Solidity: function dummy40() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy40() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy40(&_StateBloatToken.CallOpts) +} + +// Dummy40 is a free data retrieval call binding the contract method 0xaaa7af70. +// +// Solidity: function dummy40() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy40() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy40(&_StateBloatToken.CallOpts) +} + +// Dummy41 is a free data retrieval call binding the contract method 0x9df61a25. +// +// Solidity: function dummy41() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy41(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy41") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy41 is a free data retrieval call binding the contract method 0x9df61a25. +// +// Solidity: function dummy41() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy41() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy41(&_StateBloatToken.CallOpts) +} + +// Dummy41 is a free data retrieval call binding the contract method 0x9df61a25. +// +// Solidity: function dummy41() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy41() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy41(&_StateBloatToken.CallOpts) +} + +// Dummy42 is a free data retrieval call binding the contract method 0x5af92c05. +// +// Solidity: function dummy42() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy42(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy42") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy42 is a free data retrieval call binding the contract method 0x5af92c05. +// +// Solidity: function dummy42() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy42() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy42(&_StateBloatToken.CallOpts) +} + +// Dummy42 is a free data retrieval call binding the contract method 0x5af92c05. +// +// Solidity: function dummy42() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy42() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy42(&_StateBloatToken.CallOpts) +} + +// Dummy43 is a free data retrieval call binding the contract method 0x7c72ed0d. +// +// Solidity: function dummy43() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy43(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy43") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy43 is a free data retrieval call binding the contract method 0x7c72ed0d. +// +// Solidity: function dummy43() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy43() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy43(&_StateBloatToken.CallOpts) +} + +// Dummy43 is a free data retrieval call binding the contract method 0x7c72ed0d. +// +// Solidity: function dummy43() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy43() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy43(&_StateBloatToken.CallOpts) +} + +// Dummy44 is a free data retrieval call binding the contract method 0x239af2a5. +// +// Solidity: function dummy44() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy44(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy44") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy44 is a free data retrieval call binding the contract method 0x239af2a5. +// +// Solidity: function dummy44() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy44() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy44(&_StateBloatToken.CallOpts) +} + +// Dummy44 is a free data retrieval call binding the contract method 0x239af2a5. +// +// Solidity: function dummy44() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy44() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy44(&_StateBloatToken.CallOpts) +} + +// Dummy45 is a free data retrieval call binding the contract method 0x7f34d94b. +// +// Solidity: function dummy45() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy45(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy45") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy45 is a free data retrieval call binding the contract method 0x7f34d94b. +// +// Solidity: function dummy45() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy45() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy45(&_StateBloatToken.CallOpts) +} + +// Dummy45 is a free data retrieval call binding the contract method 0x7f34d94b. +// +// Solidity: function dummy45() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy45() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy45(&_StateBloatToken.CallOpts) +} + +// Dummy46 is a free data retrieval call binding the contract method 0x21ecd7a3. +// +// Solidity: function dummy46() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy46(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy46") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy46 is a free data retrieval call binding the contract method 0x21ecd7a3. +// +// Solidity: function dummy46() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy46() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy46(&_StateBloatToken.CallOpts) +} + +// Dummy46 is a free data retrieval call binding the contract method 0x21ecd7a3. +// +// Solidity: function dummy46() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy46() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy46(&_StateBloatToken.CallOpts) +} + +// Dummy47 is a free data retrieval call binding the contract method 0x0717b161. +// +// Solidity: function dummy47() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy47(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy47") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy47 is a free data retrieval call binding the contract method 0x0717b161. +// +// Solidity: function dummy47() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy47() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy47(&_StateBloatToken.CallOpts) +} + +// Dummy47 is a free data retrieval call binding the contract method 0x0717b161. +// +// Solidity: function dummy47() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy47() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy47(&_StateBloatToken.CallOpts) +} + +// Dummy48 is a free data retrieval call binding the contract method 0x8619d607. +// +// Solidity: function dummy48() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy48(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy48") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy48 is a free data retrieval call binding the contract method 0x8619d607. +// +// Solidity: function dummy48() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy48() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy48(&_StateBloatToken.CallOpts) +} + +// Dummy48 is a free data retrieval call binding the contract method 0x8619d607. +// +// Solidity: function dummy48() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy48() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy48(&_StateBloatToken.CallOpts) +} + +// Dummy49 is a free data retrieval call binding the contract method 0xd101dcd0. +// +// Solidity: function dummy49() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy49(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy49") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy49 is a free data retrieval call binding the contract method 0xd101dcd0. +// +// Solidity: function dummy49() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy49() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy49(&_StateBloatToken.CallOpts) +} + +// Dummy49 is a free data retrieval call binding the contract method 0xd101dcd0. +// +// Solidity: function dummy49() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy49() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy49(&_StateBloatToken.CallOpts) +} + +// Dummy5 is a free data retrieval call binding the contract method 0x1215a3ab. +// +// Solidity: function dummy5() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy5(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy5") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy5 is a free data retrieval call binding the contract method 0x1215a3ab. +// +// Solidity: function dummy5() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy5() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy5(&_StateBloatToken.CallOpts) +} + +// Dummy5 is a free data retrieval call binding the contract method 0x1215a3ab. +// +// Solidity: function dummy5() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy5() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy5(&_StateBloatToken.CallOpts) +} + +// Dummy50 is a free data retrieval call binding the contract method 0x42937dbd. +// +// Solidity: function dummy50() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy50(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy50") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy50 is a free data retrieval call binding the contract method 0x42937dbd. +// +// Solidity: function dummy50() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy50() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy50(&_StateBloatToken.CallOpts) +} + +// Dummy50 is a free data retrieval call binding the contract method 0x42937dbd. +// +// Solidity: function dummy50() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy50() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy50(&_StateBloatToken.CallOpts) +} + +// Dummy51 is a free data retrieval call binding the contract method 0x16a3045b. +// +// Solidity: function dummy51() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy51(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy51") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy51 is a free data retrieval call binding the contract method 0x16a3045b. +// +// Solidity: function dummy51() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy51() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy51(&_StateBloatToken.CallOpts) +} + +// Dummy51 is a free data retrieval call binding the contract method 0x16a3045b. +// +// Solidity: function dummy51() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy51() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy51(&_StateBloatToken.CallOpts) +} + +// Dummy52 is a free data retrieval call binding the contract method 0x3125f37a. +// +// Solidity: function dummy52() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy52(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy52") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy52 is a free data retrieval call binding the contract method 0x3125f37a. +// +// Solidity: function dummy52() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy52() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy52(&_StateBloatToken.CallOpts) +} + +// Dummy52 is a free data retrieval call binding the contract method 0x3125f37a. +// +// Solidity: function dummy52() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy52() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy52(&_StateBloatToken.CallOpts) +} + +// Dummy53 is a free data retrieval call binding the contract method 0x1fd298ec. +// +// Solidity: function dummy53() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy53(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy53") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy53 is a free data retrieval call binding the contract method 0x1fd298ec. +// +// Solidity: function dummy53() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy53() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy53(&_StateBloatToken.CallOpts) +} + +// Dummy53 is a free data retrieval call binding the contract method 0x1fd298ec. +// +// Solidity: function dummy53() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy53() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy53(&_StateBloatToken.CallOpts) +} + +// Dummy54 is a free data retrieval call binding the contract method 0x792c7f3e. +// +// Solidity: function dummy54() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy54(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy54") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy54 is a free data retrieval call binding the contract method 0x792c7f3e. +// +// Solidity: function dummy54() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy54() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy54(&_StateBloatToken.CallOpts) +} + +// Dummy54 is a free data retrieval call binding the contract method 0x792c7f3e. +// +// Solidity: function dummy54() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy54() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy54(&_StateBloatToken.CallOpts) +} + +// Dummy55 is a free data retrieval call binding the contract method 0x7dffdc32. +// +// Solidity: function dummy55() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy55(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy55") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy55 is a free data retrieval call binding the contract method 0x7dffdc32. +// +// Solidity: function dummy55() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy55() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy55(&_StateBloatToken.CallOpts) +} + +// Dummy55 is a free data retrieval call binding the contract method 0x7dffdc32. +// +// Solidity: function dummy55() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy55() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy55(&_StateBloatToken.CallOpts) +} + +// Dummy56 is a free data retrieval call binding the contract method 0xa891d4d4. +// +// Solidity: function dummy56() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy56(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy56") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy56 is a free data retrieval call binding the contract method 0xa891d4d4. +// +// Solidity: function dummy56() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy56() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy56(&_StateBloatToken.CallOpts) +} + +// Dummy56 is a free data retrieval call binding the contract method 0xa891d4d4. +// +// Solidity: function dummy56() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy56() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy56(&_StateBloatToken.CallOpts) +} + +// Dummy57 is a free data retrieval call binding the contract method 0x74f83d02. +// +// Solidity: function dummy57() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy57(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy57") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy57 is a free data retrieval call binding the contract method 0x74f83d02. +// +// Solidity: function dummy57() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy57() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy57(&_StateBloatToken.CallOpts) +} + +// Dummy57 is a free data retrieval call binding the contract method 0x74f83d02. +// +// Solidity: function dummy57() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy57() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy57(&_StateBloatToken.CallOpts) +} + +// Dummy58 is a free data retrieval call binding the contract method 0x54c27920. +// +// Solidity: function dummy58() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy58(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy58") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy58 is a free data retrieval call binding the contract method 0x54c27920. +// +// Solidity: function dummy58() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy58() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy58(&_StateBloatToken.CallOpts) +} + +// Dummy58 is a free data retrieval call binding the contract method 0x54c27920. +// +// Solidity: function dummy58() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy58() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy58(&_StateBloatToken.CallOpts) +} + +// Dummy59 is a free data retrieval call binding the contract method 0x77c0209e. +// +// Solidity: function dummy59() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy59(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy59") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy59 is a free data retrieval call binding the contract method 0x77c0209e. +// +// Solidity: function dummy59() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy59() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy59(&_StateBloatToken.CallOpts) +} + +// Dummy59 is a free data retrieval call binding the contract method 0x77c0209e. +// +// Solidity: function dummy59() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy59() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy59(&_StateBloatToken.CallOpts) +} + +// Dummy6 is a free data retrieval call binding the contract method 0x4128a85d. +// +// Solidity: function dummy6() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy6(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy6") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy6 is a free data retrieval call binding the contract method 0x4128a85d. +// +// Solidity: function dummy6() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy6() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy6(&_StateBloatToken.CallOpts) +} + +// Dummy6 is a free data retrieval call binding the contract method 0x4128a85d. +// +// Solidity: function dummy6() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy6() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy6(&_StateBloatToken.CallOpts) +} + +// Dummy60 is a free data retrieval call binding the contract method 0x34517f0b. +// +// Solidity: function dummy60() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy60(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy60") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy60 is a free data retrieval call binding the contract method 0x34517f0b. +// +// Solidity: function dummy60() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy60() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy60(&_StateBloatToken.CallOpts) +} + +// Dummy60 is a free data retrieval call binding the contract method 0x34517f0b. +// +// Solidity: function dummy60() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy60() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy60(&_StateBloatToken.CallOpts) +} + +// Dummy61 is a free data retrieval call binding the contract method 0xe2d27530. +// +// Solidity: function dummy61() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy61(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy61") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy61 is a free data retrieval call binding the contract method 0xe2d27530. +// +// Solidity: function dummy61() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy61() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy61(&_StateBloatToken.CallOpts) +} + +// Dummy61 is a free data retrieval call binding the contract method 0xe2d27530. +// +// Solidity: function dummy61() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy61() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy61(&_StateBloatToken.CallOpts) +} + +// Dummy62 is a free data retrieval call binding the contract method 0xf5f57381. +// +// Solidity: function dummy62() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy62(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy62") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy62 is a free data retrieval call binding the contract method 0xf5f57381. +// +// Solidity: function dummy62() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy62() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy62(&_StateBloatToken.CallOpts) +} + +// Dummy62 is a free data retrieval call binding the contract method 0xf5f57381. +// +// Solidity: function dummy62() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy62() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy62(&_StateBloatToken.CallOpts) +} + +// Dummy63 is a free data retrieval call binding the contract method 0x7a319c18. +// +// Solidity: function dummy63() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy63(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy63") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy63 is a free data retrieval call binding the contract method 0x7a319c18. +// +// Solidity: function dummy63() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy63() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy63(&_StateBloatToken.CallOpts) +} + +// Dummy63 is a free data retrieval call binding the contract method 0x7a319c18. +// +// Solidity: function dummy63() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy63() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy63(&_StateBloatToken.CallOpts) +} + +// Dummy64 is a free data retrieval call binding the contract method 0xad8f4221. +// +// Solidity: function dummy64() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy64(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy64") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy64 is a free data retrieval call binding the contract method 0xad8f4221. +// +// Solidity: function dummy64() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy64() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy64(&_StateBloatToken.CallOpts) +} + +// Dummy64 is a free data retrieval call binding the contract method 0xad8f4221. +// +// Solidity: function dummy64() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy64() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy64(&_StateBloatToken.CallOpts) +} + +// Dummy65 is a free data retrieval call binding the contract method 0x1f449589. +// +// Solidity: function dummy65() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy65(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy65") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy65 is a free data retrieval call binding the contract method 0x1f449589. +// +// Solidity: function dummy65() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy65() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy65(&_StateBloatToken.CallOpts) +} + +// Dummy65 is a free data retrieval call binding the contract method 0x1f449589. +// +// Solidity: function dummy65() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy65() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy65(&_StateBloatToken.CallOpts) +} + +// Dummy66 is a free data retrieval call binding the contract method 0xd9bb3174. +// +// Solidity: function dummy66() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy66(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy66") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy66 is a free data retrieval call binding the contract method 0xd9bb3174. +// +// Solidity: function dummy66() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy66() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy66(&_StateBloatToken.CallOpts) +} + +// Dummy66 is a free data retrieval call binding the contract method 0xd9bb3174. +// +// Solidity: function dummy66() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy66() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy66(&_StateBloatToken.CallOpts) +} + +// Dummy67 is a free data retrieval call binding the contract method 0x6578534c. +// +// Solidity: function dummy67() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy67(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy67") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy67 is a free data retrieval call binding the contract method 0x6578534c. +// +// Solidity: function dummy67() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy67() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy67(&_StateBloatToken.CallOpts) +} + +// Dummy67 is a free data retrieval call binding the contract method 0x6578534c. +// +// Solidity: function dummy67() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy67() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy67(&_StateBloatToken.CallOpts) +} + +// Dummy68 is a free data retrieval call binding the contract method 0xb9a6d645. +// +// Solidity: function dummy68() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy68(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy68") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy68 is a free data retrieval call binding the contract method 0xb9a6d645. +// +// Solidity: function dummy68() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy68() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy68(&_StateBloatToken.CallOpts) +} + +// Dummy68 is a free data retrieval call binding the contract method 0xb9a6d645. +// +// Solidity: function dummy68() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy68() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy68(&_StateBloatToken.CallOpts) +} + +// Dummy69 is a free data retrieval call binding the contract method 0x2787325b. +// +// Solidity: function dummy69() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy69(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy69") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy69 is a free data retrieval call binding the contract method 0x2787325b. +// +// Solidity: function dummy69() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy69() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy69(&_StateBloatToken.CallOpts) +} + +// Dummy69 is a free data retrieval call binding the contract method 0x2787325b. +// +// Solidity: function dummy69() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy69() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy69(&_StateBloatToken.CallOpts) +} + +// Dummy7 is a free data retrieval call binding the contract method 0xf26c779b. +// +// Solidity: function dummy7() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy7(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy7") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy7 is a free data retrieval call binding the contract method 0xf26c779b. +// +// Solidity: function dummy7() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy7() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy7(&_StateBloatToken.CallOpts) +} + +// Dummy7 is a free data retrieval call binding the contract method 0xf26c779b. +// +// Solidity: function dummy7() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy7() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy7(&_StateBloatToken.CallOpts) +} + +// Dummy8 is a free data retrieval call binding the contract method 0x19cf6a91. +// +// Solidity: function dummy8() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy8(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy8") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy8 is a free data retrieval call binding the contract method 0x19cf6a91. +// +// Solidity: function dummy8() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy8() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy8(&_StateBloatToken.CallOpts) +} + +// Dummy8 is a free data retrieval call binding the contract method 0x19cf6a91. +// +// Solidity: function dummy8() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy8() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy8(&_StateBloatToken.CallOpts) +} + +// Dummy9 is a free data retrieval call binding the contract method 0x58b6a9bd. +// +// Solidity: function dummy9() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Dummy9(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "dummy9") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Dummy9 is a free data retrieval call binding the contract method 0x58b6a9bd. +// +// Solidity: function dummy9() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Dummy9() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy9(&_StateBloatToken.CallOpts) +} + +// Dummy9 is a free data retrieval call binding the contract method 0x58b6a9bd. +// +// Solidity: function dummy9() pure returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Dummy9() (*big.Int, error) { + return _StateBloatToken.Contract.Dummy9(&_StateBloatToken.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_StateBloatToken *StateBloatTokenCaller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_StateBloatToken *StateBloatTokenSession) Name() (string, error) { + return _StateBloatToken.Contract.Name(&_StateBloatToken.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_StateBloatToken *StateBloatTokenCallerSession) Name() (string, error) { + return _StateBloatToken.Contract.Name(&_StateBloatToken.CallOpts) +} + +// Salt is a free data retrieval call binding the contract method 0xbfa0b133. +// +// Solidity: function salt() view returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) Salt(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "salt") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Salt is a free data retrieval call binding the contract method 0xbfa0b133. +// +// Solidity: function salt() view returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) Salt() (*big.Int, error) { + return _StateBloatToken.Contract.Salt(&_StateBloatToken.CallOpts) +} + +// Salt is a free data retrieval call binding the contract method 0xbfa0b133. +// +// Solidity: function salt() view returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) Salt() (*big.Int, error) { + return _StateBloatToken.Contract.Salt(&_StateBloatToken.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_StateBloatToken *StateBloatTokenCaller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_StateBloatToken *StateBloatTokenSession) Symbol() (string, error) { + return _StateBloatToken.Contract.Symbol(&_StateBloatToken.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_StateBloatToken *StateBloatTokenCallerSession) Symbol() (string, error) { + return _StateBloatToken.Contract.Symbol(&_StateBloatToken.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_StateBloatToken *StateBloatTokenCaller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _StateBloatToken.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_StateBloatToken *StateBloatTokenSession) TotalSupply() (*big.Int, error) { + return _StateBloatToken.Contract.TotalSupply(&_StateBloatToken.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_StateBloatToken *StateBloatTokenCallerSession) TotalSupply() (*big.Int, error) { + return _StateBloatToken.Contract.TotalSupply(&_StateBloatToken.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "approve", spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.Approve(&_StateBloatToken.TransactOpts, spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.Approve(&_StateBloatToken.TransactOpts, spender, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transfer", to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.Transfer(&_StateBloatToken.TransactOpts, to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.Transfer(&_StateBloatToken.TransactOpts, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom", from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom1 is a paid mutator transaction binding the contract method 0xbb9bfe06. +// +// Solidity: function transferFrom1(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom1(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom1", from, to, value) +} + +// TransferFrom1 is a paid mutator transaction binding the contract method 0xbb9bfe06. +// +// Solidity: function transferFrom1(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom1(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom1(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom1 is a paid mutator transaction binding the contract method 0xbb9bfe06. +// +// Solidity: function transferFrom1(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom1(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom1(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom10 is a paid mutator transaction binding the contract method 0xb1802b9a. +// +// Solidity: function transferFrom10(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom10(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom10", from, to, value) +} + +// TransferFrom10 is a paid mutator transaction binding the contract method 0xb1802b9a. +// +// Solidity: function transferFrom10(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom10(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom10(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom10 is a paid mutator transaction binding the contract method 0xb1802b9a. +// +// Solidity: function transferFrom10(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom10(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom10(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom11 is a paid mutator transaction binding the contract method 0xc2be97e3. +// +// Solidity: function transferFrom11(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom11(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom11", from, to, value) +} + +// TransferFrom11 is a paid mutator transaction binding the contract method 0xc2be97e3. +// +// Solidity: function transferFrom11(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom11(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom11(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom11 is a paid mutator transaction binding the contract method 0xc2be97e3. +// +// Solidity: function transferFrom11(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom11(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom11(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom12 is a paid mutator transaction binding the contract method 0x44050a28. +// +// Solidity: function transferFrom12(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom12(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom12", from, to, value) +} + +// TransferFrom12 is a paid mutator transaction binding the contract method 0x44050a28. +// +// Solidity: function transferFrom12(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom12(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom12(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom12 is a paid mutator transaction binding the contract method 0x44050a28. +// +// Solidity: function transferFrom12(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom12(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom12(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom13 is a paid mutator transaction binding the contract method 0xacc5aee9. +// +// Solidity: function transferFrom13(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom13(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom13", from, to, value) +} + +// TransferFrom13 is a paid mutator transaction binding the contract method 0xacc5aee9. +// +// Solidity: function transferFrom13(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom13(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom13(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom13 is a paid mutator transaction binding the contract method 0xacc5aee9. +// +// Solidity: function transferFrom13(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom13(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom13(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom14 is a paid mutator transaction binding the contract method 0x1bbffe6f. +// +// Solidity: function transferFrom14(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom14(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom14", from, to, value) +} + +// TransferFrom14 is a paid mutator transaction binding the contract method 0x1bbffe6f. +// +// Solidity: function transferFrom14(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom14(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom14(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom14 is a paid mutator transaction binding the contract method 0x1bbffe6f. +// +// Solidity: function transferFrom14(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom14(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom14(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom15 is a paid mutator transaction binding the contract method 0x8789ca67. +// +// Solidity: function transferFrom15(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom15(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom15", from, to, value) +} + +// TransferFrom15 is a paid mutator transaction binding the contract method 0x8789ca67. +// +// Solidity: function transferFrom15(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom15(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom15(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom15 is a paid mutator transaction binding the contract method 0x8789ca67. +// +// Solidity: function transferFrom15(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom15(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom15(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom16 is a paid mutator transaction binding the contract method 0x39e0bd12. +// +// Solidity: function transferFrom16(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom16(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom16", from, to, value) +} + +// TransferFrom16 is a paid mutator transaction binding the contract method 0x39e0bd12. +// +// Solidity: function transferFrom16(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom16(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom16(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom16 is a paid mutator transaction binding the contract method 0x39e0bd12. +// +// Solidity: function transferFrom16(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom16(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom16(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom17 is a paid mutator transaction binding the contract method 0x291c3bd7. +// +// Solidity: function transferFrom17(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom17(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom17", from, to, value) +} + +// TransferFrom17 is a paid mutator transaction binding the contract method 0x291c3bd7. +// +// Solidity: function transferFrom17(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom17(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom17(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom17 is a paid mutator transaction binding the contract method 0x291c3bd7. +// +// Solidity: function transferFrom17(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom17(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom17(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom18 is a paid mutator transaction binding the contract method 0x657b6ef7. +// +// Solidity: function transferFrom18(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom18(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom18", from, to, value) +} + +// TransferFrom18 is a paid mutator transaction binding the contract method 0x657b6ef7. +// +// Solidity: function transferFrom18(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom18(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom18(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom18 is a paid mutator transaction binding the contract method 0x657b6ef7. +// +// Solidity: function transferFrom18(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom18(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom18(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom19 is a paid mutator transaction binding the contract method 0xeb4329c8. +// +// Solidity: function transferFrom19(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom19(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom19", from, to, value) +} + +// TransferFrom19 is a paid mutator transaction binding the contract method 0xeb4329c8. +// +// Solidity: function transferFrom19(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom19(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom19(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom19 is a paid mutator transaction binding the contract method 0xeb4329c8. +// +// Solidity: function transferFrom19(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom19(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom19(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom2 is a paid mutator transaction binding the contract method 0x6c12ed28. +// +// Solidity: function transferFrom2(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom2(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom2", from, to, value) +} + +// TransferFrom2 is a paid mutator transaction binding the contract method 0x6c12ed28. +// +// Solidity: function transferFrom2(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom2(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom2(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom2 is a paid mutator transaction binding the contract method 0x6c12ed28. +// +// Solidity: function transferFrom2(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom2(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom2(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom3 is a paid mutator transaction binding the contract method 0x1b17c65c. +// +// Solidity: function transferFrom3(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom3(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom3", from, to, value) +} + +// TransferFrom3 is a paid mutator transaction binding the contract method 0x1b17c65c. +// +// Solidity: function transferFrom3(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom3(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom3(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom3 is a paid mutator transaction binding the contract method 0x1b17c65c. +// +// Solidity: function transferFrom3(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom3(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom3(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom4 is a paid mutator transaction binding the contract method 0x3a131990. +// +// Solidity: function transferFrom4(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom4(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom4", from, to, value) +} + +// TransferFrom4 is a paid mutator transaction binding the contract method 0x3a131990. +// +// Solidity: function transferFrom4(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom4(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom4(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom4 is a paid mutator transaction binding the contract method 0x3a131990. +// +// Solidity: function transferFrom4(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom4(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom4(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom5 is a paid mutator transaction binding the contract method 0x0460faf6. +// +// Solidity: function transferFrom5(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom5(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom5", from, to, value) +} + +// TransferFrom5 is a paid mutator transaction binding the contract method 0x0460faf6. +// +// Solidity: function transferFrom5(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom5(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom5(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom5 is a paid mutator transaction binding the contract method 0x0460faf6. +// +// Solidity: function transferFrom5(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom5(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom5(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom6 is a paid mutator transaction binding the contract method 0x7c66673e. +// +// Solidity: function transferFrom6(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom6(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom6", from, to, value) +} + +// TransferFrom6 is a paid mutator transaction binding the contract method 0x7c66673e. +// +// Solidity: function transferFrom6(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom6(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom6(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom6 is a paid mutator transaction binding the contract method 0x7c66673e. +// +// Solidity: function transferFrom6(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom6(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom6(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom7 is a paid mutator transaction binding the contract method 0xfaf35ced. +// +// Solidity: function transferFrom7(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom7(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom7", from, to, value) +} + +// TransferFrom7 is a paid mutator transaction binding the contract method 0xfaf35ced. +// +// Solidity: function transferFrom7(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom7(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom7(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom7 is a paid mutator transaction binding the contract method 0xfaf35ced. +// +// Solidity: function transferFrom7(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom7(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom7(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom8 is a paid mutator transaction binding the contract method 0x552a1b56. +// +// Solidity: function transferFrom8(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom8(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom8", from, to, value) +} + +// TransferFrom8 is a paid mutator transaction binding the contract method 0x552a1b56. +// +// Solidity: function transferFrom8(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom8(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom8(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom8 is a paid mutator transaction binding the contract method 0x552a1b56. +// +// Solidity: function transferFrom8(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom8(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom8(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom9 is a paid mutator transaction binding the contract method 0x4f7bd75a. +// +// Solidity: function transferFrom9(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactor) TransferFrom9(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.contract.Transact(opts, "transferFrom9", from, to, value) +} + +// TransferFrom9 is a paid mutator transaction binding the contract method 0x4f7bd75a. +// +// Solidity: function transferFrom9(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenSession) TransferFrom9(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom9(&_StateBloatToken.TransactOpts, from, to, value) +} + +// TransferFrom9 is a paid mutator transaction binding the contract method 0x4f7bd75a. +// +// Solidity: function transferFrom9(address from, address to, uint256 value) returns(bool) +func (_StateBloatToken *StateBloatTokenTransactorSession) TransferFrom9(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _StateBloatToken.Contract.TransferFrom9(&_StateBloatToken.TransactOpts, from, to, value) +} + +// StateBloatTokenApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the StateBloatToken contract. +type StateBloatTokenApprovalIterator struct { + Event *StateBloatTokenApproval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *StateBloatTokenApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(StateBloatTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(StateBloatTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *StateBloatTokenApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *StateBloatTokenApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// StateBloatTokenApproval represents a Approval event raised by the StateBloatToken contract. +type StateBloatTokenApproval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*StateBloatTokenApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _StateBloatToken.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &StateBloatTokenApprovalIterator{contract: _StateBloatToken.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *StateBloatTokenApproval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _StateBloatToken.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(StateBloatTokenApproval) + if err := _StateBloatToken.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) ParseApproval(log types.Log) (*StateBloatTokenApproval, error) { + event := new(StateBloatTokenApproval) + if err := _StateBloatToken.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// StateBloatTokenTransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the StateBloatToken contract. +type StateBloatTokenTransferIterator struct { + Event *StateBloatTokenTransfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *StateBloatTokenTransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(StateBloatTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(StateBloatTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *StateBloatTokenTransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *StateBloatTokenTransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// StateBloatTokenTransfer represents a Transfer event raised by the StateBloatToken contract. +type StateBloatTokenTransfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*StateBloatTokenTransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _StateBloatToken.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &StateBloatTokenTransferIterator{contract: _StateBloatToken.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *StateBloatTokenTransfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _StateBloatToken.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(StateBloatTokenTransfer) + if err := _StateBloatToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_StateBloatToken *StateBloatTokenFilterer) ParseTransfer(log types.Log) (*StateBloatTokenTransfer, error) { + event := new(StateBloatTokenTransfer) + if err := _StateBloatToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/scenarios/statebloat/contract_deploy/contract/StateBloatToken.sol b/scenarios/statebloat/contract_deploy/contract/StateBloatToken.sol new file mode 100644 index 00000000..eb7dc2dc --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract/StateBloatToken.sol @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +contract StateBloatToken { + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + // Salt to make each deployment unique + uint256 public immutable salt; + + // Large constant to increase bytecode size to exactly 24KiB + string public constant PADDING_DATA = + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + + constructor(uint256 _salt) { + name = "State Bloat Token"; + symbol = "SBT"; + decimals = 18; + salt = _salt; + totalSupply = 1000000 * 10 ** decimals; + balanceOf[msg.sender] = totalSupply; + emit Transfer(address(0), msg.sender, totalSupply); + } + + function transfer(address to, uint256 value) public returns (bool) { + require(balanceOf[msg.sender] >= value, "Insufficient balance"); + balanceOf[msg.sender] -= value; + balanceOf[to] += value; + emit Transfer(msg.sender, to, value); + return true; + } + + function approve(address spender, uint256 value) public returns (bool) { + allowance[msg.sender][spender] = value; + emit Approval(msg.sender, spender, value); + return true; + } + + function transferFrom( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + // Dummy functions to increase bytecode size + function dummy1() public pure returns (uint256) { + return 1; + } + + function dummy2() public pure returns (uint256) { + return 2; + } + + function dummy3() public pure returns (uint256) { + return 3; + } + + function dummy4() public pure returns (uint256) { + return 4; + } + + function dummy5() public pure returns (uint256) { + return 5; + } + + function dummy6() public pure returns (uint256) { + return 6; + } + + function dummy7() public pure returns (uint256) { + return 7; + } + + function dummy8() public pure returns (uint256) { + return 8; + } + + function dummy9() public pure returns (uint256) { + return 9; + } + + function dummy10() public pure returns (uint256) { + return 10; + } + + function dummy11() public pure returns (uint256) { + return 11; + } + + function dummy12() public pure returns (uint256) { + return 12; + } + + function dummy13() public pure returns (uint256) { + return 13; + } + + function dummy14() public pure returns (uint256) { + return 14; + } + + function dummy15() public pure returns (uint256) { + return 15; + } + + function dummy16() public pure returns (uint256) { + return 16; + } + + function dummy17() public pure returns (uint256) { + return 17; + } + + function dummy18() public pure returns (uint256) { + return 18; + } + + function dummy19() public pure returns (uint256) { + return 19; + } + + function dummy20() public pure returns (uint256) { + return 20; + } + + function dummy21() public pure returns (uint256) { + return 21; + } + + function dummy22() public pure returns (uint256) { + return 22; + } + + function dummy23() public pure returns (uint256) { + return 23; + } + + function dummy24() public pure returns (uint256) { + return 24; + } + + function dummy25() public pure returns (uint256) { + return 25; + } + + function dummy26() public pure returns (uint256) { + return 26; + } + + function dummy27() public pure returns (uint256) { + return 27; + } + + function dummy28() public pure returns (uint256) { + return 28; + } + + function dummy29() public pure returns (uint256) { + return 29; + } + + function dummy30() public pure returns (uint256) { + return 30; + } + + function dummy31() public pure returns (uint256) { + return 31; + } + + function dummy32() public pure returns (uint256) { + return 32; + } + + function dummy33() public pure returns (uint256) { + return 33; + } + + function dummy34() public pure returns (uint256) { + return 34; + } + + function dummy35() public pure returns (uint256) { + return 35; + } + + function dummy36() public pure returns (uint256) { + return 36; + } + + function dummy37() public pure returns (uint256) { + return 37; + } + + function dummy38() public pure returns (uint256) { + return 38; + } + + function dummy39() public pure returns (uint256) { + return 39; + } + + function dummy40() public pure returns (uint256) { + return 40; + } + + function dummy41() public pure returns (uint256) { + return 41; + } + + function dummy42() public pure returns (uint256) { + return 42; + } + + function dummy43() public pure returns (uint256) { + return 43; + } + + function dummy44() public pure returns (uint256) { + return 44; + } + + function dummy45() public pure returns (uint256) { + return 45; + } + + function dummy46() public pure returns (uint256) { + return 46; + } + + function dummy47() public pure returns (uint256) { + return 47; + } + + function dummy48() public pure returns (uint256) { + return 48; + } + + function dummy49() public pure returns (uint256) { + return 49; + } + + function dummy50() public pure returns (uint256) { + return 50; + } + + function dummy51() public pure returns (uint256) { + return 51; + } + + function dummy52() public pure returns (uint256) { + return 52; + } + + function dummy53() public pure returns (uint256) { + return 53; + } + + function dummy54() public pure returns (uint256) { + return 54; + } + + function dummy55() public pure returns (uint256) { + return 55; + } + + function dummy56() public pure returns (uint256) { + return 56; + } + + function dummy57() public pure returns (uint256) { + return 57; + } + + function dummy58() public pure returns (uint256) { + return 58; + } + + function dummy59() public pure returns (uint256) { + return 59; + } + + function dummy60() public pure returns (uint256) { + return 60; + } + + function dummy61() public pure returns (uint256) { + return 61; + } + + function dummy62() public pure returns (uint256) { + return 62; + } + + function dummy63() public pure returns (uint256) { + return 63; + } + + function dummy64() public pure returns (uint256) { + return 64; + } + + function dummy65() public pure returns (uint256) { + return 65; + } + + // Additional dummy functions to increase bytecode to exactly 24KiB + function dummy66() public pure returns (uint256) { + return 66; + } + + function dummy67() public pure returns (uint256) { + return 67; + } + + function dummy68() public pure returns (uint256) { + return 68; + } + + function dummy69() public pure returns (uint256) { + return 69; + } + + function transferFrom1( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom2( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom3( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom4( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom5( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom6( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom7( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom8( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom9( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom10( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom11( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom12( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom13( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom14( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom15( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom16( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "Insufficient balance"); + require(allowance[from][msg.sender] >= value, "Insufficient allowance"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom17( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "A"); + require(allowance[from][msg.sender] >= value, "B"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom18( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "A"); + require(allowance[from][msg.sender] >= value, "B"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } + + function transferFrom19( + address from, + address to, + uint256 value + ) public returns (bool) { + require(balanceOf[from] >= value, "A"); + balanceOf[from] -= value; + balanceOf[to] += value; + allowance[from][msg.sender] -= value; + emit Transfer(from, to, value); + return true; + } +} diff --git a/scenarios/statebloat/contract_deploy/contract/compile.sh b/scenarios/statebloat/contract_deploy/contract/compile.sh new file mode 100755 index 00000000..c96a3f4f --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract/compile.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +compile_contract() { + local workdir=$1 + local solc_version=$2 + local solc_args=$3 + local contract_file=$4 + local contract_name=$5 + + if [ -z "$contract_name" ]; then + contract_name="$contract_file" + fi + + #echo "docker run --rm -v $workdir:/contracts ethereum/solc:$solc_version /contracts/$contract_file.sol --combined-json abi,bin $solc_args" + local contract_json=$(docker run --rm -v $workdir:/contracts ethereum/solc:$solc_version /contracts/$contract_file.sol --combined-json abi,bin $solc_args) + + local contract_abi=$(echo "$contract_json" | jq -r '.contracts["/contracts/'$contract_file'.sol:'$contract_name'"].abi') + if [ "$contract_abi" == "null" ]; then + contract_abi=$(echo "$contract_json" | jq -r '.contracts["contracts/'$contract_file'.sol:'$contract_name'"].abi') + fi + + local contract_bin=$(echo "$contract_json" | jq -r '.contracts["/contracts/'$contract_file'.sol:'$contract_name'"].bin') + if [ "$contract_bin" == "null" ]; then + contract_bin=$(echo "$contract_json" | jq -r '.contracts["contracts/'$contract_file'.sol:'$contract_name'"].bin') + fi + + echo "$contract_abi" > $contract_name.abi + echo "$contract_bin" > $contract_name.bin + abigen --bin=./$contract_name.bin --abi=./$contract_name.abi --pkg=contract --out=$contract_name.go --type $contract_name + rm $contract_name.bin $contract_name.abi + echo "$contract_json" | jq > $contract_name.output.json +} + +# StateBloatToken +compile_contract "$(pwd)" 0.8.22 "--optimize --optimize-runs 999999" StateBloatToken diff --git a/scenarios/statebloat/contract_deploy/contract_deploy.go b/scenarios/statebloat/contract_deploy/contract_deploy.go new file mode 100644 index 00000000..8c08b7b4 --- /dev/null +++ b/scenarios/statebloat/contract_deploy/contract_deploy.go @@ -0,0 +1,524 @@ +package sbcontractdeploy + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "encoding/json" + "fmt" + "math/big" + "os" + "strings" + "sync" + "sync/atomic" + "time" + + "gopkg.in/yaml.v3" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethpandaops/spamoor/scenario" + "github.com/ethpandaops/spamoor/scenarios/statebloat/contract_deploy/contract" + "github.com/ethpandaops/spamoor/spamoor" + "github.com/ethpandaops/spamoor/txbuilder" +) + +type ScenarioOptions struct { + MaxPending uint64 `yaml:"max_pending"` + MaxWallets uint64 `yaml:"max_wallets"` + BaseFee uint64 `yaml:"base_fee"` + TipFee uint64 `yaml:"tip_fee"` + ClientGroup string `yaml:"client_group"` + MaxTransactions uint64 `yaml:"max_transactions"` + DeploymentsFile string `yaml:"deployments_file"` +} + +// ContractDeployment tracks a deployed contract with its deployer info +type ContractDeployment struct { + ContractAddress string `json:"contract_address"` + PrivateKey string `json:"private_key"` +} + +// BlockDeploymentStats tracks deployment statistics per block +type BlockDeploymentStats struct { + BlockNumber uint64 + ContractCount int + TotalGasUsed uint64 + TotalBytecodeSize int +} + +type Scenario struct { + options ScenarioOptions + logger *logrus.Entry + walletPool *spamoor.WalletPool + + // Results tracking + deployedContracts []ContractDeployment + contractsMutex sync.Mutex + + // Block-level statistics tracking + blockStats map[uint64]*BlockDeploymentStats + blockStatsMutex sync.Mutex + lastLoggedBlock uint64 +} + +var ScenarioName = "statebloat-contract-deploy" +var ScenarioDefaultOptions = ScenarioOptions{ + MaxWallets: 0, // Use root wallet only by default + BaseFee: 5, // Moderate base fee (5 gwei) + TipFee: 1, // Priority fee (1 gwei) + MaxTransactions: 0, + DeploymentsFile: "deployments.json", +} +var ScenarioDescriptor = scenario.Descriptor{ + Name: ScenarioName, + Description: "Deploy contracts to create state bloat", + DefaultOptions: ScenarioDefaultOptions, + NewScenario: newScenario, +} + +func newScenario(logger logrus.FieldLogger) scenario.Scenario { + return &Scenario{ + logger: logger.WithField("scenario", ScenarioName), + } +} + +func (s *Scenario) Flags(flags *pflag.FlagSet) error { + flags.Uint64Var(&s.options.MaxPending, "max-pending", ScenarioDefaultOptions.MaxPending, "Maximum number of pending transactions") + flags.Uint64Var(&s.options.MaxWallets, "max-wallets", ScenarioDefaultOptions.MaxWallets, "Maximum number of child wallets to use") + flags.Uint64Var(&s.options.BaseFee, "basefee", ScenarioDefaultOptions.BaseFee, "Base fee per gas in gwei") + flags.Uint64Var(&s.options.TipFee, "tipfee", ScenarioDefaultOptions.TipFee, "Tip fee per gas in gwei") + flags.StringVar(&s.options.ClientGroup, "client-group", ScenarioDefaultOptions.ClientGroup, "Client group to use for sending transactions") + flags.Uint64Var(&s.options.MaxTransactions, "max-transactions", ScenarioDefaultOptions.MaxTransactions, "Maximum number of transactions to send (0 = use rate limiting based on block gas limit)") + flags.StringVar(&s.options.DeploymentsFile, "deployments-file", ScenarioDefaultOptions.DeploymentsFile, "File to save deployments to") + return nil +} + +func (s *Scenario) Init(options *scenario.Options) error { + s.walletPool = options.WalletPool + + if options.Config != "" { + err := yaml.Unmarshal([]byte(options.Config), &s.options) + if err != nil { + return fmt.Errorf("failed to unmarshal config: %w", err) + } + } + + if s.options.MaxWallets > 0 { + s.walletPool.SetWalletCount(s.options.MaxWallets) + } else { + s.walletPool.SetWalletCount(10) + } + + return nil +} + +// recordDeployedContract records a successfully deployed contract +func (s *Scenario) recordDeployedContract(contractAddress common.Address, privateKey *ecdsa.PrivateKey, receipt *types.Receipt, txHash common.Hash) { + s.contractsMutex.Lock() + defer s.contractsMutex.Unlock() + + // Keep the JSON structure simple - only contract address and private key + deployment := ContractDeployment{ + ContractAddress: contractAddress.Hex(), + PrivateKey: fmt.Sprintf("0x%x", crypto.FromECDSA(privateKey)), + } + + s.deployedContracts = append(s.deployedContracts, deployment) + + // Get the actual deployed contract bytecode size + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, s.options.ClientGroup) + // TODO: This should be a constant documented on how this number is obtained. + var bytecodeSize int = 23914 + + if client != nil { + // Get the actual deployed bytecode size using eth_getCode + contractCode, err := client.GetEthClient().CodeAt(context.Background(), contractAddress, nil) + if err == nil { + bytecodeSize = len(contractCode) + } + } + + blockNumber := receipt.BlockNumber.Uint64() + + // Debug logging for block tracking + s.logger.WithFields(logrus.Fields{ + "tx_block": blockNumber, + "existing_blocks": len(s.blockStats), + }).Debug("Recording contract deployment") + + // Update block-level statistics + s.blockStatsMutex.Lock() + defer s.blockStatsMutex.Unlock() + + if s.blockStats == nil { + s.blockStats = make(map[uint64]*BlockDeploymentStats) + } + + // Create or update current block stats (removed the old logging logic) + if s.blockStats[blockNumber] == nil { + s.blockStats[blockNumber] = &BlockDeploymentStats{ + BlockNumber: blockNumber, + } + s.logger.WithField("block_number", blockNumber).Debug("Created new block stats") + } + + blockStat := s.blockStats[blockNumber] + blockStat.ContractCount++ + blockStat.TotalGasUsed += receipt.GasUsed + blockStat.TotalBytecodeSize += bytecodeSize + + s.logger.WithFields(logrus.Fields{ + "block_number": blockNumber, + "contracts_in_block": blockStat.ContractCount, + "gas_used": blockStat.TotalGasUsed, + "bytecode_size": blockStat.TotalBytecodeSize, + }).Debug("Updated block stats") + + // Save the deployments.json file each time a contract is confirmed + if err := s.saveDeploymentsMapping(); err != nil { + s.logger.Warnf("Failed to save deployments.json: %v", err) + } +} + +// Helper function for max calculation +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// saveDeploymentsMapping creates/updates deployments.json with private key to contract address mapping +func (s *Scenario) saveDeploymentsMapping() error { + if s.options.DeploymentsFile == "" { + return nil + } + + // Create a map from private key to array of contract addresses + deploymentMap := make(map[string][]string) + + for _, contract := range s.deployedContracts { + privateKey := contract.PrivateKey + contractAddr := contract.ContractAddress + deploymentMap[privateKey] = append(deploymentMap[privateKey], contractAddr) + } + + // Create or overwrite the deployments.json file + deploymentsFile, err := os.Create(s.options.DeploymentsFile) + if err != nil { + return fmt.Errorf("failed to create %v file: %w", s.options.DeploymentsFile, err) + } + defer deploymentsFile.Close() + + // Write the mapping as JSON with pretty formatting + encoder := json.NewEncoder(deploymentsFile) + encoder.SetIndent("", " ") + err = encoder.Encode(deploymentMap) + if err != nil { + return fmt.Errorf("failed to write %v: %w", s.options.DeploymentsFile, err) + } + + return nil +} + +// startBlockMonitor starts a background goroutine that monitors for new blocks +// and logs block deployment summaries immediately when blocks are mined +func (s *Scenario) startBlockMonitor(ctx context.Context) { + go func() { + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, s.options.ClientGroup) + if client == nil { + s.logger.Warn("No client available for block monitoring") + return + } + + ethClient := client.GetEthClient() + ticker := time.NewTicker(2 * time.Second) // Poll every 2 seconds + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + // Get current block number + latestBlock, err := ethClient.BlockByNumber(ctx, nil) + if err != nil { + s.logger.WithError(err).Debug("Failed to get latest block for monitoring") + continue + } + + currentBlockNumber := latestBlock.Number().Uint64() + + // Log any completed blocks that haven't been logged yet + s.blockStatsMutex.Lock() + for bn := s.lastLoggedBlock + 1; bn < currentBlockNumber; bn++ { + if stats, exists := s.blockStats[bn]; exists && stats.ContractCount > 0 { + avgGasPerByte := float64(stats.TotalGasUsed) / float64(max(stats.TotalBytecodeSize, 1)) + + s.contractsMutex.Lock() + totalContracts := len(s.deployedContracts) + s.contractsMutex.Unlock() + + s.logger.WithFields(logrus.Fields{ + "block_number": bn, + "contracts_deployed": stats.ContractCount, + "total_gas_used": stats.TotalGasUsed, + "total_bytecode_size": stats.TotalBytecodeSize, + "avg_gas_per_byte": fmt.Sprintf("%.2f", avgGasPerByte), + "total_contracts": totalContracts, + }).Info("Block deployment summary") + + s.lastLoggedBlock = bn + } + } + s.blockStatsMutex.Unlock() + } + } + }() +} + +func (s *Scenario) Run(ctx context.Context) error { + s.logger.Infof("starting scenario: %s", ScenarioName) + defer s.logger.Infof("scenario %s finished.", ScenarioName) + + // Start block monitoring for real-time logging + s.startBlockMonitor(ctx) + + // Calculate rate limiting based on block gas limit if max-transactions is 0 + var maxTxsPerBlock uint64 + var maxPending uint64 = 100 + var totalTxCount uint64 = 0 + + if s.options.MaxTransactions == 0 { + // Get block gas limit from the network + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, s.options.ClientGroup) + if client == nil { + return fmt.Errorf("no client available for gas limit query") + } + + latestBlock, err := client.GetEthClient().BlockByNumber(ctx, nil) + if err != nil { + return fmt.Errorf("failed to get latest block: %w", err) + } + + blockGasLimit := latestBlock.GasLimit() + // TODO: This should be a constant. + estimatedGasPerContract := uint64(4949468) // Updated estimate based on contract size reduction + maxTxsPerBlock = blockGasLimit / estimatedGasPerContract + + s.logger.Infof("Rate limiting enabled: block gas limit %d, gas per contract %d, max txs per block %d", + blockGasLimit, estimatedGasPerContract, maxTxsPerBlock) + } + + if s.options.MaxPending > 0 { + maxPending = s.options.MaxPending + } + + err := scenario.RunTransactionScenario(ctx, scenario.TransactionScenarioOptions{ + TotalCount: s.options.MaxTransactions, + Throughput: maxTxsPerBlock, + MaxPending: maxPending, + WalletPool: s.walletPool, + + Logger: s.logger, + ProcessNextTxFn: func(ctx context.Context, txIdx uint64, onComplete func()) (func(), error) { + logger := s.logger + tx, err := s.sendTransaction(ctx, txIdx, onComplete) + + atomic.AddUint64(&totalTxCount, 1) + + return func() { + if err != nil { + logger.Warnf("could not send transaction: %v", err) + } else { + logger.Debugf("sent deployment tx #%6d: %v", txIdx+1, tx.Hash().String()) + } + }, err + }, + }) + + // Log any remaining unlogged blocks (final blocks) - keep this as final safety net + s.blockStatsMutex.Lock() + for bn, stats := range s.blockStats { + if bn > s.lastLoggedBlock && stats.ContractCount > 0 { + avgGasPerByte := float64(stats.TotalGasUsed) / float64(max(stats.TotalBytecodeSize, 1)) + + s.logger.WithFields(logrus.Fields{ + "block_number": bn, + "contracts_deployed": stats.ContractCount, + "total_gas_used": stats.TotalGasUsed, + "total_bytecode_size": stats.TotalBytecodeSize, + "avg_gas_per_byte": fmt.Sprintf("%.2f", avgGasPerByte), + "total_contracts": len(s.deployedContracts), + }).Info("Block deployment summary") + } + } + s.blockStatsMutex.Unlock() + + // Log final summary + s.contractsMutex.Lock() + totalContracts := len(s.deployedContracts) + s.contractsMutex.Unlock() + + s.logger.WithFields(logrus.Fields{ + "total_txs": totalTxCount, + "total_contracts": totalContracts, + }).Info("All transactions completed") + + return err +} + +// sendTransaction sends a single contract deployment transaction +func (s *Scenario) sendTransaction(ctx context.Context, txIdx uint64, onComplete func()) (*types.Transaction, error) { + maxRetries := 3 + + for attempt := 0; attempt < maxRetries; attempt++ { + tx, err := s.attemptTransaction(ctx, txIdx, attempt, onComplete) + if err == nil { + return tx, nil + } + + // Check if it's a base fee error + if strings.Contains(err.Error(), "max fee per gas less than block base fee") { + s.logger.Warnf("Transaction %d base fee too low, adjusting fees and retrying (attempt %d/%d)", + txIdx, attempt+1, maxRetries) + + // Update fees based on current network conditions + if updateErr := s.updateDynamicFees(ctx); updateErr != nil { + s.logger.Warnf("Failed to update dynamic fees: %v", updateErr) + } + + time.Sleep(time.Duration(attempt+1) * 500 * time.Millisecond) // Exponential backoff + continue + } + + // For other errors, return immediately + return nil, err + } + + return nil, fmt.Errorf("failed to send transaction after %d attempts", maxRetries) +} + +// updateDynamicFees queries the network and updates base fee and tip fee +func (s *Scenario) updateDynamicFees(ctx context.Context) error { + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, s.options.ClientGroup) + if client == nil { + return fmt.Errorf("no client available") + } + + ethClient := client.GetEthClient() + + // Get the latest block to check current base fee + latestBlock, err := ethClient.BlockByNumber(ctx, nil) + if err != nil { + return fmt.Errorf("failed to get latest block: %w", err) + } + + if latestBlock.BaseFee() != nil { + // Convert base fee from wei to gwei + currentBaseFeeGwei := new(big.Int).Div(latestBlock.BaseFee(), big.NewInt(1000000000)) + + newBaseFeeGwei := new(big.Int).Add(currentBaseFeeGwei, big.NewInt(100)) + + s.options.BaseFee = newBaseFeeGwei.Uint64() + + // Also increase tip fee slightly to ensure competitive priority + if s.options.TipFee+1 > 3 { + s.options.TipFee = s.options.TipFee + 1 + } else { + s.options.TipFee = 2 // Minimum 3 gwei tip + } + + s.logger.Infof("Updated dynamic fees - Base fee: %d gwei, Tip fee: %d gwei (network base fee: %s gwei)", + s.options.BaseFee, s.options.TipFee, currentBaseFeeGwei.String()) + } + + return nil +} + +// attemptTransaction makes a single attempt to send a transaction +func (s *Scenario) attemptTransaction(ctx context.Context, txIdx uint64, attempt int, onComplete func()) (*types.Transaction, error) { + // Get client and wallet + client := s.walletPool.GetClient(spamoor.SelectClientRoundRobin, 0, "") + wallet := s.walletPool.GetWallet(spamoor.SelectWalletByIndex, int(txIdx)) + + if client == nil { + return nil, fmt.Errorf("no client available") + } + if wallet == nil { + return nil, fmt.Errorf("no wallet available") + } + + // Set EIP-1559 fee parameters + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return nil, fmt.Errorf("failed to get suggested fees: %w", err) + } + + // Generate random salt for unique contract + salt := make([]byte, 32) + _, err = rand.Read(salt) + if err != nil { + return nil, fmt.Errorf("failed to generate salt: %w", err) + } + saltInt := new(big.Int).SetBytes(salt) + + // Deploy the contract + ethClient := client.GetEthClient() + if ethClient == nil { + return nil, fmt.Errorf("failed to get eth client") + } + + tx, err := wallet.BuildBoundTx(ctx, &txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: 5200000, + Value: uint256.NewInt(0), + }, func(transactOpts *bind.TransactOpts) (*types.Transaction, error) { + _, deployTx, _, err := contract.DeployStateBloatToken(transactOpts, client.GetEthClient(), saltInt) + return deployTx, err + }) + + if err != nil { + return nil, fmt.Errorf("failed to create deployment transaction: %w", err) + } + + mu := sync.Mutex{} + mu.Lock() + defer mu.Unlock() + + var callOnComplete bool + + err = s.walletPool.GetTxPool().SendTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: true, + OnComplete: func(tx *types.Transaction, receipt *types.Receipt, err error) { + defer func() { + mu.Lock() + defer mu.Unlock() + + if callOnComplete { + onComplete() + } + }() + + if receipt != nil { + s.recordDeployedContract(receipt.ContractAddress, wallet.GetPrivateKey(), receipt, tx.Hash()) + } + }, + }) + + callOnComplete = err == nil + if err != nil { + return nil, err + } + + return tx, nil +} diff --git a/scenarios/statebloat/eoa_delegation/README.md b/scenarios/statebloat/eoa_delegation/README.md new file mode 100644 index 00000000..0a4f5a60 --- /dev/null +++ b/scenarios/statebloat/eoa_delegation/README.md @@ -0,0 +1,79 @@ +# EOA Delegation Scenario + +## Overview + +The EOA Delegation scenario is designed for maximum state bloating through EIP-7702 SetCode transactions. It creates the largest possible state growth by delegating thousands of Externally Owned Accounts (EOAs) to existing contracts in a single block. + +This scenario is a specialized stress-testing tool that: +- Automatically adjusts to fill 99.5% of block gas limit +- Funds EOAs with 1 wei each before delegation +- Tracks all funded EOAs in `EOAs.json` for future reference +- Handles transaction size limits by batching when necessary +- Continuously operates in a loop, creating maximum state growth + +## How It Works + +### Three-Phase Operation + +1. **Funding Phase**: Pre-funds delegator EOAs with 1 wei each +2. **Bloating Phase**: Sends SetCode transaction(s) with maximum authorizations +3. **Analysis Phase**: Measures performance and adjusts parameters + +### Key Features + +- **Self-Adjusting**: Dynamically adjusts authorization count based on actual gas usage +- **Network-Aware**: Queries actual block gas limit from the network +- **Size-Aware**: Automatically splits large transactions that exceed 128KiB limit +- **Persistent Storage**: Saves all funded EOAs to `EOAs.json` for reuse + +### Technical Details + +- Each EOA delegation creates ~135 bytes of new state +- Uses ecrecover precompile (0x1) as default delegation target +- Targets 99.5% block utilization for maximum impact +- Handles up to ~1300 authorizations per 128KiB RLP-encoded transaction +- Transaction size limit: 128KiB (131,072 bytes) applies to the RLP-encoded transaction +- Actual authorization size in transaction: ~94 bytes per authorization (RLP-encoded) + +## Usage + +### Basic Usage + +```bash +# Run with default settings (auto-adjusts to network) +# Address is set to Identity precompile by default. +eoa-delegation -h -p + +# Specify custom delegation target +eoa-delegation eoa-delegation --code-addr 0x1234567890123456789012345678901234567890 + +# Control gas prices +eoa-delegation eoa-delegation --basefee 30 --tipfee 3 +``` + +### Command Line Options + +- `--code-addr`: Contract address to delegate to (default: ecrecover precompile) +- `--basefee`: Base fee in gwei (default: 20) +- `--tipfee`: Priority fee in gwei (default: 2) +- `--client-group`: Specific client group to use +- `--rebroadcast`: Seconds between transaction rebroadcasts (default: 120) + +### Output + +The scenario logs detailed metrics for each iteration: + +``` +STATE BLOATING METRICS - Total bytes written: 130.5 KiB, Gas used: 25.2M, Block utilization: 98.7%, Authorizations: 990, Gas/auth: 25454.5, Gas/byte: 188.6, Total fee: 0.0504 ETH +``` + +## State Impact + +This scenario creates maximum state growth by: +1. Creating new EOA accounts (funded with 1 wei) +2. Adding delegation records for each EOA +3. Updating nonce and balance for each account + +## Files Created + +- `EOAs.json`: Contains addresses and private keys of all funded EOAs diff --git a/scenarios/statebloat/eoa_delegation/eoa_delegation.go b/scenarios/statebloat/eoa_delegation/eoa_delegation.go new file mode 100644 index 00000000..43473770 --- /dev/null +++ b/scenarios/statebloat/eoa_delegation/eoa_delegation.go @@ -0,0 +1,891 @@ +package sbeoadelegation + +import ( + "context" + "crypto/sha256" + "encoding/binary" + "encoding/json" + "fmt" + "math/big" + "os" + "strings" + "sync" + "sync/atomic" + "time" + + "gopkg.in/yaml.v3" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + + "github.com/ethpandaops/spamoor/scenario" + "github.com/ethpandaops/spamoor/spamoor" + "github.com/ethpandaops/spamoor/txbuilder" +) + +// EIP-7702 gas cost constants +const ( + // PER_AUTH_BASE_COST (EIP-7702) - upper bound on gas cost per authorization when delegating to existing contract in this scenario + GasPerAuthorization = 26000 + // EstimatedBytesPerAuth - estimated state change in bytes per EOA delegation + EstimatedBytesPerAuth = 135.0 + // ActualBytesPerAuth - actual observed RLP-encoded bytes per authorization in transaction + // RLP encoding breakdown for typical authorization: + // - ChainID: ~3 bytes (RLP encodes integers efficiently, not fixed 8 bytes) + // - Address: 21 bytes (0x94 prefix + 20 bytes) + // - Nonce: 1 byte (0x80 for nonce 0, small values) + // - YParity: 1 byte (0x00 or 0x01) + // - R: 33 bytes (0xa0 prefix + 32 bytes) + // - S: 33 bytes (0xa0 prefix + 32 bytes) + // - List overhead: 2 bytes (0xf8 + length byte) + // Total: ~94 bytes (confirmed by empirical data: 89KiB for 969 auths) + ActualBytesPerAuth = 94 + // DefaultTargetGasRatio - target percentage of block gas limit to use (99.5% for minimal safety margin) + DefaultTargetGasRatio = 0.995 + // FallbackBlockGasLimit - fallback gas limit if network query fails + FallbackBlockGasLimit = 30000000 + // BaseTransferCost - gas cost for a standard ETH transfer + BaseTransferCost = 21000 + // MaxTransactionSize - Ethereum transaction size limit in bytes (128KiB) + MaxTransactionSize = 131072 // 128 * 1024 + // GweiPerEth - conversion factor from Gwei to Wei + GweiPerEth = 1000000000 + // BlockMiningTimeout - timeout for waiting for a new block to be mined + BlockMiningTimeout = 30 * time.Second + // BlockPollingInterval - interval for checking new blocks + BlockPollingInterval = 1 * time.Second + // MaxRebroadcasts - maximum number of times to rebroadcast a transaction + MaxRebroadcasts = 10 + // TransactionBatchSize - used for batching funding transactions + TransactionBatchSize = 100 + // TransactionBatchThreshold - threshold for continuing to fill a block + TransactionBatchThreshold = 50 + // InitialTransactionDelay - delay between initial funding transactions + InitialTransactionDelay = 10 * time.Millisecond + // OptimizedTransactionDelay - reduced delay after initial batch + OptimizedTransactionDelay = 5 * time.Millisecond + // FundingConfirmationDelay - delay before checking funding confirmations + FundingConfirmationDelay = 3 * time.Second + // RetryDelay - delay before retrying failed operations + RetryDelay = 5 * time.Second + // FundingIterationOffset - large offset to avoid delegator index conflicts between iterations + FundingIterationOffset = 1000000 + // TransactionBaseOverhead - base RLP encoding overhead for a transaction + TransactionBaseOverhead = 200 + // TransactionExtraOverhead - additional RLP encoding overhead + TransactionExtraOverhead = 50 + // GasPerCallDataByte - gas cost per byte of calldata (16 gas per non-zero byte) + GasPerCallDataByte = 16 + // BytesPerKiB - bytes in a kibibyte + BytesPerKiB = 1024.0 + // GasPerMillion - divisor for converting gas to millions + GasPerMillion = 1_000_000.0 + // TransactionSizeSafetyFactor - safety factor for transaction size (95%) + TransactionSizeSafetyFactor = 95 +) + +type ScenarioOptions struct { + BaseFee uint64 `yaml:"base_fee"` + TipFee uint64 `yaml:"tip_fee"` + CodeAddr string `yaml:"code_addr"` + EoaFile string `yaml:"eoa_file"` + LogTxs bool `yaml:"log_txs"` +} + +// EOAEntry represents a funded EOA account +type EOAEntry struct { + Address string `json:"address"` + PrivateKey string `json:"private_key"` +} + +type Scenario struct { + options ScenarioOptions + logger *logrus.Entry + walletPool *spamoor.WalletPool + + // FIFO queue for funded accounts + eoaQueue []EOAEntry + eoaQueueMutex sync.Mutex +} + +var ScenarioName = "statebloat-eoa-delegation" +var ScenarioDefaultOptions = ScenarioOptions{ + BaseFee: 20, + TipFee: 2, + CodeAddr: "", + EoaFile: "", + LogTxs: false, +} +var ScenarioDescriptor = scenario.Descriptor{ + Name: ScenarioName, + Description: "Maximum state bloating via EIP-7702 EOA delegations", + DefaultOptions: ScenarioDefaultOptions, + NewScenario: newScenario, +} + +func newScenario(logger logrus.FieldLogger) scenario.Scenario { + return &Scenario{ + logger: logger.WithField("scenario", ScenarioName), + } +} + +func (s *Scenario) Flags(flags *pflag.FlagSet) error { + flags.Uint64Var(&s.options.BaseFee, "basefee", ScenarioDefaultOptions.BaseFee, "Max fee per gas to use in transactions (in gwei)") + flags.Uint64Var(&s.options.TipFee, "tipfee", ScenarioDefaultOptions.TipFee, "Max tip per gas to use in transactions (in gwei)") + flags.StringVar(&s.options.CodeAddr, "code-addr", ScenarioDefaultOptions.CodeAddr, "Code delegation target address to use for transactions (default: ecrecover precompile)") + flags.StringVar(&s.options.EoaFile, "eoa-file", ScenarioDefaultOptions.EoaFile, "File to write EOAs to") + flags.BoolVar(&s.options.LogTxs, "log-txs", ScenarioDefaultOptions.LogTxs, "Log transactions") + return nil +} + +func (s *Scenario) Init(options *scenario.Options) error { + s.walletPool = options.WalletPool + + if options.Config != "" { + err := yaml.Unmarshal([]byte(options.Config), &s.options) + if err != nil { + return fmt.Errorf("failed to unmarshal config: %w", err) + } + } + + if s.options.CodeAddr == "" { + s.logger.Infof("no --code-addr specified, using ecrecover precompile as delegate: %s", common.HexToAddress("0x0000000000000000000000000000000000000001")) + } + + // In max-bloating mode, use 100 wallets for funding delegators + s.walletPool.SetWalletCount(100) + s.walletPool.SetRefillAmount(uint256.NewInt(0).Mul(uint256.NewInt(20), uint256.NewInt(1000000000000000000))) // 20 ETH + s.walletPool.SetRefillBalance(uint256.NewInt(0).Mul(uint256.NewInt(10), uint256.NewInt(1000000000000000000))) // 10 ETH + + // register well known wallets + s.walletPool.AddWellKnownWallet(&spamoor.WellKnownWalletConfig{ + Name: "bloater", + RefillAmount: uint256.NewInt(0).Mul(uint256.NewInt(20), uint256.NewInt(1000000000000000000)), // 20 ETH + RefillBalance: uint256.NewInt(0).Mul(uint256.NewInt(10), uint256.NewInt(1000000000000000000)), // 10 ETH + }) + + // Initialize FIFO queue and worker for EOA management + s.eoaQueue = make([]EOAEntry, 0) + + return nil +} + +func (s *Scenario) Config() string { + yamlBytes, _ := yaml.Marshal(&s.options) + return string(yamlBytes) +} + +func (s *Scenario) prepareDelegator(delegatorIndex uint64) (*spamoor.Wallet, error) { + delegatorSeed := make([]byte, 8) + binary.BigEndian.PutUint64(delegatorSeed, delegatorIndex) + delegatorSeed = append(delegatorSeed, s.walletPool.GetWalletSeed()...) + delegatorSeed = append(delegatorSeed, s.walletPool.GetRootWallet().GetWallet().GetAddress().Bytes()...) + childKey := sha256.Sum256(delegatorSeed) + return spamoor.NewWallet(fmt.Sprintf("%x", childKey)) +} + +func (s *Scenario) Run(ctx context.Context) error { + s.logger.Infof("starting max bloating mode: self-adjusting to target block gas limit, continuous operation") + + go s.eoaWorker(ctx) + + // Get the actual network block gas limit + networkGasLimit, err := s.walletPool.GetTxPool().GetCurrentGasLimitWithInit() + if err != nil { + s.logger.Errorf("failed to get current gas limit: %v", err) + return err + } + + targetGas := uint64(float64(networkGasLimit) * DefaultTargetGasRatio) + + // Calculate initial authorization count based on network gas limit and known gas cost per authorization + initialAuthorizations := int(targetGas / GasPerAuthorization) + + // Dynamic authorization count - starts based on network parameters and adjusts based on actual performance + currentAuthorizations := initialAuthorizations + + var blockCounter int + + for { + select { + case <-ctx.Done(): + s.logger.Errorf("max bloating mode stopping due to context cancellation") + return ctx.Err() + default: + } + + blockCounter++ + + // For the first iteration, we need to fund delegators before bloating + // For subsequent iterations, funding happens after analysis + if blockCounter == 1 { + s.logger.Infof("════════════════ INITIAL FUNDING PHASE ════════════════") + _, err := s.fundMaxBloatingDelegators(ctx, currentAuthorizations, blockCounter, networkGasLimit) + if err != nil { + s.logger.Errorf("failed to fund delegators for initial iteration: %v", err) + time.Sleep(RetryDelay) // Wait before retry + blockCounter-- // Retry the same iteration + continue + } + } + + // Send the max bloating transaction and wait for confirmation + s.logger.Infof("════════════════ BLOATING PHASE #%d ════════════════", blockCounter) + actualGasUsed, _, authCount, gasPerAuth, gasPerByte, _, err := s.sendMaxBloatingTransaction(ctx, currentAuthorizations, targetGas, blockCounter) + if err != nil { + s.logger.Errorf("failed to send max bloating transaction for iteration %d: %v", blockCounter, err) + time.Sleep(RetryDelay) // Wait before retry + continue + } + + s.logger.Infof("%%%%%%%%%%%%%%%%%%%% ANALYSIS PHASE #%d %%%%%%%%%%%%%%%%%%%%", blockCounter) + + // Calculate total bytes written to state + totalBytesWritten := authCount * int(EstimatedBytesPerAuth) + + // Get block gas limit for utilization calculation + blockGasLimit := float64(networkGasLimit) + gasUtilization := (float64(actualGasUsed) / blockGasLimit) * 100 + + s.logger.WithField("scenario", "eoa-delegation").Infof("STATE BLOATING METRICS - Total bytes written: %.2f KiB, Gas used: %.2fM, Block utilization: %.2f%%, Authorizations: %d, Gas/auth: %.1f, Gas/byte: %.1f", + float64(totalBytesWritten)/BytesPerKiB, float64(actualGasUsed)/GasPerMillion, gasUtilization, authCount, gasPerAuth, gasPerByte) + + // Self-adjust authorization count based on actual performance + if actualGasUsed > 0 && authCount > 0 { + gasPerAuth := float64(actualGasUsed) / float64(authCount) + targetAuths := int(float64(targetGas) / gasPerAuth) + + // Calculate the adjustment needed + authDifference := targetAuths - authCount + + if actualGasUsed < targetGas { + // We're under target, increase authorization count with a slight safety margin + newAuthorizations := currentAuthorizations + authDifference - 1 + + if newAuthorizations > currentAuthorizations { + s.logger.Infof("Adjusting authorizations: %d β†’ %d (need %d more for target)", + currentAuthorizations, newAuthorizations, authDifference) + currentAuthorizations = newAuthorizations + } + } else if actualGasUsed > targetGas { + // We're over target, reduce to reach max block utilization + excess := actualGasUsed - targetGas + newAuthorizations := currentAuthorizations - int(excess) + 1 + + s.logger.Infof("Reducing authorizations: %d β†’ %d (excess: %d gas)", + currentAuthorizations, newAuthorizations, excess) + currentAuthorizations = newAuthorizations + + } else { + s.logger.Infof("Target achieved! Gas Used: %d / Target: %d", actualGasUsed, targetGas) + } + } + + // Now fund delegators for the next iteration (except on the last iteration) + // This ensures funding happens AFTER bloating transactions are confirmed + s.logger.Infof("════════════════ FUNDING PHASE #%d (for next iteration) ════════════════", blockCounter) + _, err = s.fundMaxBloatingDelegators(ctx, currentAuthorizations, blockCounter+1, networkGasLimit) + if err != nil { + s.logger.Errorf("failed to fund delegators for next iteration: %v", err) + // Don't fail the entire loop, just log the error and continue + } + } +} + +func (s *Scenario) fundMaxBloatingDelegators(ctx context.Context, targetCount int, iteration int, gasLimit uint64) (int64, error) { + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return 0, fmt.Errorf("no client available for funding delegators") + } + + // Get suggested fees for funding transactions + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return 0, fmt.Errorf("failed to get suggested fees for funding: %w", err) + } + + // Fund with 1 wei as requested by user + fundingAmount := uint256.NewInt(1) + + var confirmedCount int64 + + delegatorIndexBase := uint64(iteration * FundingIterationOffset) // Large offset per iteration to avoid conflicts + + fundNextDelegator := func(ctx context.Context, txIdx uint64, onComplete func()) (*types.Transaction, *spamoor.Client, *spamoor.Wallet, error) { + wallet := s.walletPool.GetWallet(spamoor.SelectWalletByIndex, int(txIdx)) + if wallet == nil { + return nil, nil, nil, fmt.Errorf("no wallet available for funding") + } + + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return nil, nil, nil, fmt.Errorf("no client available for funding delegators") + } + + delegatorIndex := delegatorIndexBase + txIdx + transactionSubmitted := false + + defer func() { + if !transactionSubmitted { + onComplete() + } + }() + + delegator, err := s.prepareDelegator(delegatorIndex) + if err != nil { + s.logger.Errorf("could not prepare delegator %v for funding: %v", delegatorIndex, err) + return nil, nil, nil, err + } + + // Build funding transaction + delegatorAddr := delegator.GetAddress() + txData, err := txbuilder.DynFeeTx(&txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: BaseTransferCost, // Standard ETH transfer gas + To: &delegatorAddr, + Value: fundingAmount, + Data: []byte{}, + }) + if err != nil { + s.logger.Errorf("failed to build funding tx for delegator %d: %v", delegatorIndex, err) + return nil, nil, nil, err + } + + tx, err := wallet.BuildDynamicFeeTx(txData) + if err != nil { + s.logger.Errorf("failed to build funding transaction for delegator %d: %v", delegatorIndex, err) + return nil, nil, nil, err + } + + // Send funding transaction with no retries to avoid duplicates + transactionSubmitted = true + err = s.walletPool.GetTxPool().SendTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: false, // No retries to avoid duplicates + OnComplete: func(tx *types.Transaction, receipt *types.Receipt, err error) { + defer onComplete() + + if err != nil { + return // Don't log individual failures + } + if receipt != nil && receipt.Status == 1 { + atomic.AddInt64(&confirmedCount, 1) + + // Add successfully funded delegator to EOA queue + s.addEOAToQueue(delegator.GetAddress().Hex(), fmt.Sprintf("%x", delegator.GetPrivateKey().D)) + + // No progress logging - only log when target is reached + } + }, + LogFn: func(client *spamoor.Client, retry int, rebroadcast int, err error) { + // Only log actual send failures, not confirmation failures + if err != nil { + s.logger.Debugf("funding tx send failed: %v", err) + } + }, + }) + + return tx, client, wallet, err + } + + // Calculate approximate transactions per block based on gas limit + // Standard transfer = BaseTransferCost gas. + maxTxsPerBlock := gasLimit / uint64(BaseTransferCost) + + scenario.RunTransactionScenario(ctx, scenario.TransactionScenarioOptions{ + TotalCount: uint64(targetCount), + Throughput: maxTxsPerBlock * 2, + MaxPending: maxTxsPerBlock * 2, + WalletPool: s.walletPool, + Logger: s.logger, + ProcessNextTxFn: func(ctx context.Context, txIdx uint64, onComplete func()) (func(), error) { + logger := s.logger + tx, client, wallet, err := fundNextDelegator(ctx, txIdx, onComplete) + if client != nil { + logger = logger.WithField("rpc", client.GetName()) + } + if tx != nil { + logger = logger.WithField("nonce", tx.Nonce()) + } + if wallet != nil { + logger = logger.WithField("wallet", s.walletPool.GetWalletName(wallet.GetAddress())) + } + + return func() { + if err != nil { + logger.Warnf("could not send transaction: %v", err) + } else if s.options.LogTxs { + logger.Infof("sent tx #%6d: %v", txIdx+1, tx.Hash().String()) + } else { + logger.Debugf("sent tx #%6d: %v", txIdx+1, tx.Hash().String()) + } + }, err + }, + }) + + // Return the confirmed count + confirmed := atomic.LoadInt64(&confirmedCount) + return confirmed, nil +} + +func (s *Scenario) sendMaxBloatingTransaction(ctx context.Context, targetAuthorizations int, targetGasLimit uint64, blockCounter int) (uint64, string, int, float64, float64, string, error) { + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("no client available for sending max bloating transaction") + } + + // Use bloater wallet + wallet := s.walletPool.GetWellKnownWallet("bloater") + if wallet == nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("no bloater wallet available") + } + + // Get suggested fees or use configured values + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("failed to get suggested fees for max bloating: %w", err) + } + + // Use minimal amount for max bloating (focus on authorizations, not value transfer) + amount := uint256.NewInt(0) // No value transfer needed + + // Target address - use our own wallet for simplicity + toAddr := wallet.GetAddress() + + // No call data for max bloating transactions + txCallData := []byte{} + + // Build the authorizations for maximum state bloat + authorizations := s.buildMaxBloatingAuthorizations(targetAuthorizations, blockCounter) + + // Check transaction size and split into batches if needed + batches := s.splitAuthorizationsBatches(authorizations, len(txCallData)) + + if len(batches) == 1 { + // Single transaction - use existing logic + return s.sendSingleMaxBloatingTransaction(ctx, batches[0], txCallData, feeCap, tipCap, amount, toAddr, targetGasLimit, wallet, client) + } else { + // Multiple transactions needed - send them as a batch + return s.sendBatchedMaxBloatingTransactions(ctx, batches, txCallData, feeCap, tipCap, amount, toAddr, targetGasLimit, wallet, client) + } +} + +// buildMaxBloatingAuthorizations builds the authorizations for the max bloating transaction +func (s *Scenario) buildMaxBloatingAuthorizations(targetCount int, iteration int) []types.SetCodeAuthorization { + authorizations := make([]types.SetCodeAuthorization, 0, targetCount) + + // Use a fixed delegate contract address for maximum efficiency + // In max bloating mode, we want all EOAs to delegate to the same existing contract + // to benefit from reduced gas costs (PER_AUTH_BASE_COST vs PER_EMPTY_ACCOUNT_COST) + // Precompiles are ideal as they're guaranteed to exist with code on all networks + var codeAddr common.Address + if s.options.CodeAddr != "" { + codeAddr = common.HexToAddress(s.options.CodeAddr) + } else { + // Default to using the ecrecover precompile (0x1) as delegate target + codeAddr = common.HexToAddress("0x0000000000000000000000000000000000000001") + } + + chainId := s.walletPool.GetChainId() + + for i := 0; i < targetCount; i++ { + // Create a unique delegator for each authorization + // Include iteration counter to ensure different addresses for each iteration + delegatorIndex := uint64(iteration*targetCount + i) + + delegator, err := s.prepareDelegator(delegatorIndex) + if err != nil { + s.logger.Errorf("could not prepare delegator %v: %v", delegatorIndex, err) + continue + } + + // Each EOA uses auth_nonce = 0 (assuming first EIP-7702 operation) + // This creates maximum new state as each EOA gets its first delegation + authorization := types.SetCodeAuthorization{ + ChainID: *uint256.MustFromBig(chainId), + Address: codeAddr, + Nonce: 0, // First delegation for each EOA + } + + // Sign the authorization with the delegator's private key + signedAuth, err := types.SignSetCode(delegator.GetPrivateKey(), authorization) + if err != nil { + s.logger.Errorf("could not sign set code authorization for delegator %v: %v", delegatorIndex, err) + continue + } + + authorizations = append(authorizations, signedAuth) + } + + return authorizations +} + +// splitAuthorizationsBatches splits authorizations into batches that fit within transaction size limit +func (s *Scenario) splitAuthorizationsBatches(authorizations []types.SetCodeAuthorization, callDataSize int) [][]types.SetCodeAuthorization { + if len(authorizations) == 0 { + return [][]types.SetCodeAuthorization{authorizations} + } + + // To get closer to 128KiB limit, we need to adjust our estimate + // Using a safety factor of 0.95 to stay just under the limit + targetSize := MaxTransactionSize * TransactionSizeSafetyFactor / 100 // Safety margin + + maxAuthsPerTx := (targetSize - TransactionBaseOverhead - callDataSize) / ActualBytesPerAuth + if maxAuthsPerTx <= 0 { + s.logger.Warnf("Transaction call data too large, using minimal batch size of 1") + maxAuthsPerTx = 1 + } + + // If all authorizations fit in one transaction, return as single batch + if len(authorizations) <= maxAuthsPerTx { + estimatedSize := s.calculateTransactionSize(len(authorizations), callDataSize) + s.logger.Infof("All %d authorizations fit in single transaction (estimated size: %d bytes)", len(authorizations), estimatedSize) + return [][]types.SetCodeAuthorization{authorizations} + } + + // Split into multiple batches + var batches [][]types.SetCodeAuthorization + for i := 0; i < len(authorizations); i += maxAuthsPerTx { + end := i + maxAuthsPerTx + if end > len(authorizations) { + end = len(authorizations) + } + batch := authorizations[i:end] + batches = append(batches, batch) + } + + s.logger.Infof("Split %d authorizations into %d batches (max %d auths per batch, target size: %.2f KiB)", + len(authorizations), len(batches), maxAuthsPerTx, float64(targetSize)/BytesPerKiB) + return batches +} + +// calculateTransactionSize estimates the serialized size of a transaction with given authorizations +func (s *Scenario) calculateTransactionSize(authCount int, callDataSize int) int { + // Estimation based on empirical data and RLP encoding structure: + // - Base transaction overhead: ~200 bytes + // - Each SetCodeAuthorization: ~94 bytes (based on actual observed data) + // - Call data: variable size + // - Additional RLP encoding overhead: ~50 bytes + + baseSize := TransactionBaseOverhead + callDataSize + TransactionExtraOverhead + authSize := authCount * ActualBytesPerAuth + return baseSize + authSize +} + +// sendSingleMaxBloatingTransaction sends a single transaction (original logic) +func (s *Scenario) sendSingleMaxBloatingTransaction(ctx context.Context, authorizations []types.SetCodeAuthorization, txCallData []byte, feeCap, tipCap *big.Int, amount *uint256.Int, toAddr common.Address, targetGasLimit uint64, wallet *spamoor.Wallet, client *spamoor.Client) (uint64, string, int, float64, float64, string, error) { + txData, err := txbuilder.SetCodeTx(&txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: targetGasLimit, + To: &toAddr, + Value: amount, + Data: txCallData, + AuthList: authorizations, + }) + if err != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("failed to build transaction metadata: %w", err) + } + + tx, err := wallet.BuildSetCodeTx(txData) + if err != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("failed to build transaction: %w", err) + } + + // Log actual transaction size + txSize := len(tx.Data()) + if encoded, err := tx.MarshalBinary(); err == nil { + txSize = len(encoded) + } + sizeKiB := float64(txSize) / BytesPerKiB + exceedsLimit := txSize > MaxTransactionSize + limitKiB := float64(MaxTransactionSize) / BytesPerKiB + + s.logger.WithField("scenario", "eoa-delegation").Infof("MAX BLOATING TX SIZE: %d bytes (%.2f KiB) | Limit: %d bytes (%.1f KiB) | %d authorizations | Exceeds limit: %v", + txSize, sizeKiB, MaxTransactionSize, limitKiB, len(authorizations), exceedsLimit) + + // Send the transaction + txreceipt, txerr := s.walletPool.GetTxPool().SendAndAwaitTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: true, + LogFn: spamoor.GetDefaultLogFn(s.logger, "max bloating", "", tx), + }) + + if txerr != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("failed to send max bloating transaction: %w", txerr) + } + + if txreceipt == nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("no receipt received") + } + + effectiveGasPrice := txreceipt.EffectiveGasPrice + if effectiveGasPrice == nil { + effectiveGasPrice = big.NewInt(0) + } + feeAmount := new(big.Int).Mul(effectiveGasPrice, big.NewInt(int64(txreceipt.GasUsed))) + totalAmount := new(big.Int).Add(tx.Value(), feeAmount) + wallet.SubBalance(totalAmount) + + gweiTotalFee := new(big.Int).Div(feeAmount, big.NewInt(1000000000)) + + // Calculate efficiency metrics + authCount := len(authorizations) + gasPerAuth := float64(txreceipt.GasUsed) / float64(authCount) + gasPerByte := gasPerAuth / EstimatedBytesPerAuth + + return txreceipt.GasUsed, txreceipt.BlockNumber.String(), authCount, gasPerAuth, gasPerByte, gweiTotalFee.String(), nil +} + +// sendBatchedMaxBloatingTransactions sends multiple transactions when size limit is exceeded +func (s *Scenario) sendBatchedMaxBloatingTransactions(ctx context.Context, batches [][]types.SetCodeAuthorization, txCallData []byte, feeCap, tipCap *big.Int, amount *uint256.Int, toAddr common.Address, targetGasLimit uint64, wallet *spamoor.Wallet, client *spamoor.Client) (uint64, string, int, float64, float64, string, error) { + // Aggregate results + var totalGasUsed uint64 + var totalAuthCount int + var totalFees *big.Int = big.NewInt(0) + var lastBlockNumber string + + // Create result channels for all batches upfront + wg := sync.WaitGroup{} + wg.Add(len(batches)) + + txreceipts := make([]*types.Receipt, len(batches)) + txerrs := make([]error, len(batches)) + + // Send all batches quickly with minimal delay to increase chance of same block inclusion + for batchIndex, batch := range batches { + + err := func(batchIndex int, batch []types.SetCodeAuthorization) error { + // Calculate appropriate gas limit for this batch based on authorization count + // Each authorization needs ~26000 gas, plus some overhead for the transaction itself + batchGasLimit := uint64(len(batch))*GasPerAuthorization + BaseTransferCost + uint64(len(txCallData)*GasPerCallDataByte) + + // Ensure we don't exceed the target limit per transaction + maxGasPerTx := targetGasLimit + if batchGasLimit > maxGasPerTx { + batchGasLimit = maxGasPerTx + } + + // Build the transaction for this batch + txData, err := txbuilder.SetCodeTx(&txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: batchGasLimit, + To: &toAddr, + Value: amount, + Data: txCallData, + AuthList: batch, + }) + if err != nil { + return fmt.Errorf("failed to build batch %d transaction metadata: %w", batchIndex+1, err) + } + + tx, err := wallet.BuildSetCodeTx(txData) + if err != nil { + return fmt.Errorf("failed to build batch %d transaction: %w", batchIndex+1, err) + } + + // Send the transaction immediately without waiting for confirmation + err = s.walletPool.GetTxPool().SendTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: true, + OnComplete: func(tx *types.Transaction, receipt *types.Receipt, err error) { + txreceipts[batchIndex] = receipt + txerrs[batchIndex] = err + wg.Done() + }, + LogFn: func(client *spamoor.Client, retry int, rebroadcast int, err error) { + logger := s.logger.WithField("rpc", client.GetName()) + if err != nil { + logger.Errorf("failed sending batch %d tx: %v", batchIndex+1, err) + } else if retry > 0 || rebroadcast > 0 { + logger.Debugf("successfully sent batch %d tx (retry/rebroadcast)", batchIndex+1) + } + }, + }) + + if err != nil { + wallet.ResetPendingNonce(ctx, client) + return fmt.Errorf("failed to send batch %d transaction: %w", batchIndex+1, err) + } + + return nil + }(batchIndex, batch) + + if err != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("failed to send batch %d transaction: %w", batchIndex+1, err) + } + } + + // Now wait for all batch confirmations + wg.Wait() + + blockNumbers := make(map[string]int) // Track which blocks contain our transactions + batchDetails := make([]string, len(batches)) // Store details of each batch + for batchIndex := range batches { + if txerrs[batchIndex] != nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("batch %d failed: %w", batchIndex+1, txerrs[batchIndex]) + } + + txreceipt := txreceipts[batchIndex] + if txreceipt == nil { + return 0, "", 0, 0, 0, "", fmt.Errorf("batch %d: no receipt received", batchIndex+1) + } + + effectiveGasPrice := txreceipt.EffectiveGasPrice + if effectiveGasPrice == nil { + effectiveGasPrice = big.NewInt(0) + } + feeAmount := new(big.Int).Mul(effectiveGasPrice, big.NewInt(int64(txreceipt.GasUsed))) + + gweiTotalFee := new(big.Int).Div(feeAmount, big.NewInt(GweiPerEth)) + + // Aggregate successful results + totalGasUsed += txreceipt.GasUsed + totalAuthCount += len(batches[batchIndex]) + lastBlockNumber = txreceipt.BlockNumber.String() + + // Track block numbers + blockNumbers[txreceipt.BlockNumber.String()]++ + + // Parse and add fee + totalFees.Add(totalFees, gweiTotalFee) + + // Store batch details + batchGasInM := float64(txreceipt.GasUsed) / GasPerMillion + gasPerAuthBatch := float64(txreceipt.GasUsed) / float64(len(batches[batchIndex])) + gasPerByteBatch := gasPerAuthBatch / EstimatedBytesPerAuth + + // Calculate tx size based on authorizations + txSize := s.calculateTransactionSize(len(batches[batchIndex]), len(txCallData)) + sizeKiB := float64(txSize) / BytesPerKiB + + batchDetails[batchIndex] = fmt.Sprintf("Batch %d/%d: %.2fM gas, %d auths, %.2f KiB, %.2f gas/auth, %.2f gas/byte, (block %s)", + batchIndex+1, len(batches), batchGasInM, len(batches[batchIndex]), sizeKiB, gasPerAuthBatch, gasPerByteBatch, txreceipt.BlockNumber.String()) + } + + // Calculate aggregate metrics + gasPerAuth := float64(totalGasUsed) / float64(totalAuthCount) + gasPerByte := gasPerAuth / EstimatedBytesPerAuth + totalGasInM := float64(totalGasUsed) / GasPerMillion + + // Build block distribution summary + var blockDistribution strings.Builder + for blockNum, txCount := range blockNumbers { + if blockDistribution.Len() > 0 { + blockDistribution.WriteString(", ") + } + blockDistribution.WriteString(fmt.Sprintf("Block #%s: %d tx", blockNum, txCount)) + } + + // Create comprehensive summary log with decorative border + s.logger.WithField("scenario", "eoa-delegation").Infof(`════════════════ BATCHED MAX BLOATING SUMMARY ════════════════ +Individual Batches: +%s + +Block Distribution: %s + +Aggregate Metrics: +- Total Gas Used: %.2fM +- Total Authorizations: %d +- Gas per Auth: %.2f +- Gas per Byte: %.2f`, + strings.Join(batchDetails, "\n"), + blockDistribution.String(), + totalGasInM, + totalAuthCount, + gasPerAuth, + gasPerByte) + + return totalGasUsed, lastBlockNumber, totalAuthCount, gasPerAuth, gasPerByte, totalFees.String(), nil +} + +// eoaWorker runs in a separate goroutine and writes funded EOAs to EOAs.json +func (s *Scenario) eoaWorker(ctx context.Context) { + for { + select { + case <-ctx.Done(): + // Shutdown signal received + return + case <-time.After(30 * time.Second): // flush every 30 seconds + s.processEOAQueue() + } + } +} + +// processEOAQueue drains the EOA queue and writes entries to EOAs.json +func (s *Scenario) processEOAQueue() { + // Check if there are items in the queue + s.eoaQueueMutex.Lock() + if len(s.eoaQueue) == 0 { + s.eoaQueueMutex.Unlock() + return // Queue is empty, exit processing + } + + // Dequeue all items (FIFO) + eoasToWrite := make([]EOAEntry, len(s.eoaQueue)) + copy(eoasToWrite, s.eoaQueue) + s.eoaQueue = s.eoaQueue[:0] // Clear the queue + s.eoaQueueMutex.Unlock() + + if s.options.EoaFile != "" { + // Write to file + err := s.writeEOAsToFile(eoasToWrite) + if err != nil { + s.logger.Errorf("failed to write EOAs to file: %v", err) + // Re-queue the items if write failed + s.eoaQueueMutex.Lock() + s.eoaQueue = append(eoasToWrite, s.eoaQueue...) + s.eoaQueueMutex.Unlock() + return + } + } +} + +// writeEOAsToFile appends EOA entries to EOAs.json file +func (s *Scenario) writeEOAsToFile(eoas []EOAEntry) error { + if len(eoas) == 0 { + return nil + } + + fileName := "EOAs.json" + + // Read existing entries if file exists + var existingEntries []EOAEntry + if data, err := os.ReadFile(fileName); err == nil { + json.Unmarshal(data, &existingEntries) + } + + // Append new entries + allEntries := append(existingEntries, eoas...) + + // Write back to file + data, err := json.MarshalIndent(allEntries, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal EOA entries: %w", err) + } + + err = os.WriteFile(fileName, data, 0644) + if err != nil { + return fmt.Errorf("failed to write EOAs.json: %w", err) + } + + return nil +} + +// addEOAToQueue adds a funded EOA to the queue +func (s *Scenario) addEOAToQueue(address, privateKey string) { + s.eoaQueueMutex.Lock() + defer s.eoaQueueMutex.Unlock() + + entry := EOAEntry{ + Address: address, + PrivateKey: privateKey, + } + + s.eoaQueue = append(s.eoaQueue, entry) +} diff --git a/scenarios/statebloat/erc20_max_transfers/README.md b/scenarios/statebloat/erc20_max_transfers/README.md new file mode 100644 index 00000000..2767073b --- /dev/null +++ b/scenarios/statebloat/erc20_max_transfers/README.md @@ -0,0 +1,81 @@ +# ERC20 Max Transfers Scenario + +This scenario maximizes the number of ERC20 token transfers per block to unique recipient addresses, creating state bloat through new account storage entries. + +## Overview + +The scenario uses deployed StateBloatToken contracts from `deployments.json` to send the maximum possible number of ERC20 transfers per block. Each transfer sends 1 token to a unique, never-before-used address, maximizing state growth. + +## Features + +- **Dynamic Block Gas Limit**: Fetches the actual network block gas limit before starting +- **Self-Adjusting Transfer Count**: Automatically adjusts the number of transfers based on actual gas usage +- **Unique Recipients**: Generates deterministic unique addresses for each transfer +- **Minimum Gas Fees**: Uses configured minimum gas fees (default: 10 gwei base, 5 gwei tip) +- **Round-Robin Contract Usage**: Distributes transfers across multiple deployed contracts +- **Recipient Tracking**: Saves all recipient addresses to `recipients.json` for analysis + +## Configuration + +### Command Line Flags + +- `--basefee`: Max fee per gas in gwei (default: 10) +- `--tipfee`: Max tip per gas in gwei (default: 5) +- `--contract`: Specific contract address to use (default: rotate through all) + +## How It Works + +1. **Initialization**: + - Loads deployed contracts and private key from `deployments.json` + - Sets up the deployer wallet (which holds all tokens) + - Fetches network block gas limit + +2. **Transfer Phase**: + - Calculates optimal transfer count based on gas limit + - Generates unique recipient addresses deterministically + - Sends transfers in batches with minimal delays + - Uses round-robin contract selection + +3. **Analysis Phase**: + - Tracks confirmed transfers and gas usage + - Calculates actual gas per transfer + - Adjusts transfer count for next iteration + - Saves recipient data to file + +4. **Self-Adjustment**: + - If under target gas usage: increases transfers + - If over target gas usage: decreases transfers + - Aims for 99.5% block utilization + +## State Growth Impact + +Each successful transfer creates: +- New account entry for the recipient (~100 bytes) +- Token balance storage slot for the recipient +- Estimated state growth: 100 bytes per transfer + +## Output + +Recipient addresses are saved to `recipients.json` with: +- Address +- Block number +- Tokens sent + +## Requirements + +- Deployed StateBloatToken contracts (via contract_deploy scenario) +- Deployer private key with full token supply +- Sufficient ETH for gas fees + +## Example Usage + +```bash +# Use default settings +./spamoor scenario --scenario erc20-max-transfers + +# Custom gas fees +./spamoor scenario --scenario erc20-max-transfers --basefee 20 --tipfee 10 + +# Use specific contract only +./spamoor scenario --scenario erc20-max-transfers --contract 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 +``` diff --git a/scenarios/statebloat/erc20_max_transfers/erc20_max_transfers.go b/scenarios/statebloat/erc20_max_transfers/erc20_max_transfers.go new file mode 100644 index 00000000..f271a324 --- /dev/null +++ b/scenarios/statebloat/erc20_max_transfers/erc20_max_transfers.go @@ -0,0 +1,811 @@ +package sberc20maxtransfers + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/json" + "fmt" + "math/big" + "os" + "strings" + "sync" + "sync/atomic" + "time" + + "gopkg.in/yaml.v3" + + "github.com/ethereum/go-ethereum/accounts/abi" + //"github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + + "github.com/ethpandaops/spamoor/scenario" + contract "github.com/ethpandaops/spamoor/scenarios/statebloat/contract_deploy/contract" + "github.com/ethpandaops/spamoor/spamoor" + "github.com/ethpandaops/spamoor/txbuilder" +) + +// Constants for ERC20 transfer operations +const ( + // ERC20TransferGasCost - gas cost for a standard ERC20 transfer (updated to 70K) + ERC20TransferGasCost = 70000 + // DefaultBaseFeeGwei - default base fee in gwei + DefaultBaseFeeGwei = 10 + // DefaultTipFeeGwei - default tip fee in gwei + DefaultTipFeeGwei = 5 + // TokenTransferAmount - amount of tokens to transfer (1 token in smallest unit) + TokenTransferAmount = 1 + // DefaultTargetGasRatio - target percentage of block gas limit to use (99.5% for minimal safety margin) + DefaultTargetGasRatio = 0.995 + // FallbackBlockGasLimit - fallback gas limit if network query fails + FallbackBlockGasLimit = 30000000 + // GweiPerEth - conversion factor from Gwei to Wei + GweiPerEth = 1000000000 + // BlockMiningTimeout - timeout for waiting for a new block to be mined + BlockMiningTimeout = 30 * time.Second + // BlockPollingInterval - interval for checking new blocks + BlockPollingInterval = 1 * time.Second + // TransactionBatchSize - number of transactions to send in a batch + TransactionBatchSize = 100 + // TransactionBatchThreshold - threshold for continuing to fill a block + TransactionBatchThreshold = 50 + // InitialTransactionDelay - delay between initial transactions + InitialTransactionDelay = 10 * time.Millisecond + // OptimizedTransactionDelay - reduced delay after initial batch + OptimizedTransactionDelay = 5 * time.Millisecond + // ConfirmationDelay - delay before checking confirmations + ConfirmationDelay = 2 * time.Second + // MaxRebroadcasts - maximum number of times to rebroadcast a transaction + MaxRebroadcasts = 10 + // RetryDelay - delay before retrying failed operations + RetryDelay = 5 * time.Second + // GasPerMillion - divisor for converting gas to millions + GasPerMillion = 1_000_000.0 + // BytesPerKiB - bytes in a kibibyte + BytesPerKiB = 1024.0 + // EstimatedStateGrowthPerTransfer - estimated state growth in bytes per new recipient + EstimatedStateGrowthPerTransfer = 100 + // BloatingSummaryFileName - name of the bloating summary file + BloatingSummaryFileName = "erc20_bloating_summary.json" +) + +// ScenarioOptions defines the configuration options for the scenario +type ScenarioOptions struct { + BaseFee uint64 `yaml:"base_fee"` + TipFee uint64 `yaml:"tip_fee"` + Contract string `yaml:"contract"` + DeploymentsFile string `yaml:"deployments_file"` +} + +// DeploymentEntry represents a contract deployment from deployments.json +type DeploymentEntry map[string][]string + +// ContractBloatStats tracks unique recipients per contract +type ContractBloatStats struct { + UniqueRecipients int `json:"unique_recipients"` +} + +// BloatingSummary represents the JSON file structure +type BloatingSummary struct { + Contracts map[string]*ContractBloatStats `json:"contracts"` + TotalRecipients int `json:"total_recipients"` + LastBlockNumber string `json:"last_block_number"` + LastBlockUpdate time.Time `json:"last_block_update"` +} + +// Scenario implements the ERC20 max transfers scenario +type Scenario struct { + options ScenarioOptions + logger *logrus.Entry + walletPool *spamoor.WalletPool + + // Deployed contracts and private key + deployerPrivateKey string + deployerAddress common.Address + deployerWallet *spamoor.Wallet // Store the wallet instance + deployedContracts []common.Address + currentRoundContract common.Address // Contract being used for current round + contractsLock sync.Mutex + + // Transfer function ABI + transferABI abi.Method + contractABI abi.ABI + + // Used addresses tracking + usedAddresses map[common.Address]bool + usedAddressesLock sync.Mutex + + // Bloating statistics tracking + contractStats map[common.Address]*ContractBloatStats + contractStatsLock sync.Mutex +} + +var ScenarioName = "statebloat-erc20-max-transfers" +var ScenarioDefaultOptions = ScenarioOptions{ + BaseFee: DefaultBaseFeeGwei, + TipFee: DefaultTipFeeGwei, + Contract: "", + DeploymentsFile: "deployments.json", +} +var ScenarioDescriptor = scenario.Descriptor{ + Name: ScenarioName, + Description: "Maximum ERC20 transfers per block to unique addresses", + DefaultOptions: ScenarioDefaultOptions, + NewScenario: newScenario, +} + +func newScenario(logger logrus.FieldLogger) scenario.Scenario { + return &Scenario{ + logger: logger.WithField("scenario", ScenarioName), + usedAddresses: make(map[common.Address]bool), + contractStats: make(map[common.Address]*ContractBloatStats), + } +} + +func (s *Scenario) Flags(flags *pflag.FlagSet) error { + flags.Uint64Var(&s.options.BaseFee, "basefee", ScenarioDefaultOptions.BaseFee, "Max fee per gas to use in transactions (in gwei)") + flags.Uint64Var(&s.options.TipFee, "tipfee", ScenarioDefaultOptions.TipFee, "Max tip per gas to use in transactions (in gwei)") + flags.StringVar(&s.options.Contract, "contract", ScenarioDefaultOptions.Contract, "Specific contract address to use (default: rotate through all)") + flags.StringVar(&s.options.DeploymentsFile, "deployments-file", ScenarioDefaultOptions.DeploymentsFile, "File to save deployments to") + return nil +} + +func (s *Scenario) Init(options *scenario.Options) error { + s.walletPool = options.WalletPool + + if options.Config != "" { + err := yaml.Unmarshal([]byte(options.Config), &s.options) + if err != nil { + return fmt.Errorf("failed to unmarshal config: %w", err) + } + } + + // Load deployed contracts from deployments.json + err := s.loadDeployedContracts() + if err != nil { + return fmt.Errorf("failed to load deployed contracts: %w", err) + } + + // Load transfer function ABI + err = s.loadTransferABI() + if err != nil { + return fmt.Errorf("failed to load transfer ABI: %w", err) + } + + // We'll use the deployer wallet which we'll prepare in runMaxTransfersMode + s.walletPool.SetWalletCount(0) + + return nil +} + +func (s *Scenario) Config() string { + yamlBytes, _ := yaml.Marshal(&s.options) + return string(yamlBytes) +} + +// loadDeployedContracts loads contract addresses and private key from deployments.json +func (s *Scenario) loadDeployedContracts() error { + if s.options.DeploymentsFile == "" { + return fmt.Errorf("deployments file is not set") + } + + data, err := os.ReadFile(s.options.DeploymentsFile) + if err != nil { + return fmt.Errorf("failed to read %v: %w", s.options.DeploymentsFile, err) + } + + var deployments DeploymentEntry + err = json.Unmarshal(data, &deployments) + if err != nil { + return fmt.Errorf("failed to parse deployments.json: %w", err) + } + + // Get the first (and only) entry + for privateKey, addresses := range deployments { + // Trim 0x prefix if present + privateKey = strings.TrimPrefix(privateKey, "0x") + + s.deployerPrivateKey = privateKey + s.deployedContracts = make([]common.Address, len(addresses)) + for i, addr := range addresses { + s.deployedContracts[i] = common.HexToAddress(addr) + } + break // Only process the first entry + } + + if s.deployerPrivateKey == "" || len(s.deployedContracts) == 0 { + return fmt.Errorf("no valid deployments found in deployments.json") + } + + s.logger.Infof("Loaded %d deployed contracts from deployments.json", len(s.deployedContracts)) + + // Initialize contract stats for all deployed contracts + for _, contractAddr := range s.deployedContracts { + s.contractStats[contractAddr] = &ContractBloatStats{ + UniqueRecipients: 0, + } + } + + // If specific contract requested, validate it exists + if s.options.Contract != "" { + contractAddr := common.HexToAddress(s.options.Contract) + found := false + for _, addr := range s.deployedContracts { + if addr == contractAddr { + found = true + s.deployedContracts = []common.Address{contractAddr} // Use only this contract + break + } + } + if !found { + return fmt.Errorf("specified contract %s not found in deployments", s.options.Contract) + } + s.logger.Infof("Using specific contract: %s", contractAddr.Hex()) + } + + return nil +} + +// loadTransferABI loads the transfer function ABI from the contract +func (s *Scenario) loadTransferABI() error { + // Parse the contract ABI to get the transfer method + contractABI, err := abi.JSON(strings.NewReader(contract.StateBloatTokenMetaData.ABI)) + if err != nil { + return fmt.Errorf("failed to parse contract ABI: %w", err) + } + + transferMethod, exists := contractABI.Methods["transfer"] + if !exists { + return fmt.Errorf("transfer method not found in contract ABI") + } + + s.transferABI = transferMethod + s.contractABI = contractABI + return nil +} + +// getNetworkBlockGasLimit retrieves the current block gas limit from the network +// It waits for a new block to be mined (with timeout) to ensure fresh data +func (s *Scenario) getNetworkBlockGasLimit(ctx context.Context, client *spamoor.Client) uint64 { + // Create a timeout context for the entire operation + timeoutCtx, cancel := context.WithTimeout(ctx, BlockMiningTimeout) + defer cancel() + + // Get the current block number first + currentBlockNumber, err := client.GetEthClient().BlockNumber(timeoutCtx) + if err != nil { + s.logger.Warnf("failed to get current block number: %v, using fallback: %d", err, FallbackBlockGasLimit) + return FallbackBlockGasLimit + } + + s.logger.Debugf("waiting for new block to be mined (current: %d, timeout: %v)", currentBlockNumber, BlockMiningTimeout) + + // Wait for a new block to be mined + ticker := time.NewTicker(BlockPollingInterval) + defer ticker.Stop() + + var latestBlock *types.Block + for { + select { + case <-timeoutCtx.Done(): + s.logger.Warnf("timeout waiting for new block to be mined, using fallback: %d", FallbackBlockGasLimit) + return FallbackBlockGasLimit + case <-ticker.C: + // Check for a new block + newBlockNumber, err := client.GetEthClient().BlockNumber(timeoutCtx) + if err != nil { + s.logger.Debugf("error checking block number: %v", err) + continue + } + + // If we have a new block, get its details + if newBlockNumber > currentBlockNumber { + latestBlock, err = client.GetEthClient().BlockByNumber(timeoutCtx, nil) + if err != nil { + s.logger.Debugf("error getting latest block details: %v", err) + continue + } + s.logger.Debugf("new block mined: %d", newBlockNumber) + goto blockFound + } + } + } + +blockFound: + gasLimit := latestBlock.GasLimit() + s.logger.Debugf("network block gas limit from fresh block #%d: %d", latestBlock.NumberU64(), gasLimit) + return gasLimit +} + +// generateRecipient generates a deterministic recipient address based on index +func (s *Scenario) generateRecipient(recipientIndex uint64) common.Address { + idxBytes := make([]byte, 8) + binary.BigEndian.PutUint64(idxBytes, recipientIndex) + // Use deployer address as seed for deterministic generation + hash := sha256.Sum256(append(s.deployerAddress.Bytes(), idxBytes...)) + return common.BytesToAddress(hash[12:]) // Use last 20 bytes as address +} + +// loadBloatingSummary loads the bloating summary from file or creates a new one +func (s *Scenario) loadBloatingSummary() (*BloatingSummary, error) { + data, err := os.ReadFile(BloatingSummaryFileName) + if err != nil { + if os.IsNotExist(err) { + // File doesn't exist, return new summary + return &BloatingSummary{ + Contracts: make(map[string]*ContractBloatStats), + TotalRecipients: 0, + }, nil + } + return nil, fmt.Errorf("failed to read bloating summary: %w", err) + } + + var summary BloatingSummary + if err := json.Unmarshal(data, &summary); err != nil { + return nil, fmt.Errorf("failed to unmarshal bloating summary: %w", err) + } + + // Ensure contracts map is initialized + if summary.Contracts == nil { + summary.Contracts = make(map[string]*ContractBloatStats) + } + + return &summary, nil +} + +// saveBloatingSummary saves the bloating summary to file +func (s *Scenario) saveBloatingSummary(summary *BloatingSummary) error { + data, err := json.MarshalIndent(summary, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal bloating summary: %w", err) + } + + if err := os.WriteFile(BloatingSummaryFileName, data, 0644); err != nil { + return fmt.Errorf("failed to write bloating summary: %w", err) + } + + return nil +} + +// updateContractStats updates the statistics for a contract when a transfer is confirmed +func (s *Scenario) updateContractStats(contractAddr common.Address) { + s.contractStatsLock.Lock() + defer s.contractStatsLock.Unlock() + + stats, exists := s.contractStats[contractAddr] + if !exists { + stats = &ContractBloatStats{ + UniqueRecipients: 0, + } + s.contractStats[contractAddr] = stats + } + stats.UniqueRecipients++ +} + +// updateAndSaveBloatingSummary updates the bloating summary with current stats and saves to file +func (s *Scenario) updateAndSaveBloatingSummary(blockNumber string) error { + // Load existing summary + summary, err := s.loadBloatingSummary() + if err != nil { + return err + } + + // Update with current stats + s.contractStatsLock.Lock() + totalRecipients := 0 + for contractAddr, stats := range s.contractStats { + contractHex := contractAddr.Hex() + summary.Contracts[contractHex] = &ContractBloatStats{ + UniqueRecipients: stats.UniqueRecipients, + } + totalRecipients += stats.UniqueRecipients + } + s.contractStatsLock.Unlock() + + // Update summary metadata + summary.TotalRecipients = totalRecipients + summary.LastBlockNumber = blockNumber + summary.LastBlockUpdate = time.Now() + + // Save to file + return s.saveBloatingSummary(summary) +} + +// getContractBloatingSummaryForBlock returns a formatted string with contract bloating info for latest block +func (s *Scenario) getContractBloatingSummaryForBlock() string { + s.contractStatsLock.Lock() + defer s.contractStatsLock.Unlock() + + // Get current round contract + s.contractsLock.Lock() + currentContract := s.currentRoundContract + s.contractsLock.Unlock() + + if currentContract == (common.Address{}) { + return "No contract selected for current round" + } + + // Get stats for current contract + stats, exists := s.contractStats[currentContract] + if !exists { + return fmt.Sprintf("CONTRACT BLOATING STATUS:\n Round Contract: %s - No transfers yet", currentContract.Hex()) + } + + return fmt.Sprintf("CONTRACT BLOATING STATUS:\n Round Contract: %s - %d unique recipients", + currentContract.Hex(), stats.UniqueRecipients) +} + +// selectRandomContract randomly selects a contract from the deployed contracts +func (s *Scenario) selectRandomContract() (common.Address, error) { + s.contractsLock.Lock() + defer s.contractsLock.Unlock() + + if len(s.deployedContracts) == 0 { + return common.Address{}, fmt.Errorf("no deployed contracts available") + } + + // If only one contract, return it + if len(s.deployedContracts) == 1 { + return s.deployedContracts[0], nil + } + + // Generate random index + max := big.NewInt(int64(len(s.deployedContracts))) + n, err := rand.Int(rand.Reader, max) + if err != nil { + return common.Address{}, fmt.Errorf("failed to generate random number: %w", err) + } + + return s.deployedContracts[n.Int64()], nil +} + +func (s *Scenario) Run(ctx context.Context) error { + return s.runMaxTransfersMode(ctx) +} + +func (s *Scenario) runMaxTransfersMode(ctx context.Context) error { + s.logger.Infof("starting max transfers mode: self-adjusting to target block gas limit, continuous operation") + + // Get a client for network operations + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + + // Get the actual network block gas limit + networkGasLimit := s.getNetworkBlockGasLimit(ctx, client) + targetGas := uint64(float64(networkGasLimit) * DefaultTargetGasRatio) + + // Calculate initial transfer count based on network gas limit and known gas cost per transfer + initialTransfers := int(targetGas / ERC20TransferGasCost) + + // Dynamic transfer count - starts based on network parameters and adjusts based on actual performance + currentTransfers := initialTransfers + + // Prepare the deployer wallet if not already done + if s.deployerWallet == nil { + // Create wallet from deployer private key + deployerWallet, err := spamoor.NewWallet(s.deployerPrivateKey) + if err != nil { + return fmt.Errorf("failed to create deployer wallet: %w", err) + } + + // Update wallet with chain info using the client + err = client.UpdateWallet(ctx, deployerWallet) + if err != nil { + return fmt.Errorf("failed to update deployer wallet: %w", err) + } + + // Store the wallet instance + s.deployerWallet = deployerWallet + s.deployerAddress = deployerWallet.GetAddress() + + s.logger.Infof("Initialized deployer wallet - Address: %s, Nonce: %d, Balance: %s ETH", + s.deployerAddress.Hex(), deployerWallet.GetNonce(), new(big.Int).Div(deployerWallet.GetBalance(), big.NewInt(1e18)).String()) + } + + var blockCounter int + var totalTransfers uint64 + var totalUniqueRecipients uint64 + + for { + select { + case <-ctx.Done(): + s.logger.Errorf("max transfers mode stopping due to context cancellation") + return ctx.Err() + default: + } + + blockCounter++ + + // Send the max transfer transactions and wait for confirmation + s.logger.Infof("════════════════ TRANSFER PHASE #%d ════════════════", blockCounter) + actualGasUsed, blockNumber, transferCount, gasPerTransfer, uniqueRecipients, err := s.sendMaxTransfers(ctx, s.deployerWallet, currentTransfers, targetGas, blockCounter, client) + if err != nil { + s.logger.Errorf("failed to send max transfers for iteration %d: %v", blockCounter, err) + time.Sleep(RetryDelay) // Wait before retry + continue + } + + // Update totals + totalTransfers += uint64(transferCount) + totalUniqueRecipients += uint64(uniqueRecipients) + + s.logger.Infof("%%%%%%%%%%%%%%%%%%%% ANALYSIS PHASE #%d %%%%%%%%%%%%%%%%%%%%", blockCounter) + + // Calculate metrics + blockGasLimit := float64(networkGasLimit) + gasUtilization := (float64(actualGasUsed) / blockGasLimit) * 100 + estimatedStateGrowth := uniqueRecipients * EstimatedStateGrowthPerTransfer + + s.logger.WithField("scenario", ScenarioName).Infof("TRANSFER METRICS - Block #%s | Transfers: %d | Unique recipients: %d | Gas used: %.2fM | Block utilization: %.2f%% | Gas/transfer: %.1f | Est. state growth: %.2f KiB", + blockNumber, transferCount, uniqueRecipients, float64(actualGasUsed)/GasPerMillion, gasUtilization, gasPerTransfer, float64(estimatedStateGrowth)/BytesPerKiB) + + // Log contract-specific bloating info + s.logger.WithField("scenario", ScenarioName).Info(s.getContractBloatingSummaryForBlock()) + + // Log cumulative metrics + s.logger.WithField("scenario", ScenarioName).Infof("CUMULATIVE TOTALS - Total transfers: %d | Total unique recipients: %d | Avg transfers/block: %.1f", + totalTransfers, totalUniqueRecipients, float64(totalTransfers)/float64(blockCounter)) + + // Update and save bloating summary + err = s.updateAndSaveBloatingSummary(blockNumber) + if err != nil { + s.logger.Warnf("Failed to update bloating summary: %v", err) + } + + // Self-adjust transfer count based on actual performance + if actualGasUsed > 0 && transferCount > 0 { + avgGasPerTransfer := float64(actualGasUsed) / float64(transferCount) + targetTransfers := int(float64(targetGas) / avgGasPerTransfer) + + // Calculate the adjustment needed + transferDifference := targetTransfers - transferCount + + if actualGasUsed < targetGas { + // We're under target, increase transfer count with a slight safety margin + newTransfers := currentTransfers + transferDifference - 1 + + if newTransfers > currentTransfers { + s.logger.Infof("Adjusting transfers: %d β†’ %d (need %d more for target)", + currentTransfers, newTransfers, transferDifference) + currentTransfers = newTransfers + } + } else if actualGasUsed > targetGas { + // We're over target, reduce to reach max block utilization + excess := actualGasUsed - targetGas + excessTransfers := int(float64(excess) / avgGasPerTransfer) + newTransfers := currentTransfers - excessTransfers + + s.logger.Infof("Reducing transfers: %d β†’ %d (excess: %d gas, ~%d transfers)", + currentTransfers, newTransfers, excess, excessTransfers) + currentTransfers = newTransfers + + } else { + s.logger.Infof("Target achieved! Gas Used: %d / Target: %d", actualGasUsed, targetGas) + } + } + } +} + +func (s *Scenario) sendMaxTransfers(ctx context.Context, deployerWallet *spamoor.Wallet, targetTransfers int, targetGasLimit uint64, blockCounter int, client *spamoor.Client) (uint64, string, int, float64, int, error) { + // Select a random contract for this round + contractForRound, err := s.selectRandomContract() + if err != nil { + return 0, "", 0, 0, 0, fmt.Errorf("failed to select contract for round: %w", err) + } + + // Update current round contract + s.contractsLock.Lock() + s.currentRoundContract = contractForRound + s.contractsLock.Unlock() + + s.logger.Infof("Selected contract for round #%d: %s", blockCounter, contractForRound.Hex()) + + // Get suggested fees or use configured values + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return 0, "", 0, 0, 0, fmt.Errorf("failed to get suggested fees: %w", err) + } + + // Send transfers in batches + return s.sendTransferBatch(ctx, deployerWallet, targetTransfers, targetGasLimit, blockCounter, client, feeCap, tipCap) +} + +func (s *Scenario) sendTransferBatch(ctx context.Context, wallet *spamoor.Wallet, targetTransfers int, targetGasLimit uint64, iteration int, client *spamoor.Client, feeCap, tipCap *big.Int) (uint64, string, int, float64, int, error) { + var confirmedCount int64 + var uniqueRecipientsCount int64 + var totalGasUsed uint64 + var lastBlockNumber string + + sentCount := 0 + recipientIndex := uint64(iteration * 1000000) // Large offset per iteration to avoid conflicts + + // Calculate approximate transactions per block based on gas limit + maxTxsPerBlock := int(targetGasLimit / ERC20TransferGasCost) + + // Track confirmations + type confirmResult struct { + gasUsed uint64 + blockNumber string + recipient common.Address + contractUsed common.Address + } + // Make channel buffered with enough capacity for all transactions + confirmChan := make(chan confirmResult, targetTransfers*2) // Double buffer to be safe + + // Send transactions + for sentCount < targetTransfers { + // Generate unique recipient address + var recipient common.Address + for { + recipient = s.generateRecipient(recipientIndex) + recipientIndex++ + + // Check if address already used + s.usedAddressesLock.Lock() + if !s.usedAddresses[recipient] { + s.usedAddresses[recipient] = true + s.usedAddressesLock.Unlock() + break + } + s.usedAddressesLock.Unlock() + } + + // Use the contract selected for this round + s.contractsLock.Lock() + contractAddr := s.currentRoundContract + s.contractsLock.Unlock() + + // Encode transfer call data + transferAmount := big.NewInt(TokenTransferAmount) + callData, err := s.contractABI.Pack("transfer", recipient, transferAmount) + if err != nil { + s.logger.Errorf("failed to pack transfer call data: %v", err) + continue + } + + // Build transaction + txMetadata := &txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: ERC20TransferGasCost, + To: &contractAddr, + Value: uint256.NewInt(0), // No ETH value for ERC20 transfer + Data: callData, + } + + txData, err := txbuilder.DynFeeTx(txMetadata) + if err != nil { + s.logger.Errorf("failed to create tx data: %v", err) + continue + } + + tx, err := wallet.BuildDynamicFeeTx(txData) + if err != nil { + s.logger.Errorf("failed to build transaction: %v", err) + continue + } + + // Capture values for closure + capturedRecipient := recipient + capturedContract := contractAddr + + // Send transaction + err = s.walletPool.GetTxPool().SendTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: false, // No retries to avoid duplicates + OnComplete: func(tx *types.Transaction, receipt *types.Receipt, err error) { + if err != nil { + return // Don't log individual failures + } + if receipt != nil && receipt.Status == 1 { + atomic.AddInt64(&confirmedCount, 1) + atomic.AddInt64(&uniqueRecipientsCount, 1) + + // Update contract stats + s.updateContractStats(capturedContract) + + // Send result to channel with captured values + confirmChan <- confirmResult{ + gasUsed: receipt.GasUsed, + blockNumber: receipt.BlockNumber.String(), + recipient: capturedRecipient, + contractUsed: capturedContract, + } + } + }, + LogFn: func(client *spamoor.Client, retry int, rebroadcast int, err error) { + // Only log actual send failures + if err != nil { + s.logger.Debugf("transfer tx send failed: %v", err) + } + }, + }) + + if err != nil { + continue + } + + sentCount++ + + // Small delay between transactions to ensure proper nonce ordering + if sentCount < TransactionBatchSize { + time.Sleep(InitialTransactionDelay) + } else if sentCount%maxTxsPerBlock < TransactionBatchThreshold { + time.Sleep(OptimizedTransactionDelay) + } + + // Add context cancellation check + select { + case <-ctx.Done(): + return 0, "", 0, 0, 0, ctx.Err() + default: + } + } + + // Wait for confirmations + s.logger.Infof("Sent %d transfer transactions, waiting for confirmations...", sentCount) + time.Sleep(ConfirmationDelay) + + // Log initial confirmation status + initialConfirmed := atomic.LoadInt64(&confirmedCount) + if initialConfirmed > 0 { + s.logger.Debugf("Already have %d confirmations before collection", initialConfirmed) + } + + // Collect results - wait for all sent transactions or timeout + confirmTimeout := time.After(30 * time.Second) + resultCount := 0 + +collectResults: + for resultCount < sentCount { + select { + case result := <-confirmChan: + totalGasUsed += result.gasUsed + lastBlockNumber = result.blockNumber + resultCount++ + + case <-confirmTimeout: + // Final check for any remaining confirmations + confirmed := atomic.LoadInt64(&confirmedCount) + s.logger.Warnf("Timeout waiting for confirmations, received %d results, %d confirmed, %d sent", resultCount, confirmed, sentCount) + break collectResults + + case <-ctx.Done(): + return 0, "", 0, 0, 0, ctx.Err() + } + } + + // Drain any remaining results from the channel (non-blocking) + for { + select { + case result := <-confirmChan: + totalGasUsed += result.gasUsed + lastBlockNumber = result.blockNumber + resultCount++ + default: + // No more results available + goto done + } + } +done: + + // Calculate metrics + confirmed := atomic.LoadInt64(&confirmedCount) + uniqueRecipients := atomic.LoadInt64(&uniqueRecipientsCount) + + // Log detailed confirmation statistics + s.logger.Debugf("Confirmation stats: sent=%d, confirmed=%d, results=%d, gas=%d", + sentCount, confirmed, resultCount, totalGasUsed) + + if confirmed == 0 { + return 0, "", 0, 0, 0, fmt.Errorf("no transfers confirmed") + } + + gasPerTransfer := float64(totalGasUsed) / float64(confirmed) + + return totalGasUsed, lastBlockNumber, int(confirmed), gasPerTransfer, int(uniqueRecipients), nil +} diff --git a/scenarios/statebloat/rand_sstore/README.md b/scenarios/statebloat/rand_sstore/README.md new file mode 100644 index 00000000..f7a9c6b6 --- /dev/null +++ b/scenarios/statebloat/rand_sstore/README.md @@ -0,0 +1,124 @@ +# πŸ”₯ Random SSTORE State Bloater + +This scenario maximizes state growth by performing the maximum number of SSTORE operations per block using random key distribution. + +## πŸ› οΈ Contract Compilation + +### Prerequisites +- Solidity compiler (solc) version 0.8.30 or compatible +- Go 1.16+ (for go:embed directive) + +### Compiling the Contract + +To compile the SSTOREStorageBloater contract: + +```bash +cd scenarios/statebloat/rand_sstore_bloater/contract +solc --optimize --optimize-runs 200 --combined-json abi,bin SSTOREStorageBloater.sol +``` + +### Extracting ABI and Bytecode + +The compilation output is in JSON format. Extract the ABI and bytecode: + +```bash +# Extract ABI (already done) +jq -r '.contracts["SSTOREStorageBloater.sol:SSTOREStorageBloater"].abi' < output.json > SSTOREStorageBloater.abi + +# Extract bytecode (already done) +jq -r '.contracts["SSTOREStorageBloater.sol:SSTOREStorageBloater"].bin' < output.json > SSTOREStorageBloater.bin +``` + +### Regenerating Go Bindings (Optional) + +If you need to regenerate the Go bindings: + +```bash +abigen --abi SSTOREStorageBloater.abi --bin SSTOREStorageBloater.bin --pkg contract --out SSTOREStorageBloater.go +``` + +**Note**: The Go scenario code uses `go:embed` directives to automatically include the ABI and bytecode files at compile time. + +## How it Works + +1. **Contract Deployment**: Deploys an optimized `SSTOREStorageBloater` contract that uses assembly for minimal overhead +2. **Two-Stage Process**: + - **Stage 1**: Creates new storage slots (0 β†’ non-zero transitions) + - **Stage 2**: Updates existing storage slots (non-zero β†’ non-zero transitions) +3. **Key Distribution**: Uses curve25519 prime multiplication to distribute keys across the entire storage space, maximizing trie node creation +4. **Adaptive Gas Estimation**: Dynamically adjusts gas estimates based on actual usage to maximize slots per transaction + +## β›½ Gas Cost Breakdown + +### Actual Gas Costs (Measured) +The actual gas cost per SSTORE operation is higher than the base opcode cost due to additional overhead: + +**For New Slots (0 β†’ non-zero):** +- Base SSTORE cost: 20,000 gas +- Assembly loop overhead per iteration: + - MULMOD for key calculation: ~8 gas + - TIMESTAMP calls: ~2 gas + - AND operation: ~3 gas + - Loop control (JUMPI, LT, ADD): ~10 gas +- **Total: ~22,000 gas per slot** + +**For Updates (non-zero β†’ non-zero):** +- Base SSTORE cost: 5,000 gas +- Same assembly overhead: ~2,000 gas +- **Total: ~7,000 gas per slot** + +**Transaction Overhead:** +- Base transaction cost: 21,000 gas +- Function selector matching: ~100 gas +- ABI decoding (uint256 parameter): ~1,000 gas +- Contract code loading: ~2,600 gas +- Memory allocation: ~1,000 gas +- Function dispatch: ~300 gas +- Return handling: ~1,000 gas +- Safety margin: ~73,000 gas +- **Total: ~100,000 gas overhead** + +Example with 30M gas limit block (97% utilization): +- Stage 1: ~1,300 new slots per block +- Stage 2: ~4,100 slot updates per block + +## πŸš€ Usage + +### Build +```bash +go build -o bin/spamoor cmd/spamoor/main.go +``` + +### Run +```bash +./bin/spamoor --privkey --rpchost http://localhost:8545 rand_sstore_bloater [flags] +``` + +#### Flags +- `--basefee` - Base fee per gas in gwei (default: 10) +- `--tipfee` - Tip fee per gas in gwei (default: 2) + +### Example +```bash +./bin/spamoor --privkey ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ + --rpchost http://localhost:8545 rand_sstore_bloater +``` + +## πŸ“Š Deployment Tracking + +The scenario tracks all contract deployments and storage operations in `deployments_sstore_bloating.json`. This file enables future scenarios to perform targeted SLOAD operations on known storage slots. + +### File Format +```json +{ + "0xContractAddress": { + "storage_rounds": [ + { + "block_number": 123, + "timestamp": 1234567890 + }, + ... + ] + } +} +``` \ No newline at end of file diff --git a/scenarios/statebloat/rand_sstore/contract/.gitignore b/scenarios/statebloat/rand_sstore/contract/.gitignore new file mode 100644 index 00000000..f9096e53 --- /dev/null +++ b/scenarios/statebloat/rand_sstore/contract/.gitignore @@ -0,0 +1,3 @@ +*.output.json +*.bin +*.abi \ No newline at end of file diff --git a/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.go b/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.go new file mode 100644 index 00000000..6defd74e --- /dev/null +++ b/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.go @@ -0,0 +1,224 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// SSTOREStorageBloaterMetaData contains all meta data concerning the SSTOREStorageBloater contract. +var SSTOREStorageBloaterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"createSlots\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060f48061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063e3b393a414602d575b600080fd5b603c603836600460a7565b603e565b005b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed816001430340421860005b8281101560a0577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84838301098055600101606a565b5050505050565b60006020828403121560b7578081fd5b503591905056fea264697066735822122079e4ae597ac68792390217bfee59415f78d7e370132ab6590720d9f7898a50be64736f6c63430008000033", +} + +// SSTOREStorageBloaterABI is the input ABI used to generate the binding from. +// Deprecated: Use SSTOREStorageBloaterMetaData.ABI instead. +var SSTOREStorageBloaterABI = SSTOREStorageBloaterMetaData.ABI + +// SSTOREStorageBloaterBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use SSTOREStorageBloaterMetaData.Bin instead. +var SSTOREStorageBloaterBin = SSTOREStorageBloaterMetaData.Bin + +// DeploySSTOREStorageBloater deploys a new Ethereum contract, binding an instance of SSTOREStorageBloater to it. +func DeploySSTOREStorageBloater(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SSTOREStorageBloater, error) { + parsed, err := SSTOREStorageBloaterMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SSTOREStorageBloaterBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &SSTOREStorageBloater{SSTOREStorageBloaterCaller: SSTOREStorageBloaterCaller{contract: contract}, SSTOREStorageBloaterTransactor: SSTOREStorageBloaterTransactor{contract: contract}, SSTOREStorageBloaterFilterer: SSTOREStorageBloaterFilterer{contract: contract}}, nil +} + +// SSTOREStorageBloater is an auto generated Go binding around an Ethereum contract. +type SSTOREStorageBloater struct { + SSTOREStorageBloaterCaller // Read-only binding to the contract + SSTOREStorageBloaterTransactor // Write-only binding to the contract + SSTOREStorageBloaterFilterer // Log filterer for contract events +} + +// SSTOREStorageBloaterCaller is an auto generated read-only Go binding around an Ethereum contract. +type SSTOREStorageBloaterCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SSTOREStorageBloaterTransactor is an auto generated write-only Go binding around an Ethereum contract. +type SSTOREStorageBloaterTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SSTOREStorageBloaterFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type SSTOREStorageBloaterFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SSTOREStorageBloaterSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type SSTOREStorageBloaterSession struct { + Contract *SSTOREStorageBloater // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SSTOREStorageBloaterCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type SSTOREStorageBloaterCallerSession struct { + Contract *SSTOREStorageBloaterCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// SSTOREStorageBloaterTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type SSTOREStorageBloaterTransactorSession struct { + Contract *SSTOREStorageBloaterTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SSTOREStorageBloaterRaw is an auto generated low-level Go binding around an Ethereum contract. +type SSTOREStorageBloaterRaw struct { + Contract *SSTOREStorageBloater // Generic contract binding to access the raw methods on +} + +// SSTOREStorageBloaterCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type SSTOREStorageBloaterCallerRaw struct { + Contract *SSTOREStorageBloaterCaller // Generic read-only contract binding to access the raw methods on +} + +// SSTOREStorageBloaterTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type SSTOREStorageBloaterTransactorRaw struct { + Contract *SSTOREStorageBloaterTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewSSTOREStorageBloater creates a new instance of SSTOREStorageBloater, bound to a specific deployed contract. +func NewSSTOREStorageBloater(address common.Address, backend bind.ContractBackend) (*SSTOREStorageBloater, error) { + contract, err := bindSSTOREStorageBloater(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &SSTOREStorageBloater{SSTOREStorageBloaterCaller: SSTOREStorageBloaterCaller{contract: contract}, SSTOREStorageBloaterTransactor: SSTOREStorageBloaterTransactor{contract: contract}, SSTOREStorageBloaterFilterer: SSTOREStorageBloaterFilterer{contract: contract}}, nil +} + +// NewSSTOREStorageBloaterCaller creates a new read-only instance of SSTOREStorageBloater, bound to a specific deployed contract. +func NewSSTOREStorageBloaterCaller(address common.Address, caller bind.ContractCaller) (*SSTOREStorageBloaterCaller, error) { + contract, err := bindSSTOREStorageBloater(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SSTOREStorageBloaterCaller{contract: contract}, nil +} + +// NewSSTOREStorageBloaterTransactor creates a new write-only instance of SSTOREStorageBloater, bound to a specific deployed contract. +func NewSSTOREStorageBloaterTransactor(address common.Address, transactor bind.ContractTransactor) (*SSTOREStorageBloaterTransactor, error) { + contract, err := bindSSTOREStorageBloater(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SSTOREStorageBloaterTransactor{contract: contract}, nil +} + +// NewSSTOREStorageBloaterFilterer creates a new log filterer instance of SSTOREStorageBloater, bound to a specific deployed contract. +func NewSSTOREStorageBloaterFilterer(address common.Address, filterer bind.ContractFilterer) (*SSTOREStorageBloaterFilterer, error) { + contract, err := bindSSTOREStorageBloater(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SSTOREStorageBloaterFilterer{contract: contract}, nil +} + +// bindSSTOREStorageBloater binds a generic wrapper to an already deployed contract. +func bindSSTOREStorageBloater(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SSTOREStorageBloaterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_SSTOREStorageBloater *SSTOREStorageBloaterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SSTOREStorageBloater.Contract.SSTOREStorageBloaterCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_SSTOREStorageBloater *SSTOREStorageBloaterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.SSTOREStorageBloaterTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SSTOREStorageBloater *SSTOREStorageBloaterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.SSTOREStorageBloaterTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_SSTOREStorageBloater *SSTOREStorageBloaterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SSTOREStorageBloater.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_SSTOREStorageBloater *SSTOREStorageBloaterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SSTOREStorageBloater *SSTOREStorageBloaterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.contract.Transact(opts, method, params...) +} + +// CreateSlots is a paid mutator transaction binding the contract method 0xe3b393a4. +// +// Solidity: function createSlots(uint256 count) returns() +func (_SSTOREStorageBloater *SSTOREStorageBloaterTransactor) CreateSlots(opts *bind.TransactOpts, count *big.Int) (*types.Transaction, error) { + return _SSTOREStorageBloater.contract.Transact(opts, "createSlots", count) +} + +// CreateSlots is a paid mutator transaction binding the contract method 0xe3b393a4. +// +// Solidity: function createSlots(uint256 count) returns() +func (_SSTOREStorageBloater *SSTOREStorageBloaterSession) CreateSlots(count *big.Int) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.CreateSlots(&_SSTOREStorageBloater.TransactOpts, count) +} + +// CreateSlots is a paid mutator transaction binding the contract method 0xe3b393a4. +// +// Solidity: function createSlots(uint256 count) returns() +func (_SSTOREStorageBloater *SSTOREStorageBloaterTransactorSession) CreateSlots(count *big.Int) (*types.Transaction, error) { + return _SSTOREStorageBloater.Contract.CreateSlots(&_SSTOREStorageBloater.TransactOpts, count) +} diff --git a/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.sol b/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.sol new file mode 100644 index 00000000..6a48bf81 --- /dev/null +++ b/scenarios/statebloat/rand_sstore/contract/SSTOREStorageBloater.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title SSTOREStorageBloater + * @dev Optimized contract for maximum SSTORE operations using curve25519 prime (2^255 - 19) + * Uses assembly for gas efficiency and distributes keys across storage space + */ +contract SSTOREStorageBloater { + // Counter to track total slots created (stored at slot 0) + uint256 private counter; + + // curve25519 prime: 2^255 - 19 = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed + uint256 private constant CURVE25519_PRIME = + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed; + + /** + * @dev Creates new storage slots (0 -> non-zero transition, ~20k gas each) + * @param count Number of slots to create + */ + function createSlots(uint256 count) external { + assembly { + // Load current counter from storage slot 0 + let prime := CURVE25519_PRIME + let endCounter := count + + // Calculate pseudo-random offset using block data + // XOR timestamp with previous block hash for randomness + let offset := xor(timestamp(), blockhash(sub(number(), 1))) + + // Create slots with distributed keys + for { + let i := 0 + } lt(i, endCounter) { + i := add(i, 1) + } { + // Calculate key = (offset + i) * CURVE25519_PRIME + let key := mulmod(add(offset, i), prime, not(0)) + + // Store value = key + sstore(key, key) + } + } + } +} diff --git a/scenarios/statebloat/rand_sstore/contract/compile.sh b/scenarios/statebloat/rand_sstore/contract/compile.sh new file mode 100755 index 00000000..4c5e0591 --- /dev/null +++ b/scenarios/statebloat/rand_sstore/contract/compile.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +compile_contract() { + local workdir=$1 + local solc_version=$2 + local solc_args=$3 + local contract_file=$4 + local contract_name=$5 + + if [ -z "$contract_name" ]; then + contract_name="$contract_file" + fi + + #echo "docker run --rm -v $workdir:/contracts ethereum/solc:$solc_version /contracts/$contract_file.sol --combined-json abi,bin $solc_args" + local contract_json=$(docker run --rm -v $workdir:/contracts ethereum/solc:$solc_version /contracts/$contract_file.sol --combined-json abi,bin $solc_args) + + local contract_abi=$(echo "$contract_json" | jq -r '.contracts["/contracts/'$contract_file'.sol:'$contract_name'"].abi') + if [ "$contract_abi" == "null" ]; then + contract_abi=$(echo "$contract_json" | jq -r '.contracts["contracts/'$contract_file'.sol:'$contract_name'"].abi') + fi + + local contract_bin=$(echo "$contract_json" | jq -r '.contracts["/contracts/'$contract_file'.sol:'$contract_name'"].bin') + if [ "$contract_bin" == "null" ]; then + contract_bin=$(echo "$contract_json" | jq -r '.contracts["contracts/'$contract_file'.sol:'$contract_name'"].bin') + fi + + echo "$contract_abi" > $contract_name.abi + echo "$contract_bin" > $contract_name.bin + abigen --bin=./$contract_name.bin --abi=./$contract_name.abi --pkg=contract --out=$contract_name.go --type $contract_name + rm $contract_name.bin $contract_name.abi + echo "$contract_json" | jq > $contract_name.output.json +} + +# SSTOREStorageBloater +compile_contract "$(pwd)" 0.8.0 "--optimize --optimize-runs 999999" SSTOREStorageBloater diff --git a/scenarios/statebloat/rand_sstore/rand_sstore_bloater.go b/scenarios/statebloat/rand_sstore/rand_sstore_bloater.go new file mode 100644 index 00000000..5ad0ea89 --- /dev/null +++ b/scenarios/statebloat/rand_sstore/rand_sstore_bloater.go @@ -0,0 +1,433 @@ +package sbrandsstore + +import ( + "context" + _ "embed" + "encoding/json" + "fmt" + "math/big" + "os" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" + "github.com/sirupsen/logrus" + "github.com/spf13/pflag" + "gopkg.in/yaml.v3" + + "github.com/ethpandaops/spamoor/scenario" + "github.com/ethpandaops/spamoor/scenarios/statebloat/rand_sstore/contract" + "github.com/ethpandaops/spamoor/spamoor" + "github.com/ethpandaops/spamoor/txbuilder" + "github.com/ethpandaops/spamoor/utils" +) + +// Constants for SSTORE operations +const ( + // Base Ethereum transaction cost + BaseTxCost = uint64(21000) + + // Function call overhead (measured from actual transactions) + // Includes: function selector, ABI decoding, contract loading, etc. + FunctionCallOverhead = uint64(1556) + + // Gas cost per iteration (measured from actual transactions) + // Includes: SSTORE (0β†’non-zero), MULMOD, loop overhead, stack operations + // Measured: 22,165 gas per iteration + GasPerNewSlotIteration = uint64(22165) + + // Contract deployment and call overhead + EstimatedDeployGas = uint64(500000) // Deployment gas for our contract + + // Safety margins and multipliers + GasLimitSafetyMargin = 0.99 // Use 99% of block gas limit (1% margin for gas price variations) +) + +// BlockInfo stores block information for each storage round +type BlockInfo struct { + BlockNumber uint64 `json:"block_number"` + Timestamp uint64 `json:"timestamp"` +} + +// DeploymentData tracks a single contract deployment and its storage rounds +type DeploymentData struct { + StorageRounds []BlockInfo `json:"storage_rounds"` +} + +// DeploymentFile represents the entire deployment tracking file +type DeploymentFile map[string]*DeploymentData // key is contract address + +type ScenarioOptions struct { + BaseFee uint64 `yaml:"base_fee"` + TipFee uint64 `yaml:"tip_fee"` + DeploymentsFile string `yaml:"deployments_file"` +} + +type Scenario struct { + options ScenarioOptions + logger *logrus.Entry + walletPool *spamoor.WalletPool + + // Contract state + contractAddress common.Address + contractABI abi.ABI + contractInstance *contract.SSTOREStorageBloater // Generated contract binding + isDeployed bool + deployMutex sync.Mutex + + // Scenario state + totalSlots uint64 // Total number of slots created + roundNumber uint64 // Current round number for SSTORE bloating + + // Adaptive gas tracking + actualGasPerNewSlotIteration uint64 // Dynamically adjusted based on actual usage + successfulSlotCounts map[uint64]bool // Track successful slot counts to avoid retries +} + +var ScenarioName = "statebloat-rand-sstore" +var ScenarioDefaultOptions = ScenarioOptions{ + BaseFee: 10, // 10 gwei default + TipFee: 2, // 2 gwei default + DeploymentsFile: "", +} +var ScenarioDescriptor = scenario.Descriptor{ + Name: ScenarioName, + Description: "Maximum state bloat via SSTORE operations using curve25519 prime dispersion", + DefaultOptions: ScenarioDefaultOptions, + NewScenario: newScenario, +} + +func newScenario(logger logrus.FieldLogger) scenario.Scenario { + return &Scenario{ + logger: logger.WithField("scenario", ScenarioName), + actualGasPerNewSlotIteration: GasPerNewSlotIteration, // Start with estimated values + successfulSlotCounts: make(map[uint64]bool), + } +} + +func (s *Scenario) Flags(flags *pflag.FlagSet) error { + flags.Uint64Var(&s.options.BaseFee, "basefee", ScenarioDefaultOptions.BaseFee, "Base fee per gas in gwei") + flags.Uint64Var(&s.options.TipFee, "tipfee", ScenarioDefaultOptions.TipFee, "Tip fee per gas in gwei") + flags.StringVar(&s.options.DeploymentsFile, "deployments-file", ScenarioDefaultOptions.DeploymentsFile, "Deployments file") + return nil +} + +func (s *Scenario) Init(options *scenario.Options) error { + s.walletPool = options.WalletPool + + if options.Config != "" { + err := yaml.Unmarshal([]byte(options.Config), &s.options) + if err != nil { + return fmt.Errorf("failed to unmarshal config: %w", err) + } + } + + s.walletPool.SetWalletCount(1) + s.walletPool.SetRefillAmount(uint256.NewInt(0).Mul(uint256.NewInt(20), uint256.NewInt(1000000000000000000))) // 20 ETH + s.walletPool.SetRefillBalance(uint256.NewInt(0).Mul(uint256.NewInt(10), uint256.NewInt(1000000000000000000))) // 10 ETH + + // register well known wallets + s.walletPool.AddWellKnownWallet(&spamoor.WellKnownWalletConfig{ + Name: "deployer", + RefillAmount: uint256.NewInt(2000000000000000000), // 2 ETH + RefillBalance: uint256.NewInt(1000000000000000000), // 1 ETH + }) + + // Parse contract ABI + parsedABI, err := abi.JSON(strings.NewReader(string(contract.SSTOREStorageBloaterMetaData.ABI))) + if err != nil { + return fmt.Errorf("failed to parse contract ABI: %w", err) + } + s.contractABI = parsedABI + + return nil +} + +func (s *Scenario) Config() string { + yamlBytes, _ := yaml.Marshal(&s.options) + return string(yamlBytes) +} + +// loadDeploymentFile loads the deployment tracking file or creates an empty one +func (s *Scenario) loadDeploymentFile() (DeploymentFile, error) { + if s.options.DeploymentsFile == "" { + return make(DeploymentFile), nil + } + + data, err := os.ReadFile(s.options.DeploymentsFile) + if err != nil { + if os.IsNotExist(err) { + // File doesn't exist, return empty map + return make(DeploymentFile), nil + } + return nil, fmt.Errorf("failed to read deployment file: %w", err) + } + + var deployments DeploymentFile + if err := json.Unmarshal(data, &deployments); err != nil { + return nil, fmt.Errorf("failed to unmarshal deployment file: %w", err) + } + + return deployments, nil +} + +// saveDeploymentFile saves the deployment tracking file +func (s *Scenario) saveDeploymentFile(deployments DeploymentFile) error { + if s.options.DeploymentsFile == "" { + return nil + } + + data, err := json.MarshalIndent(deployments, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal deployment file: %w", err) + } + + if err := os.WriteFile(s.options.DeploymentsFile, data, 0644); err != nil { + return fmt.Errorf("failed to write deployment file: %w", err) + } + + return nil +} + +func (s *Scenario) deployContract(ctx context.Context) error { + s.deployMutex.Lock() + defer s.deployMutex.Unlock() + + if s.isDeployed { + return nil + } + + s.logger.Info("Deploying SSTOREStorageBloater contract...") + + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return fmt.Errorf("no client available") + } + + wallet := s.walletPool.GetWellKnownWallet("deployer") + if wallet == nil { + return fmt.Errorf("no wallet available") + } + + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return err + } + + tx, err := wallet.BuildBoundTx(ctx, &txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: 2000000, + Value: uint256.NewInt(0), + }, func(transactOpts *bind.TransactOpts) (*types.Transaction, error) { + _, deployTx, _, err := contract.DeploySSTOREStorageBloater(transactOpts, client.GetEthClient()) + return deployTx, err + }) + + if err != nil { + return err + } + + txreceipt, err := s.walletPool.GetTxPool().SendAndAwaitTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: true, + }) + if err != nil { + return err + } + + s.contractAddress = txreceipt.ContractAddress + s.contractInstance, err = contract.NewSSTOREStorageBloater(s.contractAddress, client.GetEthClient()) + if err != nil { + return err + } + s.isDeployed = true + + // No need to reset nonce - the wallet manager handles it automatically + + // Track deployment in JSON file + deployments, err := s.loadDeploymentFile() + if err != nil { + s.logger.Warnf("failed to load deployment file: %v", err) + deployments = make(DeploymentFile) + } + + // Initialize deployment data for this contract + deployments[s.contractAddress.Hex()] = &DeploymentData{ + StorageRounds: []BlockInfo{}, + } + + if err := s.saveDeploymentFile(deployments); err != nil { + s.logger.Warnf("failed to save deployment file: %v", err) + } + + s.logger.WithField("address", s.contractAddress.Hex()).Info("SSTOREStorageBloater contract deployed successfully") + + return nil +} + +func (s *Scenario) Run(ctx context.Context) error { + s.logger.Infof("starting scenario: %s", ScenarioName) + defer s.logger.Infof("scenario %s finished.", ScenarioName) + + // Deploy the contract if not already deployed + if !s.isDeployed { + if err := s.deployContract(ctx); err != nil { + return fmt.Errorf("failed to deploy contract: %w", err) + } + } + + // Get network parameters + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return fmt.Errorf("no client available") + } + + // Main loop - alternate between creating and updating slots + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + blockGasLimit, err := s.walletPool.GetTxPool().GetCurrentGasLimitWithInit() + if err != nil { + s.logger.Warnf("failed to get current gas limit: %v", err) + time.Sleep(5 * time.Second) + continue + } + + targetGas := uint64(float64(blockGasLimit) * GasLimitSafetyMargin) + + // Never stop spamming SSTORE operations. + s.roundNumber++ + if err := s.executeCreateSlots(ctx, targetGas, blockGasLimit); err != nil { + s.logger.Errorf("failed to create slots: %v", err) + time.Sleep(5 * time.Second) + continue + } + } +} + +func (s *Scenario) executeCreateSlots(ctx context.Context, targetGas uint64, blockGasLimit uint64) error { + // Calculate how many slots we can create with precise gas costs + // Account for base tx cost and function overhead + availableGas := targetGas - BaseTxCost - FunctionCallOverhead + slotsToCreate := availableGas / s.actualGasPerNewSlotIteration // Integer division rounds down + + if slotsToCreate == 0 { + return fmt.Errorf("not enough gas to create any slots") + } + + // Get client and wallet + client := s.walletPool.GetClient(spamoor.SelectClientByIndex, 0, "") + if client == nil { + return fmt.Errorf("no client available") + } + + wallet := s.walletPool.GetWallet(spamoor.SelectWalletByIndex, 0) + if wallet == nil { + return fmt.Errorf("no wallet available") + } + + // Create transaction options + feeCap, tipCap, err := s.walletPool.GetTxPool().GetSuggestedFees(client, s.options.BaseFee, s.options.TipFee) + if err != nil { + return fmt.Errorf("failed to get suggested fees: %w", err) + } + + tx, err := wallet.BuildBoundTx(ctx, &txbuilder.TxMetadata{ + GasFeeCap: uint256.MustFromBig(feeCap), + GasTipCap: uint256.MustFromBig(tipCap), + Gas: targetGas, + Value: uint256.NewInt(0), + }, func(transactOpts *bind.TransactOpts) (*types.Transaction, error) { + return s.contractInstance.CreateSlots(transactOpts, big.NewInt(int64(slotsToCreate))) + }) + if err != nil { + return err + } + + txreceipt, txerr := s.walletPool.GetTxPool().SendAndAwaitTransaction(ctx, wallet, tx, &spamoor.SendTransactionOptions{ + Client: client, + Rebroadcast: true, + LogFn: spamoor.GetDefaultLogFn(s.logger, "rand-sstore", "", tx), + }) + + if txerr != nil { + return fmt.Errorf("transaction failed: %w", txerr) + } + + if txreceipt == nil || txreceipt.Status != 1 { + // Increase our gas estimate by 10% + s.actualGasPerNewSlotIteration = uint64(float64(s.actualGasPerNewSlotIteration) * 1.1) + + return fmt.Errorf("transaction rejected") + } + + txFees := utils.GetTransactionFees(tx, txreceipt) + s.logger.WithField("rpc", client.GetName()).Debugf(" transaction confirmed in block #%v. total fee: %v gwei (base: %v) logs: %v", txreceipt.BlockNumber.String(), txFees.TotalFeeGwei(), txFees.TxBaseFeeGwei(), len(txreceipt.Logs)) + + // Mark this slot count as successful + s.successfulSlotCounts[slotsToCreate] = true + + // Update metrics and adaptive gas tracking + s.totalSlots += slotsToCreate + totalOverhead := BaseTxCost + FunctionCallOverhead + actualGasPerSlotIteration := (txreceipt.GasUsed - totalOverhead) / slotsToCreate + + // Update our gas estimate using exponential moving average + // New estimate = 0.7 * old estimate + 0.3 * actual + s.actualGasPerNewSlotIteration = uint64(float64(s.actualGasPerNewSlotIteration)*0.7 + float64(actualGasPerSlotIteration)*0.3) + + // Get previous block info for tracking + prevBlockNumber := txreceipt.BlockNumber.Uint64() - 1 + + prevBlock, err := client.GetEthClient().BlockByNumber(ctx, big.NewInt(int64(prevBlockNumber))) + if err != nil { + s.logger.Warnf("failed to get previous block info: %v", err) + } else { + // Track this storage round in deployment file + deployments, err := s.loadDeploymentFile() + if err != nil { + s.logger.Warnf("failed to load deployment file: %v", err) + } else if deployments != nil { + contractAddr := s.contractAddress.Hex() + if deploymentData, exists := deployments[contractAddr]; exists { + // Append new block info + deploymentData.StorageRounds = append(deploymentData.StorageRounds, BlockInfo{ + BlockNumber: prevBlockNumber, + Timestamp: prevBlock.Time(), + }) + + if err := s.saveDeploymentFile(deployments); err != nil { + s.logger.Warnf("failed to save deployment file: %v", err) + } + } + } + } + + // Calculate MB written in this transaction (64 bytes per slot: 32 byte key + 32 byte value) + mbWrittenThisTx := float64(slotsToCreate*64) / (1024 * 1024) + + // Calculate block utilization percentage + blockUtilization := float64(txreceipt.GasUsed) / float64(blockGasLimit) * 100 + + s.logger.WithFields(logrus.Fields{ + "block_number": txreceipt.BlockNumber, + "gas_used": txreceipt.GasUsed, + "slots_created": slotsToCreate, + "gas_per_slot": actualGasPerSlotIteration, + "total_slots": s.totalSlots, + "mb_written": mbWrittenThisTx, + "block_utilization": fmt.Sprintf("%.2f%%", blockUtilization), + }).Info("SSTORE bloating round summary") + + return nil +} diff --git a/spamoor/walletpool.go b/spamoor/walletpool.go index 3fba6ac5..949611ec 100644 --- a/spamoor/walletpool.go +++ b/spamoor/walletpool.go @@ -55,6 +55,16 @@ type WellKnownWalletConfig struct { VeryWellKnown bool } +// ExternalWalletConfig defines configuration for an external wallet imported into the pool. +// External wallets are not used for GetWallet calls but can optionally be funded. +type ExternalWalletConfig struct { + Name string + Wallet *Wallet + RefillAmount *uint256.Int + RefillBalance *uint256.Int + EnableFunding bool +} + // WalletPool manages a pool of child wallets derived from a root wallet with automatic funding // and balance monitoring. It provides wallet selection strategies, automatic refills when balances // drop below thresholds, and batch funding operations for efficiency. @@ -70,9 +80,11 @@ type WalletPool struct { childWallets []*Wallet wellKnownNames []*WellKnownWalletConfig wellKnownWallets map[string]*Wallet + externalWallets []*ExternalWalletConfig selectionMutex sync.Mutex rrWalletIdx int reclaimedFunds bool + preparedWallets bool // Optional callback to track transaction results for metrics transactionTracker func(err error) @@ -112,6 +124,7 @@ func NewWalletPool(ctx context.Context, logger logrus.FieldLogger, rootWallet *R txpool: txpool, childWallets: make([]*Wallet, 0), wellKnownWallets: make(map[string]*Wallet), + externalWallets: make([]*ExternalWalletConfig, 0), runFundings: true, lowBalanceNotifyChan: make(chan struct{}, 100), // buffered channel } @@ -179,6 +192,16 @@ func (pool *WalletPool) AddWellKnownWallet(config *WellKnownWalletConfig) { pool.wellKnownNames = append(pool.wellKnownNames, config) } +// AddExternalWallet adds an external wallet with custom funding configuration. +// External wallets are not part of the deterministic wallet generation but can optionally be tracked and funded. +func (pool *WalletPool) AddExternalWallet(config *ExternalWalletConfig) { + pool.externalWallets = append(pool.externalWallets, config) + + if pool.runFundings && config.EnableFunding && pool.preparedWallets { + config.Wallet.setLowBalanceNotification(pool.lowBalanceNotifyChan, config.RefillBalance.ToBig()) + } +} + // SetRefillAmount sets the amount sent to wallets when they need funding. func (pool *WalletPool) SetRefillAmount(amount *uint256.Int) { pool.config.RefillAmount = amount @@ -189,6 +212,11 @@ func (pool *WalletPool) SetRefillBalance(balance *uint256.Int) { pool.config.RefillBalance = balance } +// GetWalletSeed returns the seed used for deterministic wallet generation. +func (pool *WalletPool) GetWalletSeed() string { + return pool.config.WalletSeed +} + // SetWalletSeed sets the seed used for deterministic wallet generation. // The same seed will always generate the same set of wallets. func (pool *WalletPool) SetWalletSeed(seed string) { @@ -260,6 +288,17 @@ func (pool *WalletPool) GetWellKnownWallet(name string) *Wallet { return pool.wellKnownWallets[name] } +// GetExternalWallet returns an external wallet by name. +// Returns nil if the wallet doesn't exist. +func (pool *WalletPool) GetExternalWallet(name string) *Wallet { + for _, config := range pool.externalWallets { + if config.Name == name { + return config.Wallet + } + } + return nil +} + // GetVeryWellKnownWalletAddress derives the address of a "very well known" wallet // without registering it. Very well known wallets are derived only from the root // wallet's private key and the wallet name, without any scenario seed. @@ -308,17 +347,30 @@ func (pool *WalletPool) GetWalletName(address common.Address) string { } } + for _, config := range pool.externalWallets { + if config.Wallet != nil && config.Wallet.GetAddress() == address { + return config.Name + } + } + return "unknown" } -// GetAllWallets returns a slice containing all wallets (well-known and child wallets). +// GetAllWallets returns a slice containing all wallets (well-known, external, and child wallets). // The root wallet is not included in this list. func (pool *WalletPool) GetAllWallets() []*Wallet { - wallets := make([]*Wallet, len(pool.childWallets)+len(pool.wellKnownWallets)) - for i, config := range pool.wellKnownNames { - wallets[i] = pool.wellKnownWallets[config.Name] + totalCount := len(pool.childWallets) + len(pool.wellKnownWallets) + len(pool.externalWallets) + wallets := make([]*Wallet, totalCount) + idx := 0 + for _, config := range pool.wellKnownNames { + wallets[idx] = pool.wellKnownWallets[config.Name] + idx++ + } + for _, config := range pool.externalWallets { + wallets[idx] = config.Wallet + idx++ } - copy(wallets[len(pool.wellKnownWallets):], pool.childWallets) + copy(wallets[idx:], pool.childWallets) return wallets } @@ -337,13 +389,14 @@ func (pool *WalletPool) GetWalletCount() uint64 { // then funds any wallets below the refill threshold. Also starts the // automatic balance monitoring if funding is enabled. func (pool *WalletPool) PrepareWallets() error { - if len(pool.childWallets) > 0 { + if pool.preparedWallets { return nil } + pool.preparedWallets = true seed := pool.config.WalletSeed - if pool.config.WalletCount == 0 && len(pool.wellKnownWallets) == 0 { + if pool.config.WalletCount == 0 && len(pool.wellKnownWallets) == 0 && len(pool.externalWallets) == 0 { pool.childWallets = make([]*Wallet, 0) } else { var client *Client @@ -392,6 +445,52 @@ func (pool *WalletPool) PrepareWallets() error { }(config) } + for _, config := range pool.externalWallets { + if config.EnableFunding && pool.runFundings { + wg.Add(1) + wl <- true + go func(config *ExternalWalletConfig) { + defer func() { + <-wl + wg.Done() + }() + if walletErr != nil { + return + } + + // Set up low balance notification + if pool.runFundings { + config.Wallet.setLowBalanceNotification(pool.lowBalanceNotifyChan, config.RefillBalance.ToBig()) + } + + err := client.UpdateWallet(pool.ctx, config.Wallet) + if err != nil { + pool.logger.Errorf("could not update external wallet %v: %v", config.Name, err) + walletErr = err + return + } + + refillAmount := pool.config.RefillAmount + refillBalance := pool.config.RefillBalance + if config.RefillAmount != nil { + refillAmount = config.RefillAmount + } + if config.RefillBalance != nil { + refillBalance = config.RefillBalance + } + + if config.Wallet.GetBalance().Cmp(refillBalance.ToBig()) < 0 { + walletsMutex.Lock() + fundingReqs = append(fundingReqs, &FundingRequest{ + Wallet: config.Wallet, + Amount: refillAmount, + }) + walletsMutex.Unlock() + } + }(config) + } + } + for childIdx := uint64(0); childIdx < pool.config.WalletCount; childIdx++ { wg.Add(1) wl <- true @@ -618,7 +717,8 @@ func (pool *WalletPool) resupplyChildWallets() error { wl := make(chan bool, 50) wellKnownCount := uint64(len(pool.wellKnownWallets)) - fundingReqs := make([]*FundingRequest, 0, pool.config.WalletCount+wellKnownCount) + externalCount := uint64(len(pool.externalWallets)) + fundingReqs := make([]*FundingRequest, 0, pool.config.WalletCount+wellKnownCount+externalCount) reqsMutex := &sync.Mutex{} for idx, config := range pool.wellKnownNames { @@ -665,6 +765,47 @@ func (pool *WalletPool) resupplyChildWallets() error { }(idx, wellKnownWallet, config) } + for _, config := range pool.externalWallets { + if config.EnableFunding { + wg.Add(1) + wl <- true + go func(config *ExternalWalletConfig) { + defer func() { + <-wl + wg.Done() + }() + if walletErr != nil { + return + } + + refillAmount := pool.config.RefillAmount + refillBalance := pool.config.RefillBalance + + if config.RefillAmount != nil { + refillAmount = config.RefillAmount + } + if config.RefillBalance != nil { + refillBalance = config.RefillBalance + } + + err := client.UpdateWallet(pool.ctx, config.Wallet) + if err != nil { + walletErr = err + return + } + + if config.Wallet.GetBalance().Cmp(refillBalance.ToBig()) < 0 { + reqsMutex.Lock() + fundingReqs = append(fundingReqs, &FundingRequest{ + Wallet: config.Wallet, + Amount: refillAmount, + }) + reqsMutex.Unlock() + } + }(config) + } + } + for childIdx := uint64(0); childIdx < pool.config.WalletCount; childIdx++ { wg.Add(1) wl <- true @@ -930,7 +1071,7 @@ func (pool *WalletPool) buildWalletReclaimTx(ctx context.Context, childWallet *W return tx, nil } -// collectPoolWallets adds all wallets (root, child, and well-known) to the provided map. +// collectPoolWallets adds all wallets (root, child, well-known, and external) to the provided map. // This is used by the transaction pool to track which addresses belong to this wallet pool. func (pool *WalletPool) collectPoolWallets(walletMap map[common.Address]*Wallet) { walletMap[pool.rootWallet.wallet.GetAddress()] = pool.rootWallet.wallet @@ -940,6 +1081,11 @@ func (pool *WalletPool) collectPoolWallets(walletMap map[common.Address]*Wallet) for _, wallet := range pool.wellKnownWallets { walletMap[wallet.GetAddress()] = wallet } + for _, config := range pool.externalWallets { + if config.Wallet != nil { + walletMap[config.Wallet.GetAddress()] = config.Wallet + } + } } // CheckChildWalletBalance checks and refills a specific wallet if needed. @@ -1041,6 +1187,11 @@ func (pool *WalletPool) ReclaimFunds(ctx context.Context, client *Client) error for _, wallet := range pool.wellKnownWallets { reclaimWallet(wallet) } + for _, config := range pool.externalWallets { + if config.Wallet != nil { + reclaimWallet(config.Wallet) + } + } reclaimWg.Wait() if len(reclaimTxs) > 0 {