├── .gitignore ├── README.md ├── assets └── abi │ ├── aave_abi.json │ ├── classic_pool_factory_address.json │ ├── classic_pool_syncswap_data.json │ ├── deploy.json │ ├── dmail.json │ ├── erc20.json │ ├── eth.json │ ├── eth_claim.json │ ├── l2pass_abi.json │ ├── layerbank.json │ ├── main_bridge.json │ ├── main_bridge_scroll.json │ ├── omnisea_abi.json │ ├── oracle.json │ ├── punkswap.json │ ├── rubyscore_abi.json │ ├── scroll_citizen_abi.json │ ├── skydrome.json │ ├── spacefi.json │ ├── syncswap.json │ ├── zebra_abi.json │ ├── zerius.json │ └── zkstars_abi.json ├── config.py ├── main.py ├── okx_data ├── okx_data.py └── okx_wallets.txt ├── requirements.txt ├── src ├── database │ ├── data │ │ └── analytics_data.py │ ├── models.py │ └── utils.py ├── modules │ ├── bridges │ │ ├── main_bridge │ │ │ ├── main_bridge.py │ │ │ └── utils │ │ │ │ ├── data │ │ │ │ └── proof_data │ │ │ │ │ ├── request_data.py │ │ │ │ │ └── txsbyhashes.py │ │ │ │ └── transaction_data.py │ │ ├── orbiter │ │ │ ├── oribter_bridge.py │ │ │ └── utils │ │ │ │ └── transaction_data.py │ │ └── owlto │ │ │ ├── owlto_bridge.py │ │ │ └── utils │ │ │ └── transaction_data.py │ ├── deploy │ │ ├── contract │ │ │ └── contract.sol │ │ ├── contract_deployer.py │ │ └── utils │ │ │ └── data_exctract.py │ ├── dmail │ │ └── dmail.py │ ├── lendings │ │ ├── aave │ │ │ └── aave.py │ │ └── layerbank │ │ │ └── layerbank.py │ ├── nft │ │ ├── l2pass │ │ │ └── l2pass.py │ │ ├── omnisea │ │ │ └── omnisea.py │ │ ├── scrollcitizen │ │ │ └── scrollcitizen.py │ │ ├── zerius │ │ │ ├── utils │ │ │ │ └── data.py │ │ │ └── zerius.py │ │ └── zkstars │ │ │ └── zkstars.py │ ├── okx_withdraw │ │ ├── okx_withdraw.py │ │ └── utils │ │ │ ├── data.py │ │ │ └── okx_sub_transfer.py │ ├── other │ │ ├── multi_approve │ │ │ └── multi_approve.py │ │ └── rubyscore │ │ │ └── rubyscore.py │ └── swaps │ │ ├── punk_swap │ │ ├── punk_swap.py │ │ └── utils │ │ │ └── transaction_data.py │ │ ├── skydrome │ │ ├── skydrome_swap.py │ │ └── utils │ │ │ └── transaction_data.py │ │ ├── spacefi │ │ ├── spacefi_swap.py │ │ └── utils │ │ │ └── transaction_data.py │ │ ├── sync_swap │ │ ├── sync_swap.py │ │ └── utils │ │ │ └── transaction_data.py │ │ ├── wrapper │ │ ├── eth_wrapper.py │ │ └── transaction_data.py │ │ └── zebra │ │ ├── utils │ │ └── transaction_data.py │ │ └── zebra.py └── utils │ ├── base_bridge.py │ ├── base_liquidity.py │ ├── base_liquidity_remove.py │ ├── base_mint.py │ ├── base_swap.py │ ├── data │ ├── chains.py │ ├── contracts.py │ ├── helper.py │ ├── mappings.py │ ├── tokens.py │ └── types.py │ ├── errors │ └── exceptions.py │ ├── gas_checker.py │ ├── logger.py │ ├── runner.py │ ├── user │ ├── account.py │ └── utils.py │ └── wrappers │ └── decorators.py └── wallets.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,pycharm,venv 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,pycharm,venv 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### macOS Patch ### 34 | # iCloud generated files 35 | *.icloud 36 | 37 | ### PyCharm ### 38 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 39 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 40 | 41 | # User-specific stuff 42 | .idea/**/workspace.xml 43 | .idea/**/tasks.xml 44 | .idea/**/usage.statistics.xml 45 | .idea/**/dictionaries 46 | .idea/**/shelf 47 | 48 | # AWS User-specific 49 | .idea/**/aws.xml 50 | 51 | # Generated files 52 | .idea/**/contentModel.xml 53 | 54 | # Sensitive or high-churn files 55 | .idea/**/dataSources/ 56 | .idea/**/dataSources.ids 57 | .idea/**/dataSources.local.xml 58 | .idea/**/sqlDataSources.xml 59 | .idea/**/dynamic.xml 60 | .idea/**/uiDesigner.xml 61 | .idea/**/dbnavigator.xml 62 | 63 | # Gradle 64 | .idea/**/gradle.xml 65 | .idea/**/libraries 66 | 67 | # Gradle and Maven with auto-import 68 | # When using Gradle or Maven with auto-import, you should exclude module files, 69 | # since they will be recreated, and may cause churn. Uncomment if using 70 | # auto-import. 71 | # .idea/artifacts 72 | # .idea/compiler.xml 73 | # .idea/jarRepositories.xml 74 | # .idea/modules.xml 75 | # .idea/*.iml 76 | # .idea/modules 77 | # *.iml 78 | # *.ipr 79 | 80 | # CMake 81 | cmake-build-*/ 82 | 83 | # Mongo Explorer plugin 84 | .idea/**/mongoSettings.xml 85 | 86 | # File-based project format 87 | *.iws 88 | 89 | # IntelliJ 90 | out/ 91 | 92 | # mpeltonen/sbt-idea plugin 93 | .idea_modules/ 94 | 95 | # JIRA plugin 96 | atlassian-ide-plugin.xml 97 | 98 | # Cursive Clojure plugin 99 | .idea/replstate.xml 100 | 101 | # SonarLint plugin 102 | .idea/sonarlint/ 103 | 104 | # Crashlytics plugin (for Android Studio and IntelliJ) 105 | com_crashlytics_export_strings.xml 106 | crashlytics.properties 107 | crashlytics-build.properties 108 | fabric.properties 109 | 110 | # Editor-based Rest Client 111 | .idea/httpRequests 112 | 113 | # Android studio 3.1+ serialized cache file 114 | .idea/caches/build_file_checksums.ser 115 | 116 | ### PyCharm Patch ### 117 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 118 | 119 | # *.iml 120 | # modules.xml 121 | # .idea/misc.xml 122 | # *.ipr 123 | 124 | # Sonarlint plugin 125 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 126 | .idea/**/sonarlint/ 127 | 128 | # SonarQube Plugin 129 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 130 | .idea/**/sonarIssues.xml 131 | 132 | # Markdown Navigator plugin 133 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 134 | .idea/**/markdown-navigator.xml 135 | .idea/**/markdown-navigator-enh.xml 136 | .idea/**/markdown-navigator/ 137 | 138 | # Cache file creation bug 139 | # See https://youtrack.jetbrains.com/issue/JBR-2257 140 | .idea/$CACHE_FILE$ 141 | 142 | # CodeStream plugin 143 | # https://plugins.jetbrains.com/plugin/12206-codestream 144 | .idea/codestream.xml 145 | 146 | # Azure Toolkit for IntelliJ plugin 147 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 148 | .idea/**/azureSettings.xml 149 | 150 | ### venv ### 151 | # Virtualenv 152 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 153 | .Python 154 | [Bb]in 155 | [Ii]nclude 156 | [Ll]ib 157 | [Ll]ib64 158 | [Ll]ocal 159 | [Ss]cripts 160 | pyvenv.cfg 161 | .venv 162 | pip-selfcheck.json 163 | 164 | # End of https://www.toptal.com/developers/gitignore/api/macos,pycharm,venv 165 | poetry.lock 166 | pyproject.toml 167 | __pycache__/ 168 | */__pycache__/ 169 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Scroll Soft

2 | 3 | Script to interact with contracts in Scroll. 4 | 5 | TG: https://t.me/cryptoscripts 6 | 7 | Donations: 0x763cDEa4a54991Cd85bFAd1FD47E9c175f53090B 8 | 9 | --- 10 | 11 |

Modules

12 | 13 | 1. Main Bridge (deposit/withdraw) 14 | 15 | 2. Orbiter bridge 16 | 17 | 3. Swaps on PunkSwap, SkyDrome, SpaceFi 18 | 19 | 4. Liquidity on PunkSwap, SkyDrome, SpaceFi 20 | 21 | 5. Dmail 22 | 23 | 6. Zerius (Mint + Bridge) 24 | 25 | 7. Deposit from OKX / Withdraw to OKX 26 | 27 | 8. All transactions are saved to the database. 28 | 29 | --- 30 |

Some information

31 | 32 | The routes are made randomly so that the patterns on the wallets do not match. 33 | There is a random pause between module executions, which can also be configured. 34 | You can also adjust the maximum gas value, thereby reducing transaction fees. 35 | The script implements a circular system with output to OKX sub-accounts, 36 | you can simply launch the bot with a balance on OKX, and it will scroll 37 | through all your wallets. In order for the cycle system to work, you need to enable 38 | at least one bridge each with deposit and withdrawal variables. 39 | In the future, we will be able to immediately withdraw money to OKX 40 | as soon as they add such an option. Also, all transactions will be stored in a 41 | database. The database will also contain information about how many interactions 42 | there were with a certain contract and the volumes on it. 43 | 44 | --- 45 |

Settings

46 | 47 | To configure modules you need to go to the file config.py. 48 | Inside there will be information on each variable in a specific module type. For other modules, for example, 49 | for another swap on some dex, the settings work in the same way 50 | -------------------------------------------------------------------------------- /assets/abi/aave_abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "weth", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "owner", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "contract IPool", 16 | "name": "pool", 17 | "type": "address" 18 | } 19 | ], 20 | "stateMutability": "nonpayable", 21 | "type": "constructor" 22 | }, 23 | { 24 | "anonymous": false, 25 | "inputs": [ 26 | { 27 | "indexed": true, 28 | "internalType": "address", 29 | "name": "previousOwner", 30 | "type": "address" 31 | }, 32 | { 33 | "indexed": true, 34 | "internalType": "address", 35 | "name": "newOwner", 36 | "type": "address" 37 | } 38 | ], 39 | "name": "OwnershipTransferred", 40 | "type": "event" 41 | }, 42 | { 43 | "stateMutability": "payable", 44 | "type": "fallback" 45 | }, 46 | { 47 | "inputs": [ 48 | { 49 | "internalType": "address", 50 | "name": "", 51 | "type": "address" 52 | }, 53 | { 54 | "internalType": "uint256", 55 | "name": "amount", 56 | "type": "uint256" 57 | }, 58 | { 59 | "internalType": "uint256", 60 | "name": "interestRateMode", 61 | "type": "uint256" 62 | }, 63 | { 64 | "internalType": "uint16", 65 | "name": "referralCode", 66 | "type": "uint16" 67 | } 68 | ], 69 | "name": "borrowETH", 70 | "outputs": [], 71 | "stateMutability": "nonpayable", 72 | "type": "function" 73 | }, 74 | { 75 | "inputs": [ 76 | { 77 | "internalType": "address", 78 | "name": "", 79 | "type": "address" 80 | }, 81 | { 82 | "internalType": "address", 83 | "name": "onBehalfOf", 84 | "type": "address" 85 | }, 86 | { 87 | "internalType": "uint16", 88 | "name": "referralCode", 89 | "type": "uint16" 90 | } 91 | ], 92 | "name": "depositETH", 93 | "outputs": [], 94 | "stateMutability": "payable", 95 | "type": "function" 96 | }, 97 | { 98 | "inputs": [ 99 | { 100 | "internalType": "address", 101 | "name": "to", 102 | "type": "address" 103 | }, 104 | { 105 | "internalType": "uint256", 106 | "name": "amount", 107 | "type": "uint256" 108 | } 109 | ], 110 | "name": "emergencyEtherTransfer", 111 | "outputs": [], 112 | "stateMutability": "nonpayable", 113 | "type": "function" 114 | }, 115 | { 116 | "inputs": [ 117 | { 118 | "internalType": "address", 119 | "name": "token", 120 | "type": "address" 121 | }, 122 | { 123 | "internalType": "address", 124 | "name": "to", 125 | "type": "address" 126 | }, 127 | { 128 | "internalType": "uint256", 129 | "name": "amount", 130 | "type": "uint256" 131 | } 132 | ], 133 | "name": "emergencyTokenTransfer", 134 | "outputs": [], 135 | "stateMutability": "nonpayable", 136 | "type": "function" 137 | }, 138 | { 139 | "inputs": [], 140 | "name": "getWETHAddress", 141 | "outputs": [ 142 | { 143 | "internalType": "address", 144 | "name": "", 145 | "type": "address" 146 | } 147 | ], 148 | "stateMutability": "view", 149 | "type": "function" 150 | }, 151 | { 152 | "inputs": [], 153 | "name": "owner", 154 | "outputs": [ 155 | { 156 | "internalType": "address", 157 | "name": "", 158 | "type": "address" 159 | } 160 | ], 161 | "stateMutability": "view", 162 | "type": "function" 163 | }, 164 | { 165 | "inputs": [], 166 | "name": "renounceOwnership", 167 | "outputs": [], 168 | "stateMutability": "nonpayable", 169 | "type": "function" 170 | }, 171 | { 172 | "inputs": [ 173 | { 174 | "internalType": "address", 175 | "name": "", 176 | "type": "address" 177 | }, 178 | { 179 | "internalType": "uint256", 180 | "name": "amount", 181 | "type": "uint256" 182 | }, 183 | { 184 | "internalType": "uint256", 185 | "name": "rateMode", 186 | "type": "uint256" 187 | }, 188 | { 189 | "internalType": "address", 190 | "name": "onBehalfOf", 191 | "type": "address" 192 | } 193 | ], 194 | "name": "repayETH", 195 | "outputs": [], 196 | "stateMutability": "payable", 197 | "type": "function" 198 | }, 199 | { 200 | "inputs": [ 201 | { 202 | "internalType": "address", 203 | "name": "newOwner", 204 | "type": "address" 205 | } 206 | ], 207 | "name": "transferOwnership", 208 | "outputs": [], 209 | "stateMutability": "nonpayable", 210 | "type": "function" 211 | }, 212 | { 213 | "inputs": [ 214 | { 215 | "internalType": "address", 216 | "name": "", 217 | "type": "address" 218 | }, 219 | { 220 | "internalType": "uint256", 221 | "name": "amount", 222 | "type": "uint256" 223 | }, 224 | { 225 | "internalType": "address", 226 | "name": "to", 227 | "type": "address" 228 | } 229 | ], 230 | "name": "withdrawETH", 231 | "outputs": [], 232 | "stateMutability": "nonpayable", 233 | "type": "function" 234 | }, 235 | { 236 | "inputs": [ 237 | { 238 | "internalType": "address", 239 | "name": "", 240 | "type": "address" 241 | }, 242 | { 243 | "internalType": "uint256", 244 | "name": "amount", 245 | "type": "uint256" 246 | }, 247 | { 248 | "internalType": "address", 249 | "name": "to", 250 | "type": "address" 251 | }, 252 | { 253 | "internalType": "uint256", 254 | "name": "deadline", 255 | "type": "uint256" 256 | }, 257 | { 258 | "internalType": "uint8", 259 | "name": "permitV", 260 | "type": "uint8" 261 | }, 262 | { 263 | "internalType": "bytes32", 264 | "name": "permitR", 265 | "type": "bytes32" 266 | }, 267 | { 268 | "internalType": "bytes32", 269 | "name": "permitS", 270 | "type": "bytes32" 271 | } 272 | ], 273 | "name": "withdrawETHWithPermit", 274 | "outputs": [], 275 | "stateMutability": "nonpayable", 276 | "type": "function" 277 | }, 278 | { 279 | "stateMutability": "payable", 280 | "type": "receive" 281 | } 282 | ] -------------------------------------------------------------------------------- /assets/abi/classic_pool_factory_address.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_master", 7 | "type": "address" 8 | } 9 | ], 10 | "stateMutability": "nonpayable", 11 | "type": "constructor" 12 | }, 13 | { 14 | "inputs": [], 15 | "name": "InvalidTokens", 16 | "type": "error" 17 | }, 18 | { 19 | "anonymous": false, 20 | "inputs": [ 21 | { 22 | "indexed": true, 23 | "internalType": "address", 24 | "name": "token0", 25 | "type": "address" 26 | }, 27 | { 28 | "indexed": true, 29 | "internalType": "address", 30 | "name": "token1", 31 | "type": "address" 32 | }, 33 | { 34 | "indexed": false, 35 | "internalType": "address", 36 | "name": "pool", 37 | "type": "address" 38 | } 39 | ], 40 | "name": "PoolCreated", 41 | "type": "event" 42 | }, 43 | { 44 | "inputs": [ 45 | { 46 | "internalType": "bytes", 47 | "name": "data", 48 | "type": "bytes" 49 | } 50 | ], 51 | "name": "createPool", 52 | "outputs": [ 53 | { 54 | "internalType": "address", 55 | "name": "pool", 56 | "type": "address" 57 | } 58 | ], 59 | "stateMutability": "nonpayable", 60 | "type": "function" 61 | }, 62 | { 63 | "inputs": [], 64 | "name": "getDeployData", 65 | "outputs": [ 66 | { 67 | "internalType": "bytes", 68 | "name": "deployData", 69 | "type": "bytes" 70 | } 71 | ], 72 | "stateMutability": "view", 73 | "type": "function" 74 | }, 75 | { 76 | "inputs": [ 77 | { 78 | "internalType": "address", 79 | "name": "", 80 | "type": "address" 81 | }, 82 | { 83 | "internalType": "address", 84 | "name": "", 85 | "type": "address" 86 | } 87 | ], 88 | "name": "getPool", 89 | "outputs": [ 90 | { 91 | "internalType": "address", 92 | "name": "", 93 | "type": "address" 94 | } 95 | ], 96 | "stateMutability": "view", 97 | "type": "function" 98 | }, 99 | { 100 | "inputs": [ 101 | { 102 | "internalType": "address", 103 | "name": "pool", 104 | "type": "address" 105 | }, 106 | { 107 | "internalType": "address", 108 | "name": "sender", 109 | "type": "address" 110 | }, 111 | { 112 | "internalType": "address", 113 | "name": "tokenIn", 114 | "type": "address" 115 | }, 116 | { 117 | "internalType": "address", 118 | "name": "tokenOut", 119 | "type": "address" 120 | }, 121 | { 122 | "internalType": "bytes", 123 | "name": "data", 124 | "type": "bytes" 125 | } 126 | ], 127 | "name": "getSwapFee", 128 | "outputs": [ 129 | { 130 | "internalType": "uint24", 131 | "name": "swapFee", 132 | "type": "uint24" 133 | } 134 | ], 135 | "stateMutability": "view", 136 | "type": "function" 137 | }, 138 | { 139 | "inputs": [], 140 | "name": "master", 141 | "outputs": [ 142 | { 143 | "internalType": "address", 144 | "name": "", 145 | "type": "address" 146 | } 147 | ], 148 | "stateMutability": "view", 149 | "type": "function" 150 | } 151 | ] -------------------------------------------------------------------------------- /assets/abi/deploy.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "message", 10 | "outputs": [ 11 | { 12 | "internalType": "string", 13 | "name": "", 14 | "type": "string" 15 | } 16 | ], 17 | "stateMutability": "view", 18 | "type": "function" 19 | }, 20 | { 21 | "inputs": [ 22 | { 23 | "internalType": "string", 24 | "name": "newMessage", 25 | "type": "string" 26 | } 27 | ], 28 | "name": "setMessage", 29 | "outputs": [], 30 | "stateMutability": "nonpayable", 31 | "type": "function" 32 | } 33 | ] -------------------------------------------------------------------------------- /assets/abi/erc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "_name", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "_symbol", 12 | "type": "string" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "_initialSupply", 17 | "type": "uint256" 18 | } 19 | ], 20 | "stateMutability": "nonpayable", 21 | "type": "constructor" 22 | }, 23 | { 24 | "anonymous": false, 25 | "inputs": [ 26 | { 27 | "indexed": true, 28 | "internalType": "address", 29 | "name": "owner", 30 | "type": "address" 31 | }, 32 | { 33 | "indexed": true, 34 | "internalType": "address", 35 | "name": "spender", 36 | "type": "address" 37 | }, 38 | { 39 | "indexed": false, 40 | "internalType": "uint256", 41 | "name": "value", 42 | "type": "uint256" 43 | } 44 | ], 45 | "name": "Approval", 46 | "type": "event" 47 | }, 48 | { 49 | "anonymous": false, 50 | "inputs": [ 51 | { 52 | "indexed": true, 53 | "internalType": "address", 54 | "name": "from", 55 | "type": "address" 56 | }, 57 | { 58 | "indexed": true, 59 | "internalType": "address", 60 | "name": "to", 61 | "type": "address" 62 | }, 63 | { 64 | "indexed": false, 65 | "internalType": "uint256", 66 | "name": "value", 67 | "type": "uint256" 68 | } 69 | ], 70 | "name": "Transfer", 71 | "type": "event" 72 | }, 73 | { 74 | "inputs": [ 75 | { 76 | "internalType": "address", 77 | "name": "owner", 78 | "type": "address" 79 | }, 80 | { 81 | "internalType": "address", 82 | "name": "spender", 83 | "type": "address" 84 | } 85 | ], 86 | "name": "allowance", 87 | "outputs": [ 88 | { 89 | "internalType": "uint256", 90 | "name": "", 91 | "type": "uint256" 92 | } 93 | ], 94 | "stateMutability": "view", 95 | "type": "function" 96 | }, 97 | { 98 | "inputs": [ 99 | { 100 | "internalType": "address", 101 | "name": "spender", 102 | "type": "address" 103 | }, 104 | { 105 | "internalType": "uint256", 106 | "name": "amount", 107 | "type": "uint256" 108 | } 109 | ], 110 | "name": "approve", 111 | "outputs": [ 112 | { 113 | "internalType": "bool", 114 | "name": "", 115 | "type": "bool" 116 | } 117 | ], 118 | "stateMutability": "nonpayable", 119 | "type": "function" 120 | }, 121 | { 122 | "inputs": [ 123 | { 124 | "internalType": "address", 125 | "name": "account", 126 | "type": "address" 127 | } 128 | ], 129 | "name": "balanceOf", 130 | "outputs": [ 131 | { 132 | "internalType": "uint256", 133 | "name": "", 134 | "type": "uint256" 135 | } 136 | ], 137 | "stateMutability": "view", 138 | "type": "function" 139 | }, 140 | { 141 | "inputs": [], 142 | "name": "decimals", 143 | "outputs": [ 144 | { 145 | "internalType": "uint8", 146 | "name": "", 147 | "type": "uint8" 148 | } 149 | ], 150 | "stateMutability": "view", 151 | "type": "function" 152 | }, 153 | { 154 | "inputs": [ 155 | { 156 | "internalType": "address", 157 | "name": "spender", 158 | "type": "address" 159 | }, 160 | { 161 | "internalType": "uint256", 162 | "name": "subtractedValue", 163 | "type": "uint256" 164 | } 165 | ], 166 | "name": "decreaseAllowance", 167 | "outputs": [ 168 | { 169 | "internalType": "bool", 170 | "name": "", 171 | "type": "bool" 172 | } 173 | ], 174 | "stateMutability": "nonpayable", 175 | "type": "function" 176 | }, 177 | { 178 | "inputs": [ 179 | { 180 | "internalType": "address", 181 | "name": "spender", 182 | "type": "address" 183 | }, 184 | { 185 | "internalType": "uint256", 186 | "name": "addedValue", 187 | "type": "uint256" 188 | } 189 | ], 190 | "name": "increaseAllowance", 191 | "outputs": [ 192 | { 193 | "internalType": "bool", 194 | "name": "", 195 | "type": "bool" 196 | } 197 | ], 198 | "stateMutability": "nonpayable", 199 | "type": "function" 200 | }, 201 | { 202 | "inputs": [], 203 | "name": "name", 204 | "outputs": [ 205 | { 206 | "internalType": "string", 207 | "name": "", 208 | "type": "string" 209 | } 210 | ], 211 | "stateMutability": "view", 212 | "type": "function" 213 | }, 214 | { 215 | "inputs": [ 216 | { 217 | "internalType": "uint8", 218 | "name": "decimals_", 219 | "type": "uint8" 220 | } 221 | ], 222 | "name": "setupDecimals", 223 | "outputs": [], 224 | "stateMutability": "nonpayable", 225 | "type": "function" 226 | }, 227 | { 228 | "inputs": [], 229 | "name": "symbol", 230 | "outputs": [ 231 | { 232 | "internalType": "string", 233 | "name": "", 234 | "type": "string" 235 | } 236 | ], 237 | "stateMutability": "view", 238 | "type": "function" 239 | }, 240 | { 241 | "inputs": [], 242 | "name": "totalSupply", 243 | "outputs": [ 244 | { 245 | "internalType": "uint256", 246 | "name": "", 247 | "type": "uint256" 248 | } 249 | ], 250 | "stateMutability": "view", 251 | "type": "function" 252 | }, 253 | { 254 | "inputs": [ 255 | { 256 | "internalType": "address", 257 | "name": "recipient", 258 | "type": "address" 259 | }, 260 | { 261 | "internalType": "uint256", 262 | "name": "amount", 263 | "type": "uint256" 264 | } 265 | ], 266 | "name": "transfer", 267 | "outputs": [ 268 | { 269 | "internalType": "bool", 270 | "name": "", 271 | "type": "bool" 272 | } 273 | ], 274 | "stateMutability": "nonpayable", 275 | "type": "function" 276 | }, 277 | { 278 | "inputs": [ 279 | { 280 | "internalType": "address", 281 | "name": "sender", 282 | "type": "address" 283 | }, 284 | { 285 | "internalType": "address", 286 | "name": "recipient", 287 | "type": "address" 288 | }, 289 | { 290 | "internalType": "uint256", 291 | "name": "amount", 292 | "type": "uint256" 293 | } 294 | ], 295 | "name": "transferFrom", 296 | "outputs": [ 297 | { 298 | "internalType": "bool", 299 | "name": "", 300 | "type": "bool" 301 | } 302 | ], 303 | "stateMutability": "nonpayable", 304 | "type": "function" 305 | } 306 | ] -------------------------------------------------------------------------------- /assets/abi/eth.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "src", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "guy", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "wad", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "dst", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": false, 38 | "internalType": "uint256", 39 | "name": "wad", 40 | "type": "uint256" 41 | } 42 | ], 43 | "name": "Deposit", 44 | "type": "event" 45 | }, 46 | { 47 | "anonymous": false, 48 | "inputs": [ 49 | { 50 | "indexed": true, 51 | "internalType": "address", 52 | "name": "src", 53 | "type": "address" 54 | }, 55 | { 56 | "indexed": true, 57 | "internalType": "address", 58 | "name": "dst", 59 | "type": "address" 60 | }, 61 | { 62 | "indexed": false, 63 | "internalType": "uint256", 64 | "name": "wad", 65 | "type": "uint256" 66 | } 67 | ], 68 | "name": "Transfer", 69 | "type": "event" 70 | }, 71 | { 72 | "anonymous": false, 73 | "inputs": [ 74 | { 75 | "indexed": true, 76 | "internalType": "address", 77 | "name": "src", 78 | "type": "address" 79 | }, 80 | { 81 | "indexed": false, 82 | "internalType": "uint256", 83 | "name": "wad", 84 | "type": "uint256" 85 | } 86 | ], 87 | "name": "Withdrawal", 88 | "type": "event" 89 | }, 90 | { 91 | "payable": true, 92 | "stateMutability": "payable", 93 | "type": "fallback" 94 | }, 95 | { 96 | "constant": true, 97 | "inputs": [ 98 | { 99 | "internalType": "address", 100 | "name": "", 101 | "type": "address" 102 | }, 103 | { 104 | "internalType": "address", 105 | "name": "", 106 | "type": "address" 107 | } 108 | ], 109 | "name": "allowance", 110 | "outputs": [ 111 | { 112 | "internalType": "uint256", 113 | "name": "", 114 | "type": "uint256" 115 | } 116 | ], 117 | "payable": false, 118 | "stateMutability": "view", 119 | "type": "function" 120 | }, 121 | { 122 | "constant": false, 123 | "inputs": [ 124 | { 125 | "internalType": "address", 126 | "name": "guy", 127 | "type": "address" 128 | }, 129 | { 130 | "internalType": "uint256", 131 | "name": "wad", 132 | "type": "uint256" 133 | } 134 | ], 135 | "name": "approve", 136 | "outputs": [ 137 | { 138 | "internalType": "bool", 139 | "name": "", 140 | "type": "bool" 141 | } 142 | ], 143 | "payable": false, 144 | "stateMutability": "nonpayable", 145 | "type": "function" 146 | }, 147 | { 148 | "constant": true, 149 | "inputs": [ 150 | { 151 | "internalType": "address", 152 | "name": "", 153 | "type": "address" 154 | } 155 | ], 156 | "name": "balanceOf", 157 | "outputs": [ 158 | { 159 | "internalType": "uint256", 160 | "name": "", 161 | "type": "uint256" 162 | } 163 | ], 164 | "payable": false, 165 | "stateMutability": "view", 166 | "type": "function" 167 | }, 168 | { 169 | "constant": true, 170 | "inputs": [], 171 | "name": "decimals", 172 | "outputs": [ 173 | { 174 | "internalType": "uint8", 175 | "name": "", 176 | "type": "uint8" 177 | } 178 | ], 179 | "payable": false, 180 | "stateMutability": "view", 181 | "type": "function" 182 | }, 183 | { 184 | "constant": false, 185 | "inputs": [], 186 | "name": "deposit", 187 | "outputs": [], 188 | "payable": true, 189 | "stateMutability": "payable", 190 | "type": "function" 191 | }, 192 | { 193 | "constant": true, 194 | "inputs": [], 195 | "name": "name", 196 | "outputs": [ 197 | { 198 | "internalType": "string", 199 | "name": "", 200 | "type": "string" 201 | } 202 | ], 203 | "payable": false, 204 | "stateMutability": "view", 205 | "type": "function" 206 | }, 207 | { 208 | "constant": true, 209 | "inputs": [], 210 | "name": "symbol", 211 | "outputs": [ 212 | { 213 | "internalType": "string", 214 | "name": "", 215 | "type": "string" 216 | } 217 | ], 218 | "payable": false, 219 | "stateMutability": "view", 220 | "type": "function" 221 | }, 222 | { 223 | "constant": true, 224 | "inputs": [], 225 | "name": "totalSupply", 226 | "outputs": [ 227 | { 228 | "internalType": "uint256", 229 | "name": "", 230 | "type": "uint256" 231 | } 232 | ], 233 | "payable": false, 234 | "stateMutability": "view", 235 | "type": "function" 236 | }, 237 | { 238 | "constant": false, 239 | "inputs": [ 240 | { 241 | "internalType": "address", 242 | "name": "dst", 243 | "type": "address" 244 | }, 245 | { 246 | "internalType": "uint256", 247 | "name": "wad", 248 | "type": "uint256" 249 | } 250 | ], 251 | "name": "transfer", 252 | "outputs": [ 253 | { 254 | "internalType": "bool", 255 | "name": "", 256 | "type": "bool" 257 | } 258 | ], 259 | "payable": false, 260 | "stateMutability": "nonpayable", 261 | "type": "function" 262 | }, 263 | { 264 | "constant": false, 265 | "inputs": [ 266 | { 267 | "internalType": "address", 268 | "name": "src", 269 | "type": "address" 270 | }, 271 | { 272 | "internalType": "address", 273 | "name": "dst", 274 | "type": "address" 275 | }, 276 | { 277 | "internalType": "uint256", 278 | "name": "wad", 279 | "type": "uint256" 280 | } 281 | ], 282 | "name": "transferFrom", 283 | "outputs": [ 284 | { 285 | "internalType": "bool", 286 | "name": "", 287 | "type": "bool" 288 | } 289 | ], 290 | "payable": false, 291 | "stateMutability": "nonpayable", 292 | "type": "function" 293 | }, 294 | { 295 | "constant": false, 296 | "inputs": [ 297 | { 298 | "internalType": "uint256", 299 | "name": "wad", 300 | "type": "uint256" 301 | } 302 | ], 303 | "name": "withdraw", 304 | "outputs": [], 305 | "payable": false, 306 | "stateMutability": "nonpayable", 307 | "type": "function" 308 | } 309 | ] -------------------------------------------------------------------------------- /assets/abi/omnisea_abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "_scheduler", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "_universalONFT", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "address", 16 | "name": "_omniseaERC721Psi", 17 | "type": "address" 18 | } 19 | ], 20 | "stateMutability": "nonpayable", 21 | "type": "constructor" 22 | }, 23 | { 24 | "anonymous": false, 25 | "inputs": [ 26 | { 27 | "indexed": true, 28 | "internalType": "address", 29 | "name": "collection", 30 | "type": "address" 31 | } 32 | ], 33 | "name": "Created", 34 | "type": "event" 35 | }, 36 | { 37 | "inputs": [ 38 | { 39 | "components": [ 40 | { 41 | "internalType": "string", 42 | "name": "name", 43 | "type": "string" 44 | }, 45 | { 46 | "internalType": "string", 47 | "name": "symbol", 48 | "type": "string" 49 | }, 50 | { 51 | "internalType": "string", 52 | "name": "uri", 53 | "type": "string" 54 | }, 55 | { 56 | "internalType": "string", 57 | "name": "tokensURI", 58 | "type": "string" 59 | }, 60 | { 61 | "internalType": "uint24", 62 | "name": "maxSupply", 63 | "type": "uint24" 64 | }, 65 | { 66 | "internalType": "bool", 67 | "name": "isZeroIndexed", 68 | "type": "bool" 69 | }, 70 | { 71 | "internalType": "uint24", 72 | "name": "royaltyAmount", 73 | "type": "uint24" 74 | }, 75 | { 76 | "internalType": "uint256", 77 | "name": "endTime", 78 | "type": "uint256" 79 | } 80 | ], 81 | "internalType": "struct CreateParams", 82 | "name": "_params", 83 | "type": "tuple" 84 | } 85 | ], 86 | "name": "create", 87 | "outputs": [], 88 | "stateMutability": "nonpayable", 89 | "type": "function" 90 | }, 91 | { 92 | "inputs": [ 93 | { 94 | "internalType": "address", 95 | "name": "", 96 | "type": "address" 97 | } 98 | ], 99 | "name": "drops", 100 | "outputs": [ 101 | { 102 | "internalType": "bool", 103 | "name": "", 104 | "type": "bool" 105 | } 106 | ], 107 | "stateMutability": "view", 108 | "type": "function" 109 | }, 110 | { 111 | "inputs": [], 112 | "name": "omniseaERC721Psi", 113 | "outputs": [ 114 | { 115 | "internalType": "address", 116 | "name": "", 117 | "type": "address" 118 | } 119 | ], 120 | "stateMutability": "view", 121 | "type": "function" 122 | }, 123 | { 124 | "inputs": [], 125 | "name": "owner", 126 | "outputs": [ 127 | { 128 | "internalType": "address", 129 | "name": "", 130 | "type": "address" 131 | } 132 | ], 133 | "stateMutability": "view", 134 | "type": "function" 135 | }, 136 | { 137 | "inputs": [], 138 | "name": "scheduler", 139 | "outputs": [ 140 | { 141 | "internalType": "address", 142 | "name": "", 143 | "type": "address" 144 | } 145 | ], 146 | "stateMutability": "view", 147 | "type": "function" 148 | }, 149 | { 150 | "inputs": [ 151 | { 152 | "internalType": "address", 153 | "name": "manager_", 154 | "type": "address" 155 | } 156 | ], 157 | "name": "setManager", 158 | "outputs": [], 159 | "stateMutability": "nonpayable", 160 | "type": "function" 161 | }, 162 | { 163 | "inputs": [], 164 | "name": "universalONFT", 165 | "outputs": [ 166 | { 167 | "internalType": "address", 168 | "name": "", 169 | "type": "address" 170 | } 171 | ], 172 | "stateMutability": "view", 173 | "type": "function" 174 | } 175 | ] -------------------------------------------------------------------------------- /assets/abi/oracle.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "anonymous": false, 9 | "inputs": [ 10 | { 11 | "indexed": false, 12 | "internalType": "uint8", 13 | "name": "version", 14 | "type": "uint8" 15 | } 16 | ], 17 | "name": "Initialized", 18 | "type": "event" 19 | }, 20 | { 21 | "anonymous": false, 22 | "inputs": [ 23 | { 24 | "indexed": false, 25 | "internalType": "uint256", 26 | "name": "txGas", 27 | "type": "uint256" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "uint256", 32 | "name": "txGasContractCreation", 33 | "type": "uint256" 34 | }, 35 | { 36 | "indexed": false, 37 | "internalType": "uint256", 38 | "name": "zeroGas", 39 | "type": "uint256" 40 | }, 41 | { 42 | "indexed": false, 43 | "internalType": "uint256", 44 | "name": "nonZeroGas", 45 | "type": "uint256" 46 | } 47 | ], 48 | "name": "IntrinsicParamsUpdated", 49 | "type": "event" 50 | }, 51 | { 52 | "anonymous": false, 53 | "inputs": [ 54 | { 55 | "indexed": false, 56 | "internalType": "uint256", 57 | "name": "oldL2BaseFee", 58 | "type": "uint256" 59 | }, 60 | { 61 | "indexed": false, 62 | "internalType": "uint256", 63 | "name": "newL2BaseFee", 64 | "type": "uint256" 65 | } 66 | ], 67 | "name": "L2BaseFeeUpdated", 68 | "type": "event" 69 | }, 70 | { 71 | "anonymous": false, 72 | "inputs": [ 73 | { 74 | "indexed": true, 75 | "internalType": "address", 76 | "name": "previousOwner", 77 | "type": "address" 78 | }, 79 | { 80 | "indexed": true, 81 | "internalType": "address", 82 | "name": "newOwner", 83 | "type": "address" 84 | } 85 | ], 86 | "name": "OwnershipTransferred", 87 | "type": "event" 88 | }, 89 | { 90 | "anonymous": false, 91 | "inputs": [ 92 | { 93 | "indexed": false, 94 | "internalType": "address", 95 | "name": "_oldWhitelist", 96 | "type": "address" 97 | }, 98 | { 99 | "indexed": false, 100 | "internalType": "address", 101 | "name": "_newWhitelist", 102 | "type": "address" 103 | } 104 | ], 105 | "name": "UpdateWhitelist", 106 | "type": "event" 107 | }, 108 | { 109 | "inputs": [ 110 | { 111 | "internalType": "bytes", 112 | "name": "_message", 113 | "type": "bytes" 114 | } 115 | ], 116 | "name": "calculateIntrinsicGasFee", 117 | "outputs": [ 118 | { 119 | "internalType": "uint256", 120 | "name": "", 121 | "type": "uint256" 122 | } 123 | ], 124 | "stateMutability": "view", 125 | "type": "function" 126 | }, 127 | { 128 | "inputs": [ 129 | { 130 | "internalType": "uint256", 131 | "name": "_gasLimit", 132 | "type": "uint256" 133 | } 134 | ], 135 | "name": "estimateCrossDomainMessageFee", 136 | "outputs": [ 137 | { 138 | "internalType": "uint256", 139 | "name": "", 140 | "type": "uint256" 141 | } 142 | ], 143 | "stateMutability": "view", 144 | "type": "function" 145 | }, 146 | { 147 | "inputs": [ 148 | { 149 | "internalType": "uint64", 150 | "name": "_txGas", 151 | "type": "uint64" 152 | }, 153 | { 154 | "internalType": "uint64", 155 | "name": "_txGasContractCreation", 156 | "type": "uint64" 157 | }, 158 | { 159 | "internalType": "uint64", 160 | "name": "_zeroGas", 161 | "type": "uint64" 162 | }, 163 | { 164 | "internalType": "uint64", 165 | "name": "_nonZeroGas", 166 | "type": "uint64" 167 | } 168 | ], 169 | "name": "initialize", 170 | "outputs": [], 171 | "stateMutability": "nonpayable", 172 | "type": "function" 173 | }, 174 | { 175 | "inputs": [], 176 | "name": "intrinsicParams", 177 | "outputs": [ 178 | { 179 | "internalType": "uint64", 180 | "name": "txGas", 181 | "type": "uint64" 182 | }, 183 | { 184 | "internalType": "uint64", 185 | "name": "txGasContractCreation", 186 | "type": "uint64" 187 | }, 188 | { 189 | "internalType": "uint64", 190 | "name": "zeroGas", 191 | "type": "uint64" 192 | }, 193 | { 194 | "internalType": "uint64", 195 | "name": "nonZeroGas", 196 | "type": "uint64" 197 | } 198 | ], 199 | "stateMutability": "view", 200 | "type": "function" 201 | }, 202 | { 203 | "inputs": [], 204 | "name": "l2BaseFee", 205 | "outputs": [ 206 | { 207 | "internalType": "uint256", 208 | "name": "", 209 | "type": "uint256" 210 | } 211 | ], 212 | "stateMutability": "view", 213 | "type": "function" 214 | }, 215 | { 216 | "inputs": [], 217 | "name": "owner", 218 | "outputs": [ 219 | { 220 | "internalType": "address", 221 | "name": "", 222 | "type": "address" 223 | } 224 | ], 225 | "stateMutability": "view", 226 | "type": "function" 227 | }, 228 | { 229 | "inputs": [], 230 | "name": "renounceOwnership", 231 | "outputs": [], 232 | "stateMutability": "nonpayable", 233 | "type": "function" 234 | }, 235 | { 236 | "inputs": [ 237 | { 238 | "internalType": "uint64", 239 | "name": "_txGas", 240 | "type": "uint64" 241 | }, 242 | { 243 | "internalType": "uint64", 244 | "name": "_txGasContractCreation", 245 | "type": "uint64" 246 | }, 247 | { 248 | "internalType": "uint64", 249 | "name": "_zeroGas", 250 | "type": "uint64" 251 | }, 252 | { 253 | "internalType": "uint64", 254 | "name": "_nonZeroGas", 255 | "type": "uint64" 256 | } 257 | ], 258 | "name": "setIntrinsicParams", 259 | "outputs": [], 260 | "stateMutability": "nonpayable", 261 | "type": "function" 262 | }, 263 | { 264 | "inputs": [ 265 | { 266 | "internalType": "uint256", 267 | "name": "_newL2BaseFee", 268 | "type": "uint256" 269 | } 270 | ], 271 | "name": "setL2BaseFee", 272 | "outputs": [], 273 | "stateMutability": "nonpayable", 274 | "type": "function" 275 | }, 276 | { 277 | "inputs": [ 278 | { 279 | "internalType": "address", 280 | "name": "newOwner", 281 | "type": "address" 282 | } 283 | ], 284 | "name": "transferOwnership", 285 | "outputs": [], 286 | "stateMutability": "nonpayable", 287 | "type": "function" 288 | }, 289 | { 290 | "inputs": [ 291 | { 292 | "internalType": "address", 293 | "name": "_newWhitelist", 294 | "type": "address" 295 | } 296 | ], 297 | "name": "updateWhitelist", 298 | "outputs": [], 299 | "stateMutability": "nonpayable", 300 | "type": "function" 301 | }, 302 | { 303 | "inputs": [], 304 | "name": "whitelist", 305 | "outputs": [ 306 | { 307 | "internalType": "contract IWhitelist", 308 | "name": "", 309 | "type": "address" 310 | } 311 | ], 312 | "stateMutability": "view", 313 | "type": "function" 314 | } 315 | ] -------------------------------------------------------------------------------- /assets/abi/rubyscore_abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "stateMutability": "payable", 4 | "type": "fallback" 5 | }, 6 | { 7 | "inputs": [], 8 | "name": "vote", 9 | "outputs": [], 10 | "stateMutability": "payable", 11 | "type": "function" 12 | } 13 | ] -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | RANDOMIZE = True 2 | SLIPPAGE = 0.03 3 | MIN_PAUSE = 10 4 | MAX_PAUSE = 20 5 | RETRIES = 3 6 | PAUSE_BETWEEN_RETRIES = 1 7 | CHECK_GWEI = False 8 | MAX_GWEI = 25 9 | USE_DATABASE = False 10 | 11 | deploy_contract = False 12 | 13 | 14 | # --- Other --- # 15 | dmail = False 16 | rubyscore = False 17 | multi_approve = True 18 | 19 | # --- Lendings --- # 20 | layerbank_deposit = False 21 | layerbank_withdraw = False 22 | aave_deposit = False 23 | aave_withdraw = False 24 | 25 | 26 | # --- Bridges --- # 27 | main_bridge = False 28 | owl_bridge = False 29 | orbiter_bridge = False 30 | 31 | # --- Swaps --- # 32 | skydrome_swap = False 33 | punk_swap = False 34 | zebra_swap = False 35 | syncswap_swap = False 36 | specefi_swap = False 37 | wrapper = False 38 | 39 | swap_all_to_eth = False 40 | random_dex_swap = False 41 | 42 | # --- Liquidity --- # 43 | punk_swap_liquidity = False 44 | punk_swap_liquidity_remove = False 45 | 46 | spacefi_liquidity = False 47 | spacefi_liquidity_remove = False 48 | 49 | syncswap_liquidity = True 50 | syncswap_liquidity_remove = False 51 | 52 | skydrome_liquidity = False 53 | skydrome_liquidity_remove = False 54 | 55 | random_add_liquidity = False 56 | 57 | # --- NFT --- # 58 | zerius = False 59 | l2pass = False 60 | omnisea = False 61 | scroll_citizen = False 62 | zk_stars = False 63 | 64 | # --- Withdrawals --- # 65 | okx_withdraw = False # from okx to wallets 66 | okx_deposit = False # from wallets to okx 67 | 68 | 69 | class OkxWithdrawConfig: 70 | """ 71 | OKX => Wallets 72 | 73 | amount: Union[float, List[float]]. 74 | chain: str. ERC20 - Ethereum. 75 | """ 76 | 77 | amount = [0.001, 0.002] 78 | chain = 'ERC-20' 79 | 80 | 81 | class WithdrawFromWalletsToOKXConfig: 82 | """ 83 | Wallets => OKX 84 | 85 | amount: Union[float, List[float]]. 86 | from_chain: str. ETH, ERA, etc. 87 | withdraw_all: bool. True/False. 88 | keep_value: Union[float, List[float]]. ETH value to keep on your wallet if withdraw_all = True. 89 | """ 90 | amount = [0.001, 0.002] 91 | from_chain = 'ARB' 92 | withdraw_all = True 93 | keep_value = [0.005, 0.007] 94 | 95 | 96 | class MainBridgeConfig: 97 | """ 98 | action: str. deposit/withdraw. 99 | amount: Union[float, List[float]]. Value to bridge. You can use float or list. e.g. amount = 0.5 (will bridge 0.5 ETH) 100 | or amount = [0.5, 0.6] (will randomly take a number between 0.5 and 0.6) 101 | 102 | use_percentage: bool. Use True if you want to use a percentage of the balance instead of numbers. 103 | 104 | bridge_percentage: Union[float, List[float]]. Value as a percentage of balance to bridge. 105 | You can use float or list. e.g. bridge_percentage = 0.5 (will bridge 50% of your ETH balance) 106 | or bridge_percentage = [0.5, 0.6] (will randomly take a number between 50% and 60%) 107 | 108 | """ 109 | action = 'withdraw' 110 | amount = [0.1, 0.2] 111 | use_percentage = True 112 | bridge_percentage = [0.5, 0.5] 113 | claim_eth = False # True is only if you withdraw from Scroll to ETH (need to wait for ~40 minutes after bridge) 114 | 115 | 116 | class OrbiterBridgeConfig: 117 | """ 118 | chains: eth, arb, op, era, base, scroll 119 | 120 | action: deposit/withdraw 121 | """ 122 | action = 'deposit' 123 | 124 | from_chain = 'ARB' 125 | to_chain = 'SCROLL' 126 | amount = [0.05, 0.06] 127 | use_percentage = False 128 | bridge_percentage = [0.5, 0.5] 129 | 130 | 131 | class OwlBridgeConfig: 132 | """ 133 | """ 134 | action = 'deposit' 135 | 136 | from_chain = 'SCROLL' 137 | to_chain = 'ARB' 138 | amount = 0.004 139 | use_percentage = False 140 | bridge_percentage = [0.5, 0.5] 141 | 142 | 143 | class SwapAllTokensConfig: 144 | tokens_list = ['WETH', 'USDC', 'USDT', 'WBTC', 'DAI'] 145 | to_token = 'ETH' 146 | 147 | 148 | class RandomDexSwapConfig: 149 | from_token = 'ETH' 150 | to_token = ['USDC', 'USDT', 'WBTC', 'DAI'] 151 | amount = [0.001, 0.003] 152 | use_percentage = False 153 | swap_percentage = [0.1, 0.2] 154 | num_swaps = [1, 4] 155 | 156 | 157 | class SyncSwapSwapConfig: 158 | """ 159 | from_token: str. 160 | to_token: str | list[str]. If to_token = ['USDC', 'USDT'] it will randomly take USDT or USDC. 161 | """ 162 | 163 | from_token = 'ETH' 164 | to_token = ['USDC', 'USDT'] 165 | amount = 0.001 166 | use_percentage = False 167 | swap_percentage = 0.5 168 | swap_all_balance = True 169 | 170 | 171 | # --- Swaps --- # 172 | class SkyDromeSwapConfig: 173 | from_token = 'WETH' 174 | to_token = 'USDC' 175 | amount = 0.0024 176 | use_percentage = False 177 | swap_percentage = 0.5 178 | swap_all_balance = True 179 | 180 | 181 | class ZebraSwapConfig: 182 | from_token = 'USDT' 183 | to_token = 'ETH' 184 | amount = 0.0024 185 | use_percentage = False 186 | swap_percentage = [0.1, 0.2] 187 | swap_all_balance = True 188 | 189 | 190 | class SpaceFiSwapConfig: 191 | from_token = 'ETH' 192 | to_token = 'USDT' 193 | amount = 0.002 194 | use_percentage = False 195 | swap_percentage = 0.5 196 | swap_all_balance = True 197 | 198 | 199 | class PunkSwapConfig: 200 | from_token = 'USDC' 201 | to_token = 'ETH' 202 | amount = [0.0009, 0.0011] 203 | use_percentage = False 204 | swap_percentage = 0.1 205 | swap_all_balance = True 206 | 207 | 208 | class WrapperConfig: 209 | """ 210 | action: str. Wrap/Unwrap. 211 | amount: Union[float, List[float]]. 212 | use_all_balance: bool. True is only for action = Unwrap. 213 | use_percentage: bool. True/False. 214 | percentage_to_wrap: Union[List[float], float]. 215 | """ 216 | 217 | action = 'unwrap' 218 | amount = [0.001, 0.002] 219 | use_all_balance = False 220 | use_percentage = True 221 | percentage_to_wrap = [0.1, 0.2] 222 | 223 | 224 | class RandomLiquidityConfig: 225 | token = 'ETH' 226 | token2 = ['USDT'] 227 | amount = [0.0004, 0.0004] 228 | use_percentage = True 229 | liquidity_percentage = [0.1, 0.2] 230 | num_transactions = [1, 1] 231 | 232 | 233 | class PunkSwapLiquidityConfig: 234 | token = 'USDC' 235 | token2 = 'USDT' 236 | amount = [1, 1] 237 | use_percentage = False 238 | liquidity_percentage = 0.01 239 | 240 | 241 | class SpaceFiLiquidityConfig: 242 | token = 'USDC' 243 | token2 = 'USDT' 244 | amount = [1, 1] 245 | use_percentage = False 246 | liquidity_percentage = 0.01 247 | 248 | 249 | class SyncSwapLiquidityConfig: 250 | token = 'ETH' 251 | token2 = 'USDC' 252 | amount = [0.001, 0.001] 253 | use_percentage = False 254 | liquidity_percentage = 0.01 255 | 256 | 257 | class SyncSwapLiquidityRemoveConfig: 258 | token = 'ETH' 259 | token2 = 'USDC' 260 | removing_percentage = 0.5 261 | remove_all = True 262 | 263 | 264 | class PunkSwapLiquidityRemoveConfig: 265 | token = 'USDC' 266 | token2 = 'USDT' 267 | removing_percentage = 0.5 268 | remove_all = True 269 | 270 | 271 | class SpaceFiLiquidityRemoveConfig: 272 | token = 'USDC' 273 | token2 = 'USDT' 274 | removing_percentage = 0.5 275 | remove_all = True 276 | 277 | 278 | class SkyDromeLiquidityConfig: 279 | token = 'ETH' 280 | token2 = 'USDC' 281 | amount = [0.001, 0.001] 282 | use_percentage = False 283 | liquidity_percentage = 0.01 284 | 285 | 286 | class SkyDromeLiquidityRemoveConfig: 287 | token = 'USDC' 288 | token2 = 'USDT' 289 | removing_percentage = 0.5 290 | remove_all = True 291 | 292 | 293 | class DmailConfig: 294 | num_transactions = 1 295 | 296 | 297 | class ZeruisConfig: 298 | """ 299 | chain_to_bridge: Union[str, List[str]]: ['ARB', 'OP', 'POLYGON', 'BSC', 'AVAX'] 300 | """ 301 | chain_to_bridge = ['ARB', 'OP', 'POLYGON', 'BSC', 'AVAX'] 302 | 303 | 304 | class DeployerConfig: 305 | use_0x_bytecode = True 306 | 307 | 308 | class LayerBankDepositConfig: 309 | """ 310 | action: str. deposit/withdraw 311 | amount: Union[float, List[float]] 312 | use_percentage: bool. True/False 313 | percentage: float. 0 - 1. 314 | """ 315 | 316 | amount = [0.0005, 0.001] 317 | use_percentage = True 318 | percentage = [0.85, 0.9] 319 | only_collateral = False 320 | 321 | 322 | class LayerBankWithdrawConfig: 323 | amount = [0.001, 0.002] 324 | withdraw_all = True 325 | use_percentage = False 326 | percentage = 0.01 327 | 328 | 329 | class AaveDepositConfig: 330 | amount = [0.001, 0.002] 331 | use_percentage = True 332 | percentage = [0.1, 0.2] 333 | 334 | 335 | class AaveWithdrawConfig: 336 | amount = [0.001, 0.002] 337 | withdraw_all = True 338 | use_percentage = False 339 | percentage = [0.1, 0.2] 340 | 341 | 342 | class ScrollCitizenMintConfig: 343 | mint_all = False 344 | quantity = [1, 2] 345 | 346 | 347 | class ZkStarsMintConfig: 348 | mint_all = False 349 | quantity = [1, 2] 350 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from typing import Awaitable 4 | 5 | from asyncio import ( 6 | create_task, 7 | gather, 8 | sleep, 9 | run 10 | ) 11 | 12 | from loguru import logger 13 | from config import * 14 | 15 | from src.utils.data.mappings import module_handlers 16 | from src.utils.wrappers.decorators import check_gas 17 | 18 | from src.utils.data.helper import ( 19 | private_keys, 20 | active_module, 21 | ) 22 | 23 | 24 | @check_gas 25 | async def process_task(private_key: str, pattern: str) -> None: 26 | await module_handlers[pattern](private_key) 27 | 28 | 29 | async def main() -> None: 30 | bridge_mappings = { 31 | 'main_bridge': MainBridgeConfig, 32 | 'orbiter_bridge': OrbiterBridgeConfig, 33 | 'owl_bridge': OwlBridgeConfig 34 | } 35 | tasks = [] 36 | 37 | random.shuffle(private_keys) 38 | for private_key in private_keys: 39 | patterns = active_module.copy() 40 | 41 | if RANDOMIZE: 42 | random.shuffle(patterns) 43 | lendings = [['layerbank_deposit', 'layerbank_withdraw'], ['aave_deposit', 'aave_withdraw']] 44 | 45 | bridge_patterns = ['main_bridge', 'orbiter_bridge', 'owl_bridge'] 46 | deposit_bridges = [] 47 | withdraw_bridges = [] 48 | for bridge_pattern in bridge_patterns: 49 | if bridge_pattern in patterns and bridge_mappings[bridge_pattern].action.lower() == 'deposit': 50 | deposit_bridges.append(bridge_pattern) 51 | elif bridge_pattern in patterns and bridge_mappings[bridge_pattern].action.lower() == 'withdraw': 52 | withdraw_bridges.append(bridge_pattern) 53 | 54 | if deposit_bridges: 55 | random_deposit_bridge = random.choice(deposit_bridges) 56 | patterns.remove(random_deposit_bridge) 57 | patterns.insert(0, random_deposit_bridge) 58 | deposit_bridges.remove(random_deposit_bridge) 59 | 60 | for deposit_bridge in deposit_bridges: 61 | patterns.remove(deposit_bridge) 62 | 63 | if 'okx_withdraw' in patterns: 64 | patterns.remove('okx_withdraw') 65 | patterns.insert(0, 'okx_withdraw') 66 | if 'swap_all_to_eth' in patterns: 67 | patterns.remove('swap_all_to_eth') 68 | patterns.append('swap_all_to_eth') 69 | for lending in lendings: 70 | if lending[0] in patterns: 71 | patterns.remove(lending[0]) 72 | patterns.append(lending[0]) 73 | if lending[1] in patterns: 74 | patterns.remove(lending[1]) 75 | patterns.append(lending[1]) 76 | 77 | if withdraw_bridges: 78 | random_withdraw_bridge = random.choice(withdraw_bridges) 79 | patterns.remove(random_withdraw_bridge) 80 | patterns.append(random_withdraw_bridge) 81 | withdraw_bridges.remove(random_withdraw_bridge) 82 | 83 | for withdraw_bridge in withdraw_bridges: 84 | patterns.remove(withdraw_bridge) 85 | 86 | if 'okx_deposit' in patterns: 87 | patterns.remove('okx_deposit') 88 | patterns.append('okx_deposit') 89 | 90 | for pattern in patterns: 91 | task = create_task(process_task(private_key, pattern)) 92 | tasks.append(task) 93 | 94 | await task 95 | time_to_sleep = random.randint(MIN_PAUSE, MAX_PAUSE) 96 | logger.info(f'Sleeping {time_to_sleep} seconds...') 97 | await sleep(time_to_sleep) 98 | 99 | await gather(*tasks) 100 | 101 | 102 | def start_event_loop(awaitable: Awaitable[None]) -> None: 103 | run(awaitable) 104 | 105 | 106 | if __name__ == '__main__': 107 | start_event_loop(main()) 108 | -------------------------------------------------------------------------------- /okx_data/okx_data.py: -------------------------------------------------------------------------------- 1 | API_KEY = '' 2 | SECRET_KEY = '' 3 | PASSPHRASE = '' 4 | 5 | USE_PROXY = True 6 | 7 | proxy = 'http:/login:pass@ip:port' 8 | -------------------------------------------------------------------------------- /okx_data/okx_wallets.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nftscripts/scroll/c8cadeb3a914effba7a0fb3cb085a20a181aea16/okx_data/okx_wallets.txt -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.4 2 | ccxt==4.1.16 3 | colorama==0.4.6 4 | eth_abi==5.0.1 5 | eth_typing==3.5.0 6 | hexbytes==0.3.1 7 | loguru==0.7.2 8 | py_solc_x==1.1.1 9 | pyuseragents==1.0.5 10 | SQLAlchemy==2.0.21 11 | web3==6.7.0 -------------------------------------------------------------------------------- /src/database/data/analytics_data.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from sqlalchemy import func 3 | 4 | from src.database.models import ( 5 | LiquidityTransaction, 6 | LendingTransaction, 7 | NFTTransaction, 8 | SwapTransaction, 9 | BridgeTransaction, 10 | DmailTransaction 11 | ) 12 | 13 | 14 | class AnalyticData: 15 | def __init__(self, session, dex_name: str, wallet_address: str) -> None: 16 | self.session = session 17 | self.dex_name = dex_name 18 | self.wallet_address = wallet_address 19 | 20 | @property 21 | def interactions_data( 22 | self 23 | ) -> tuple[Optional[int], Optional[int], Optional[int], Optional[int], Optional[int], Optional[int]]: 24 | with self.session: 25 | liquidity_result = self.session.query(func.count().label('interactions')).filter( 26 | LiquidityTransaction.dex_name == self.dex_name, 27 | LiquidityTransaction.wallet_address == self.wallet_address 28 | ).first() 29 | swap_result = self.session.query(func.count().label('interactions')).filter( 30 | SwapTransaction.dex_name == self.dex_name, 31 | SwapTransaction.wallet_address == self.wallet_address 32 | ).first() 33 | bridge_result = self.session.query(func.count().label('interactions')).filter( 34 | BridgeTransaction.dex_name == self.dex_name, 35 | BridgeTransaction.wallet_address == self.wallet_address 36 | ).first() 37 | dmail_result = self.session.query(func.count().label('interactions')).filter( 38 | DmailTransaction.wallet_address == self.wallet_address 39 | ).first() 40 | nft_result = self.session.query(func.count().label('interactions')).filter( 41 | NFTTransaction.nft_name == self.dex_name, 42 | NFTTransaction.wallet_address == self.wallet_address 43 | ).first() 44 | lending_result = self.session.query(func.count().label('interactions')).filter( 45 | LendingTransaction.dex_name == self.dex_name, 46 | LendingTransaction.wallet_address == self.wallet_address 47 | ).first() 48 | 49 | return swap_result[0], liquidity_result[0], bridge_result[0], dmail_result[0], nft_result[0], \ 50 | lending_result[0] 51 | 52 | @property 53 | def volume_data(self) -> tuple[Optional[int], Optional[int], Optional[int], Optional[int]]: 54 | with self.session: 55 | swap_volume = self.session.query(func.sum(SwapTransaction.amount).label('total_volume')).filter( 56 | SwapTransaction.dex_name == self.dex_name, 57 | SwapTransaction.wallet_address == self.wallet_address 58 | ).first() 59 | liquidity_volume = self.session.query(func.sum(LiquidityTransaction.amount).label('total_volume')).filter( 60 | LiquidityTransaction.dex_name == self.dex_name, 61 | LiquidityTransaction.wallet_address == self.wallet_address 62 | ).first() 63 | bridge_volume = self.session.query(func.sum(BridgeTransaction.amount).label('total_volume')).filter( 64 | BridgeTransaction.dex_name == self.dex_name, 65 | BridgeTransaction.wallet_address == self.wallet_address 66 | ).first() 67 | lending_volume = self.session.query(func.sum(LendingTransaction.amount).label('total_volume')).filter( 68 | LendingTransaction.dex_name == self.dex_name, 69 | LendingTransaction.wallet_address == self.wallet_address 70 | ).first() 71 | return swap_volume[0], liquidity_volume[0], bridge_volume[0], lending_volume[0] 72 | -------------------------------------------------------------------------------- /src/database/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | 3 | from sqlalchemy import ( 4 | create_engine, 5 | Sequence, 6 | Integer, 7 | Column, 8 | String, 9 | Float, 10 | ) 11 | 12 | Base = declarative_base() 13 | 14 | 15 | class BridgeTransaction(Base): 16 | __tablename__ = 'bridge_transactions' 17 | id = Column(Integer, Sequence('bridge_transaction_id_seq'), primary_key=True) 18 | wallet_address = Column(String) 19 | tx_hash = Column(String) 20 | dex_name = Column(String) 21 | token = Column(String) 22 | amount = Column(Float) 23 | 24 | 25 | class SwapTransaction(Base): 26 | __tablename__ = 'swap_transactions' 27 | id = Column(Integer, Sequence('swap_transaction_id_seq'), primary_key=True) 28 | wallet_address = Column(String) 29 | tx_hash = Column(String) 30 | dex_name = Column(String) 31 | from_token = Column(String) 32 | to_token = Column(String) 33 | amount = Column(Float) 34 | 35 | 36 | class LiquidityTransaction(Base): 37 | __tablename__ = 'liquidity_transactions' 38 | id = Column(Integer, Sequence('liquidity_transaction_id_seq'), primary_key=True) 39 | wallet_address = Column(String) 40 | tx_hash = Column(String) 41 | dex_name = Column(String) 42 | token = Column(String) 43 | token2 = Column(String) 44 | amount = Column(Float) 45 | 46 | 47 | class NFTTransaction(Base): 48 | __tablename__ = 'nft_transactions' 49 | id = Column(Integer, Sequence('nft_transaction_id_seq'), primary_key=True) 50 | nft_name = Column(String) 51 | wallet_address = Column(String) 52 | tx_hash = Column(String) 53 | amount = Column(Float) 54 | 55 | 56 | class DmailTransaction(Base): 57 | __tablename__ = 'dmail_transactions' 58 | id = Column(Integer, Sequence('dmail_transaction_id_seq'), primary_key=True) 59 | wallet_address = Column(String) 60 | tx_hash = Column(String) 61 | dex_name = Column(String) 62 | 63 | 64 | class LendingTransaction(Base): 65 | __tablename__ = 'lending_transactions' 66 | id = Column(Integer, Sequence('lending_transaction_id_seq'), primary_key=True) 67 | dex_name = Column(String) 68 | wallet_address = Column(String) 69 | tx_hash = Column(String) 70 | amount = Column(Float) 71 | 72 | 73 | class Analytics(Base): 74 | __tablename__ = 'analytics' 75 | id = Column(String, primary_key=True) 76 | wallet_address = Column(String) 77 | dex_name = Column(String) 78 | interactions = Column(Integer, default=0) 79 | total_volume = Column(Float) 80 | 81 | 82 | engine = create_engine('sqlite:///transactions.db') 83 | Base.metadata.create_all(engine) 84 | -------------------------------------------------------------------------------- /src/database/utils.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | import types 3 | 4 | from typing import ( 5 | Optional, 6 | Type, 7 | ) 8 | 9 | from sqlalchemy.orm import sessionmaker 10 | from loguru import logger 11 | 12 | from eth_typing import ( 13 | Address, 14 | HexStr, 15 | ) 16 | 17 | from src.database.data.analytics_data import AnalyticData 18 | 19 | from src.database.models import ( 20 | LiquidityTransaction, 21 | LendingTransaction, 22 | BridgeTransaction, 23 | DmailTransaction, 24 | SwapTransaction, 25 | NFTTransaction, 26 | Analytics, 27 | engine, 28 | ) 29 | 30 | 31 | class DataBaseUtils: 32 | SWAP_ACTION: str = 'swap' 33 | LIQUIDITY_ACTION: str = 'liquidity' 34 | MINT_ACTION: str = 'mint' 35 | BRIDGE_ACTION: str = 'bridge' 36 | DMAIL_ACTION: str = 'dmail' 37 | LENDING_ACTION: str = 'lending' 38 | 39 | def __init__(self, action: str) -> None: 40 | self.__action = action 41 | self.__session = sessionmaker(bind=engine)() 42 | 43 | if self.__action == self.SWAP_ACTION: 44 | self.__table_object = SwapTransaction 45 | elif self.__action == self.LIQUIDITY_ACTION: 46 | self.__table_object = LiquidityTransaction 47 | elif self.__action == self.MINT_ACTION: 48 | self.__table_object = NFTTransaction 49 | elif self.__action == self.BRIDGE_ACTION: 50 | self.__table_object = BridgeTransaction 51 | elif self.__action == self.DMAIL_ACTION: 52 | self.__table_object = DmailTransaction 53 | elif self.__action == self.LENDING_ACTION: 54 | self.__table_object = LendingTransaction 55 | else: 56 | raise ValueError() 57 | 58 | def __enter__(self) -> None: 59 | return self 60 | 61 | def __exit__(self, 62 | exc_type: Optional[Type[BaseException]], 63 | exc_value: Optional[BaseException], 64 | traceback: Optional[types.TracebackType]) -> None: 65 | self.__session.close() 66 | 67 | async def add_to_db(self, wallet_address: Address, tx_hash: HexStr, dex_name: str, amount: Optional[float] = None, 68 | from_token: Optional[str] = None, to_token: Optional[str] = None) -> None: 69 | transaction = self.__table_object( 70 | wallet_address=wallet_address, 71 | tx_hash=tx_hash, 72 | ) 73 | 74 | if self.__action == self.SWAP_ACTION: 75 | transaction.dex_name = dex_name 76 | transaction.from_token = from_token 77 | transaction.to_token = to_token 78 | transaction.amount = amount 79 | elif self.__action == self.LIQUIDITY_ACTION: 80 | transaction.token = from_token 81 | transaction.token2 = to_token 82 | transaction.dex_name = dex_name 83 | transaction.amount = amount 84 | elif self.__action == self.MINT_ACTION: 85 | transaction.nft_name = dex_name 86 | elif self.__action == self.BRIDGE_ACTION: 87 | transaction.dex_name = dex_name 88 | transaction.token = 'ETH' 89 | transaction.amount = amount 90 | elif self.__action == self.DMAIL_ACTION: 91 | transaction.dex_name = dex_name 92 | elif self.__action == self.LENDING_ACTION: 93 | transaction.dex_name = dex_name 94 | transaction.amount = amount 95 | 96 | self.__session.add(transaction) 97 | self.__session.commit() 98 | self.__session.close() 99 | logger.success('✔️ | Successfully added to DataBase') 100 | await sleep(3) 101 | logger.debug('🔄 | Updating «analytics» table...') 102 | await sleep(3) 103 | self._update_analytics(dex_name, wallet_address) 104 | logger.success('🆕 | Information successfully updated') 105 | 106 | def _update_analytics(self, dex_name: str, wallet_address: Address) -> None: 107 | analytic_data = AnalyticData(self.__session, dex_name, wallet_address) 108 | swap_interactions, liquidity_interactions, bridge_interactions, \ 109 | dmail_interactions, nft_interactions, lending_interactions = analytic_data.interactions_data 110 | swap_volume, liquidity_volume, bridge_volume, lending_volume = analytic_data.volume_data 111 | interactions = \ 112 | liquidity_interactions + swap_interactions + bridge_interactions + dmail_interactions + nft_interactions \ 113 | + lending_interactions 114 | 115 | if swap_volume is None: 116 | swap_volume = 0 117 | if liquidity_volume is None: 118 | liquidity_volume = 0 119 | if bridge_volume is None: 120 | bridge_volume = 0 121 | if lending_volume is None: 122 | lending_volume = 0 123 | 124 | total_volume = swap_volume + liquidity_volume + bridge_volume + lending_volume 125 | 126 | analytics_entry = self.__session.query(Analytics).filter_by(dex_name=dex_name, 127 | wallet_address=wallet_address).first() 128 | if analytics_entry: 129 | analytics_entry.interactions = interactions 130 | analytics_entry.total_volume = total_volume 131 | else: 132 | unique_id = str(hash(wallet_address) + hash(dex_name)) 133 | analytics_entry = Analytics(id=unique_id, wallet_address=wallet_address, dex_name=dex_name, 134 | interactions=interactions, total_volume=total_volume) 135 | self.__session.add(analytics_entry) 136 | 137 | self.__session.commit() 138 | -------------------------------------------------------------------------------- /src/modules/bridges/main_bridge/main_bridge.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | from src.modules.bridges.main_bridge.utils.transaction_data import create_bridge_tx 8 | from src.utils.data.chains import chain_mapping 9 | from src.utils.data.chains import ETH, SCROLL 10 | from src.utils.base_bridge import BaseBridge 11 | from src.utils.data.types import Types 12 | 13 | 14 | class MainBridge(BaseBridge): 15 | def __init__(self, private_key: str, action: str, amount: Union[float, List[float]], use_percentage: bool, 16 | bridge_percentage: Union[float, List[float]], claim_eth: bool) -> None: 17 | 18 | dex_name = 'MainBridge' 19 | if action.lower() == 'deposit': 20 | rpc = ETH.rpc 21 | contract_address = '0x6774bcbd5cecef1336b5300fb5186a12ddd8b367' 22 | abi_name = 'main_bridge' 23 | scan = chain_mapping['eth'].scan 24 | from_chain = 'ETH' 25 | to_chain = 'SCROLL' 26 | elif action.lower() == 'withdraw': 27 | rpc = SCROLL.rpc 28 | contract_address = '0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79' 29 | abi_name = 'main_bridge_scroll' 30 | scan = chain_mapping['scroll'].scan 31 | from_chain = 'SCROLL' 32 | to_chain = 'ETH' 33 | else: 34 | raise ValueError(f'Action must be deposit/withdraw only. Got {action}.') 35 | super().__init__(private_key, amount, use_percentage, bridge_percentage, contract_address, abi_name, dex_name, 36 | rpc, scan, from_chain, to_chain, claim_eth=claim_eth) 37 | 38 | def __repr__(self) -> str: 39 | return f'Ⓜ️ | {self.__class__.__name__}: {self.account_address}' 40 | 41 | def create_bridge_tx(self, contract: Contract, amount: int, web3: Web3, account_address: Address 42 | ) -> Types.BridgeTransaction: 43 | return create_bridge_tx(contract, amount, web3, account_address) 44 | 45 | def check_eligibility(self, amount: int) -> tuple[bool, None, None]: 46 | return True, None, None 47 | -------------------------------------------------------------------------------- /src/modules/bridges/main_bridge/utils/data/proof_data/request_data.py: -------------------------------------------------------------------------------- 1 | import pyuseragents 2 | 3 | 4 | headers = { 5 | 'authority': 'mainnet-api-bridge.scroll.io', 6 | 'accept': '*/*', 7 | 'accept-language': 'ru,en;q=0.9,ru-RU;q=0.8,en-US;q=0.7', 8 | 'content-type': 'application/json', 9 | 'origin': 'https://scroll.io', 10 | 'referer': 'https://scroll.io/', 11 | 'sec-ch-ua': '"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99"', 12 | 'sec-ch-ua-mobile': '?0', 13 | 'sec-ch-ua-platform': '"Windows"', 14 | 'sec-fetch-dest': 'empty', 15 | 'sec-fetch-mode': 'cors', 16 | 'sec-fetch-site': 'same-site', 17 | 'user-agent': pyuseragents.random(), 18 | } 19 | -------------------------------------------------------------------------------- /src/modules/bridges/main_bridge/utils/data/proof_data/txsbyhashes.py: -------------------------------------------------------------------------------- 1 | from aiohttp import ClientSession 2 | from asyncio import sleep 3 | from src.modules.bridges.main_bridge.utils.data.proof_data.request_data import headers 4 | from loguru import logger 5 | 6 | 7 | async def get_proof_data(tx_hash: str) -> tuple[int, int, str, str, str]: 8 | async with ClientSession(headers=headers) as session: 9 | json_data = { 10 | 'txs': [ 11 | tx_hash, 12 | ], 13 | } 14 | while True: 15 | response = await session.post(url='https://mainnet-api-bridge.scroll.io/api/txsbyhashes', json=json_data) 16 | response_text = await response.json() 17 | data = response_text['data']['result'] 18 | for information in data: 19 | amount = int(information['amount']) 20 | nonce = int(information['claimInfo']['nonce']) 21 | message = information['claimInfo']['message'] 22 | batch_index = int(information['claimInfo']['batch_index']) 23 | proof = information['claimInfo']['proof'] 24 | if proof == '0x': 25 | logger.info(f'Claim transaction is not ready yet..') 26 | await sleep(150) 27 | continue 28 | break 29 | 30 | return amount, nonce, message, batch_index, proof 31 | -------------------------------------------------------------------------------- /src/modules/bridges/main_bridge/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | 3 | from web3.contract import Contract 4 | from web3.types import TxParams 5 | from eth_typing import Address 6 | from loguru import logger 7 | from web3 import Web3 8 | 9 | from src.modules.bridges.main_bridge.utils.data.proof_data.txsbyhashes import get_proof_data 10 | from src.utils.data.chains import ETH 11 | from src.utils.user.utils import Utils 12 | 13 | 14 | def create_bridge_tx(contract: Contract, amount: int, web3: Web3, account_address: Address) -> TxParams: 15 | if contract.address == web3.to_checksum_address('0x6774bcbd5cecef1336b5300fb5186a12ddd8b367'): 16 | utils = Utils() 17 | oracle_contract = utils.load_contract( 18 | '0x0d7E906BD9cAFa154b048cFa766Cc1E54E39AF9B', 19 | web3, 20 | 'oracle' 21 | ) 22 | fee = oracle_contract.functions.estimateCrossDomainMessageFee(168000).call() 23 | 24 | tx = contract.functions.sendMessage( 25 | account_address, 26 | amount, 27 | "0x", 28 | 168000 29 | ).build_transaction({ 30 | 'value': amount + fee, 31 | 'nonce': web3.eth.get_transaction_count(account_address), 32 | 'from': account_address, 33 | "gasPrice": web3.eth.gas_price, 34 | }) 35 | elif contract.address == web3.to_checksum_address('0x4C0926FF5252A435FD19e10ED15e5a249Ba19d79'): 36 | tx = contract.functions.withdrawETH( 37 | amount, 38 | 0 39 | ).build_transaction({ 40 | 'value': amount, 41 | 'nonce': web3.eth.get_transaction_count(account_address), 42 | 'from': account_address, 43 | "gasPrice": web3.eth.gas_price, 44 | }) 45 | else: 46 | logger.error(f'Unknown error') 47 | return 48 | return tx 49 | 50 | 51 | async def claim_eth(tx_hash: str, private_key: str) -> None: 52 | web3 = Web3(Web3.HTTPProvider(ETH.rpc)) 53 | account = web3.eth.account.from_key(private_key) 54 | account_address = account.address 55 | 56 | contract = web3.eth.contract(Web3.to_checksum_address('0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367'), 57 | abi=Utils.load_abi('eth_claim')) 58 | 59 | while True: 60 | try: 61 | amount, nonce, message, batch_index, proof = await get_proof_data(tx_hash) 62 | break 63 | except Exception as ex: 64 | logger.error(f'Transaction is not ready yet | {ex}') 65 | await sleep(120) 66 | 67 | tx = contract.functions.relayMessageWithProof( 68 | Web3.to_checksum_address('0x6EA73e05AdC79974B931123675ea8F78FfdacDF0'), 69 | Web3.to_checksum_address('0x7F2b8C31F88B6006c382775eea88297Ec1e3E905'), 70 | amount, 71 | nonce, 72 | message, 73 | [batch_index, proof] 74 | ).build_transaction({ 75 | 'value': 0, 76 | 'nonce': web3.eth.get_transaction_count(account_address), 77 | 'from': account_address, 78 | "gasPrice": web3.eth.gas_price, 79 | }) 80 | while True: 81 | try: 82 | signed_tx = web3.eth.account.sign_transaction(tx, private_key) 83 | raw_tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) 84 | tx_hash = web3.to_hex(raw_tx_hash) 85 | logger.success(f'Successfully claimed {amount / 10 ** 18} ETH | TX: https://etherscan.io/tx/{tx_hash}') 86 | except Exception as ex: 87 | if 'not ready' in str(ex): 88 | logger.error('Claim transaction is not ready yet...') 89 | await sleep(150) 90 | continue 91 | else: 92 | logger.error(ex) 93 | break 94 | -------------------------------------------------------------------------------- /src/modules/bridges/orbiter/oribter_bridge.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from loguru import logger 6 | from web3 import Web3 7 | 8 | from src.modules.bridges.orbiter.utils.transaction_data import create_bridge_tx 9 | from src.utils.data.chains import chain_mapping 10 | from src.utils.base_bridge import BaseBridge 11 | from src.utils.data.types import Types 12 | 13 | 14 | class OrbiterBridge(BaseBridge): 15 | def __init__(self, private_key: str, amount: Union[float, List[float]], use_percentage: bool, 16 | bridge_percentage: Union[float, List[float]], from_chain: str, to_chain: str) -> None: 17 | dex_name = 'Orbiter' 18 | scan = chain_mapping[from_chain.lower()].scan 19 | rpc = chain_mapping[from_chain.lower()].rpc 20 | orbiter_codes = { 21 | "eth": 9001, 22 | "arb": 9002, 23 | "op": 9007, 24 | "era": 9014, 25 | "base": 9021, 26 | "scroll": 9019, 27 | "linea": 9023 28 | } 29 | code = orbiter_codes[to_chain.lower()] 30 | if use_percentage is False: 31 | if isinstance(amount, float): 32 | if not 0.005 < amount < 5: 33 | logger.error(f'Limits error. 0.005 < amount < 5. Got {amount}') 34 | return 35 | elif isinstance(amount, List): 36 | if not (amount[0] > 0.005 and amount[1] < 5): 37 | logger.error(f'Limits error. 0.0035 < amount < 0.2. Got {amount}') 38 | return 39 | else: 40 | logger.error(f'amount must be List or float. Got {type(amount)}') 41 | return 42 | 43 | super().__init__(private_key, amount, use_percentage, bridge_percentage, contract_address=None, abi_name=None, 44 | dex_name=dex_name, rpc=rpc, scan=scan, from_chain=from_chain, to_chain=to_chain, code=code) 45 | 46 | def __repr__(self) -> str: 47 | return f'🛸 | {self.__class__.__name__}: {self.account_address} | {self.from_chain} => {self.to_chain}' 48 | 49 | def create_bridge_tx(self, contract: Contract, amount: int, web3: Web3, account_address: Address 50 | ) -> Types.BridgeTransaction: 51 | return create_bridge_tx(amount, web3, account_address) 52 | 53 | def check_eligibility(self, amount: int) -> tuple[bool, Union[float, None], Union[int, None]]: 54 | min_limit = 0.005 55 | max_limit = 5 56 | 57 | if min_limit < amount / 10 ** 18 < max_limit: 58 | return True, None, None 59 | return True, min_limit, max_limit 60 | -------------------------------------------------------------------------------- /src/modules/bridges/orbiter/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from web3.types import TxParams 2 | from eth_typing import Address 3 | from web3 import Web3 4 | 5 | 6 | def create_bridge_tx(amount: int, web3: Web3, account_address: Address) -> TxParams: 7 | return { 8 | 'chainId': web3.eth.chain_id, 9 | 'from': account_address, 10 | 'to': web3.to_checksum_address('0x80c67432656d59144ceff962e8faf8926599bcf8'), 11 | 'value': amount, 12 | 'nonce': web3.eth.get_transaction_count(account_address), 13 | "gasPrice": web3.eth.gas_price, 14 | } 15 | -------------------------------------------------------------------------------- /src/modules/bridges/owlto/owlto_bridge.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from loguru import logger 6 | from web3 import Web3 7 | 8 | from src.modules.bridges.owlto.utils.transaction_data import create_bridge_tx 9 | from src.utils.data.chains import chain_mapping 10 | from src.utils.base_bridge import BaseBridge 11 | from src.utils.data.types import Types 12 | 13 | 14 | class OwlBridge(BaseBridge): 15 | def __init__(self, private_key: str, amount: Union[float, List[float]], use_percentage: bool, 16 | bridge_percentage: Union[float, List[float]], from_chain: str, to_chain: str) -> None: 17 | dex_name = 'Owlto' 18 | scan = chain_mapping[from_chain.lower()].scan 19 | rpc = chain_mapping[from_chain.lower()].rpc 20 | owlto_codes = { 21 | 'scroll': '0006', 22 | 'era': '0002', 23 | 'base': '0012', 24 | 'linea': '0007', 25 | 'arb': '0004', 26 | 'op': '0003', 27 | 'eth': '0001' 28 | } 29 | code = owlto_codes[to_chain.lower()] 30 | if use_percentage is False: 31 | if isinstance(amount, float): 32 | if not 0.0035 < amount < 0.2: 33 | logger.error(f'Limits error. 0.0035 < amount < 0.2. Got {amount}') 34 | return 35 | elif isinstance(amount, List): 36 | if not (amount[0] > 0.0035 and amount[1] < 0.2): 37 | logger.error(f'Limits error. 0.0035 < amount < 0.2. Got {amount}') 38 | return 39 | else: 40 | logger.error(f'amount must be List or float. Got {type(amount)}') 41 | return 42 | 43 | super().__init__(private_key, amount, use_percentage, bridge_percentage, contract_address=None, abi_name=None, 44 | dex_name=dex_name, rpc=rpc, scan=scan, from_chain=from_chain, to_chain=to_chain, code=code) 45 | 46 | def __repr__(self) -> str: 47 | return f'🦉 | {self.__class__.__name__}: {self.account_address} | {self.from_chain} => {self.to_chain}' 48 | 49 | def create_bridge_tx(self, contract: Contract, amount: int, web3: Web3, account_address: Address 50 | ) -> Types.BridgeTransaction: 51 | return create_bridge_tx(amount, web3, account_address) 52 | 53 | def check_eligibility(self, amount: int) -> tuple[bool, Union[float, None], Union[float, None]]: 54 | min_limit = 0.0035 55 | max_limit = 0.2 56 | 57 | if min_limit < amount / 10 ** 18 < max_limit: 58 | return True, None, None 59 | return False, min_limit, max_limit 60 | -------------------------------------------------------------------------------- /src/modules/bridges/owlto/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from web3.types import TxParams 2 | from eth_typing import Address 3 | from web3 import Web3 4 | 5 | 6 | def create_bridge_tx(amount: int, web3: Web3, account_address: Address) -> TxParams: 7 | return { 8 | 'chainId': web3.eth.chain_id, 9 | 'from': account_address, 10 | 'to': web3.to_checksum_address('0x5e809a85aa182a9921edd10a4163745bb3e36284'), 11 | 'value': amount, 12 | 'nonce': web3.eth.get_transaction_count(account_address), 13 | "gasPrice": web3.eth.gas_price, 14 | } 15 | -------------------------------------------------------------------------------- /src/modules/deploy/contract/contract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | contract SimpleStorage { 4 | uint256 private storedData; 5 | 6 | function set(uint256 x) public { 7 | storedData = x; 8 | } 9 | 10 | function get() public view returns (uint256) { 11 | return storedData; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/deploy/contract_deployer.py: -------------------------------------------------------------------------------- 1 | from src.utils.user.account import Account 2 | 3 | from src.modules.deploy.utils.data_exctract import ( 4 | get_bytecode, 5 | get_abi, 6 | compile_contract, 7 | ) 8 | 9 | 10 | class Deployer(Account): 11 | def __init__(self, private_key: str, use_0x_bytecode: bool): 12 | self.use_0x_bytecode = use_0x_bytecode 13 | super().__init__(private_key) 14 | 15 | def __repr__(self) -> str: 16 | return f'Deploying contract for address [{self.account_address}]' 17 | 18 | def deploy(self) -> None: 19 | if not self.use_0x_bytecode: 20 | compile_contract() 21 | abi = get_abi() 22 | bytecode = get_bytecode() 23 | else: 24 | abi = self.load_abi('deploy') 25 | bytecode = '0x' 26 | 27 | contract = self.web3.eth.contract( 28 | abi=abi, 29 | bytecode=bytecode 30 | ) 31 | 32 | tx = contract.constructor().build_transaction({ 33 | 'value': 0, 34 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 35 | 'from': self.account_address, 36 | 'gasPrice': 0, 37 | 'gas': 0 38 | }) 39 | tx.update({'gasPrice': self.web3.eth.gas_price}) 40 | gas_limit = self.web3.eth.estimate_gas(tx) 41 | tx.update({'gas': gas_limit}) 42 | 43 | tx_hash = self.sign_transaction(tx) 44 | 45 | self.logger.success( 46 | f'Successfully deployed contract for address [{self.account_address}] | TX: https://blockscout.scroll.io/tx/{tx_hash}' 47 | ) 48 | -------------------------------------------------------------------------------- /src/modules/deploy/utils/data_exctract.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from solcx import ( 4 | compile_standard, 5 | install_solc, 6 | ) 7 | 8 | 9 | def compile_contract() -> None: 10 | install_solc('0.8.0') 11 | with open('src/modules/deploy/contract/contract.sol', 'r') as file: 12 | contract = file.read() 13 | 14 | compiled_sol = compile_standard( 15 | { 16 | "language": "Solidity", 17 | "sources": {"contract.sol": {"content": contract}}, 18 | "settings": { 19 | "outputSelection": { 20 | "*": { 21 | "*": ["abi", "metadata", "evm.bytecode", "evm.bytecode.sourceMap"] 22 | } 23 | } 24 | }, 25 | }, 26 | solc_version="0.8.0", 27 | ) 28 | 29 | with open("compiled_code.json", "w") as file: 30 | json.dump(compiled_sol, file) 31 | 32 | 33 | def get_bytecode() -> str: 34 | with open('compiled_code.json', 'r') as file: 35 | compiled_sol = json.load(file) 36 | 37 | bytecode = compiled_sol["contracts"]["contract.sol"]["SimpleStorage"]["evm"][ 38 | "bytecode" 39 | ]["object"] 40 | return bytecode 41 | 42 | 43 | def get_abi() -> str: 44 | with open('compiled_code.json', 'r') as file: 45 | compiled_sol = json.load(file) 46 | 47 | abi = json.loads( 48 | compiled_sol["contracts"]["contract.sol"]["SimpleStorage"]["metadata"] 49 | )["output"]["abi"] 50 | return abi 51 | -------------------------------------------------------------------------------- /src/modules/dmail/dmail.py: -------------------------------------------------------------------------------- 1 | import random 2 | from hashlib import sha256 3 | 4 | from web3 import Web3 5 | 6 | from src.utils.data.contracts import ( 7 | contracts, 8 | abi_names, 9 | ) 10 | from src.utils.wrappers.decorators import retry 11 | from src.utils.user.account import Account 12 | from src.database.utils import DataBaseUtils 13 | from config import USE_DATABASE 14 | 15 | 16 | class Dmail(Account): 17 | def __init__(self, private_key: str) -> None: 18 | self.contract_address = contracts['dmail'] 19 | self.abi_name = abi_names['dmail'] 20 | super().__init__(private_key=private_key) 21 | self.db_utils = DataBaseUtils('dmail') 22 | 23 | def __repr__(self) -> str: 24 | return f'{self.account_address} | Sending mail...' 25 | 26 | @retry() 27 | async def send_mail(self): 28 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 29 | email = sha256(str(1e11 * random.random()).encode()).hexdigest() 30 | theme = sha256(str(1e11 * random.random()).encode()).hexdigest() 31 | 32 | data = contract.encodeABI("send_mail", args=(email, theme)) 33 | 34 | tx = { 35 | 'chainId': self.web3.eth.chain_id, 36 | 'from': self.account_address, 37 | 'to': Web3.to_checksum_address(self.contract_address), 38 | 'data': data, 39 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 40 | 'gasPrice': self.web3.eth.gas_price, 41 | 'gas': 0 42 | } 43 | 44 | gas_limit = self.web3.eth.estimate_gas(tx) 45 | tx.update({'gas': gas_limit}) 46 | 47 | tx_hash = self.sign_transaction(tx) 48 | self.wait_until_tx_finished(tx_hash) 49 | if USE_DATABASE: 50 | await self.db_utils.add_to_db(self.account_address, tx_hash, 'Dmail') 51 | -------------------------------------------------------------------------------- /src/modules/lendings/aave/aave.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from typing import ( 4 | Union, 5 | List, 6 | ) 7 | 8 | from src.database.utils import DataBaseUtils 9 | from src.utils.user.account import Account 10 | from src.utils.wrappers.decorators import retry 11 | from src.utils.data.contracts import contracts, abi_names 12 | 13 | 14 | class Aave(Account): 15 | def __init__(self, private_key: str, amount: Union[float, List[float]], use_percentage: bool, 16 | deposit_percentage: Union[float, List[float]], remove_percentage: Union[float, List[float]], 17 | remove_all: bool) -> None: 18 | self.private_key = private_key 19 | 20 | super().__init__(private_key) 21 | 22 | if isinstance(amount, List): 23 | self.amount = random.uniform(amount[0], amount[1]) 24 | elif isinstance(amount, float): 25 | self.amount = amount 26 | else: 27 | self.logger.error(f'amount must be list[float] or float. Got {type(amount)}') 28 | return 29 | self.use_percentage = use_percentage 30 | 31 | if isinstance(deposit_percentage, List): 32 | self.deposit_percentage = random.uniform(deposit_percentage[0], deposit_percentage[1]) 33 | elif isinstance(deposit_percentage, float): 34 | self.deposit_percentage = deposit_percentage 35 | else: 36 | self.logger.error(f'amount must be list[float] or float. Got {type(deposit_percentage)}') 37 | return 38 | 39 | if isinstance(remove_percentage, List): 40 | self.remove_percentage = random.uniform(remove_percentage[0], remove_percentage[1]) 41 | elif isinstance(remove_percentage, float): 42 | self.remove_percentage = remove_percentage 43 | else: 44 | self.logger.error(f'amount must be list[float] or float. Got {type(remove_percentage)}') 45 | return 46 | 47 | self.remove_all = remove_all 48 | self.db_utils = DataBaseUtils('lending') 49 | 50 | def __repr__(self) -> str: 51 | return f'{self.__class__.__name__} | [{self.account_address}]' 52 | 53 | async def get_deposit_amount(self): 54 | aave_weth_contract = self.load_contract(contracts['aave']['aave_weth'], self.web3, 'erc20') 55 | 56 | amount = aave_weth_contract.functions.balanceOf(self.account_address).call() 57 | 58 | return amount 59 | 60 | @retry() 61 | async def deposit(self) -> None: 62 | contract = self.load_contract(contracts['aave']['aave_contract'], self.web3, abi_names['aave']) 63 | amount = int(self.amount * 10 ** 18) 64 | balance = self.get_wallet_balance('ETH', '...') 65 | if self.use_percentage: 66 | amount = int(balance * self.deposit_percentage) 67 | 68 | if amount > balance: 69 | self.logger.error(f'Not enough balance for wallet | [{self.account_address}]') 70 | return 71 | 72 | tx = contract.functions.depositETH( 73 | self.web3.to_checksum_address("0x11fCfe756c05AD438e312a7fd934381537D3cFfe"), 74 | self.account_address, 75 | 0 76 | ).build_transaction({ 77 | 'value': amount, 78 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 79 | 'from': self.account_address, 80 | "gasPrice": self.web3.eth.gas_price 81 | }) 82 | self.logger.info(f"[{self.account_address}] deposit on Aave | {amount / 10 ** 18} ETH") 83 | 84 | tx_hash = self.sign_transaction(tx) 85 | confirmed = self.wait_until_tx_finished(tx_hash) 86 | 87 | if confirmed: 88 | self.logger.success( 89 | f'Successfully deposited {amount / 10 ** 18} ETH | TX: https://scrollscan.com/tx/{tx_hash}') 90 | 91 | @retry() 92 | async def withdraw(self) -> Union[str, bool]: 93 | contract = self.load_contract(contracts['aave']['aave_contract'], self.web3, abi_names['aave']) 94 | deposited_amount = await self.get_deposit_amount() 95 | amount = int(self.amount * 10 ** 18) 96 | if deposited_amount <= (0.000000001 * 10 ** 18): 97 | self.logger.error(f'Your deposited amount is 0 | [{self.account_address}]') 98 | return 'ZeroBalance' 99 | 100 | if self.remove_all is True: 101 | amount = deposited_amount 102 | if self.use_percentage: 103 | amount = int(deposited_amount * self.remove_percentage) 104 | 105 | self.logger.info( 106 | f"[{self.account_address}] withdrawing from Aave | {amount / 10 ** 18} ETH" 107 | ) 108 | 109 | await self.approve_token( 110 | amount, 111 | self.private_key, 112 | "0xf301805be1df81102c957f6d4ce29d2b8c056b2a", 113 | contracts['aave']['aave_contract'], 114 | self.account_address, 115 | self.web3 116 | ) 117 | 118 | tx = contract.functions.withdrawETH( 119 | self.web3.to_checksum_address("0x11fCfe756c05AD438e312a7fd934381537D3cFfe"), 120 | amount, 121 | self.account_address 122 | ).build_transaction({ 123 | 'value': 0, 124 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 125 | 'from': self.account_address, 126 | "gasPrice": self.web3.eth.gas_price 127 | }) 128 | 129 | tx_hash = self.sign_transaction(tx) 130 | confirmed = self.wait_until_tx_finished(tx_hash) 131 | 132 | if confirmed: 133 | self.logger.success( 134 | f'Successfully withdrawn {amount / 10 ** 18} ETH | TX: https://scrollscan.com/tx/{tx_hash}') 135 | return True 136 | return False 137 | -------------------------------------------------------------------------------- /src/modules/lendings/layerbank/layerbank.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | import random 3 | 4 | from src.database.utils import DataBaseUtils 5 | from src.utils.user.account import Account 6 | from src.utils.data.tokens import tokens 7 | from config import USE_DATABASE 8 | 9 | from src.utils.data.contracts import ( 10 | contracts, 11 | abi_names, 12 | ) 13 | 14 | 15 | class LayerBankDeposit(Account): 16 | def __init__(self, private_key: str, amount: Union[float, List[float]], use_percentage: bool, 17 | percentage: Union[float, List[float]], only_collateral: bool) -> None: 18 | 19 | super().__init__(private_key=private_key) 20 | 21 | if isinstance(amount, List): 22 | self.amount = random.uniform(amount[0], amount[1]) 23 | elif isinstance(amount, float): 24 | self.amount = amount 25 | else: 26 | self.logger.error(f'amount must be float or list[float]. Got {type(amount)}') 27 | return 28 | 29 | self.use_percentage = use_percentage 30 | if isinstance(percentage, List): 31 | self.percentage = random.uniform(percentage[0], percentage[1]) 32 | elif isinstance(percentage, float): 33 | self.percentage = percentage 34 | else: 35 | self.logger.error(f'percentage must be float or list[float]. Got {type(percentage)}') 36 | return 37 | 38 | self.contract = self.load_contract( 39 | address=contracts['layerbank'], 40 | web3=self.web3, 41 | abi_name=abi_names['layerbank'] 42 | ) 43 | self.only_collateral = only_collateral 44 | self.db_utils = DataBaseUtils('lending') 45 | 46 | def __repr__(self) -> str: 47 | return f'{self.__class__.__name__} | [{self.account_address}]' 48 | 49 | async def deposit(self) -> None: 50 | balance = self.get_wallet_balance('ETH', tokens['ETH']) 51 | 52 | if balance == 0: 53 | self.logger.warning(f'Your balance is 0 | [{self.account_address}]') 54 | return 55 | if not self.only_collateral: 56 | if self.use_percentage: 57 | amount = int(balance * self.percentage) 58 | else: 59 | amount = int(self.amount * 10 ** 18) 60 | else: 61 | amount = 0 62 | 63 | if not self.only_collateral: 64 | tx = self.contract.functions.supply( 65 | self.web3.to_checksum_address('0x274C3795dadfEbf562932992bF241ae087e0a98C'), 66 | amount 67 | ).build_transaction({ 68 | 'value': amount, 69 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 70 | 'from': self.account_address, 71 | "gasPrice": self.web3.eth.gas_price 72 | }) 73 | else: 74 | txs = [self.contract.functions.enterMarkets( 75 | [self.web3.to_checksum_address('0x274C3795dadfEbf562932992bF241ae087e0a98C')] 76 | ), self.contract.functions.exitMarket( 77 | self.web3.to_checksum_address('0x274C3795dadfEbf562932992bF241ae087e0a98C') 78 | )] 79 | tx = random.choice(txs).build_transaction({ 80 | 'value': amount, 81 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 82 | 'from': self.account_address, 83 | "gasPrice": self.web3.eth.gas_price 84 | }) 85 | 86 | tx_hash = self.sign_transaction(tx) 87 | confirmed = self.wait_until_tx_finished(tx_hash) 88 | 89 | if confirmed: 90 | if not self.only_collateral: 91 | self.logger.success( 92 | f'Successfully deposited {amount / 10 ** 18} ETH | TX: https://blockscout.scroll.io/tx/{tx_hash}' 93 | ) 94 | else: 95 | self.logger.success( 96 | f'Successfully used collateral | TX: https://blockscout.scroll.io/tx/{tx_hash}' 97 | ) 98 | if USE_DATABASE: 99 | await self.db_utils.add_to_db(self.account_address, f'https://blockscout.scroll.io/tx/{tx_hash}', 100 | self.__class__.__name__, amount / 10 ** 18) 101 | 102 | 103 | class LayerBankWithdraw(Account): 104 | def __init__(self, private_key: str, amount: Union[float, List[float]], withdraw_all: bool, use_percentage: bool, 105 | percentage: Union[float, List[float]]) -> None: 106 | 107 | super().__init__(private_key=private_key) 108 | 109 | if isinstance(amount, List): 110 | self.amount = random.uniform(amount[0], amount[1]) 111 | elif isinstance(amount, float): 112 | self.amount = amount 113 | else: 114 | self.logger.error(f'amount must be float or list[float]. Got {type(amount)}') 115 | return 116 | 117 | self.withdraw_all = withdraw_all 118 | self.use_percentage = use_percentage 119 | if withdraw_all is True and use_percentage is True: 120 | self.logger.warning(f'You are using withdraw_all and use_percentage both True. Using withdraw_all = True') 121 | if isinstance(percentage, List): 122 | self.percentage = random.uniform(percentage[0], percentage[1]) 123 | elif isinstance(percentage, float): 124 | self.percentage = percentage 125 | else: 126 | self.logger.error(f'percentage must be float or list[float]. Got {type(percentage)}') 127 | return 128 | 129 | self.contract = self.load_contract( 130 | address=contracts['layerbank'], 131 | web3=self.web3, 132 | abi_name=abi_names['layerbank'] 133 | ) 134 | self.db_utils = DataBaseUtils('lending') 135 | 136 | def __repr__(self) -> str: 137 | return f'{self.__class__.__name__} | [{self.account_address}]' 138 | 139 | async def withdraw(self) -> Union[str, bool]: 140 | balance = self.get_wallet_balance('...', '0x274C3795dadfEbf562932992bF241ae087e0a98C') 141 | 142 | if balance == 0: 143 | self.logger.warning(f"You don't have any tokens to withdraw | [{self.account_address}]") 144 | return 'ZeroBalance' 145 | 146 | if self.use_percentage: 147 | amount = int(balance * self.percentage) 148 | else: 149 | amount = int(self.amount * 10 ** 18) 150 | 151 | if self.withdraw_all: 152 | amount = balance 153 | 154 | tx = self.contract.functions.redeemToken( 155 | self.web3.to_checksum_address('0x274C3795dadfEbf562932992bF241ae087e0a98C'), 156 | amount 157 | ).build_transaction({ 158 | 'value': 0, 159 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 160 | 'from': self.account_address, 161 | "gasPrice": self.web3.eth.gas_price 162 | }) 163 | 164 | tx_hash = self.sign_transaction(tx) 165 | confirmed = self.wait_until_tx_finished(tx_hash) 166 | 167 | if confirmed: 168 | self.logger.success( 169 | f'Successfully withdrawn {amount / 10 ** 18} ETH | TX: https://blockscout.scroll.io/tx/{tx_hash}' 170 | ) 171 | if USE_DATABASE: 172 | await self.db_utils.add_to_db(self.account_address, f'https://blockscout.scroll.io/tx/{tx_hash}', 173 | self.__class__.__name__, amount / 10 ** 18) 174 | 175 | return True 176 | return False 177 | -------------------------------------------------------------------------------- /src/modules/nft/l2pass/l2pass.py: -------------------------------------------------------------------------------- 1 | from web3.contract import Contract 2 | 3 | from src.utils.wrappers.decorators import retry 4 | from src.utils.user.account import Account 5 | 6 | from src.utils.data.contracts import ( 7 | contracts, 8 | abi_names, 9 | ) 10 | 11 | 12 | class L2Pass(Account): 13 | def __init__(self, private_key: str) -> None: 14 | super().__init__(private_key) 15 | 16 | def __str__(self) -> str: 17 | return f'[{self.account_address}] | Minting L2Pass NFT' 18 | 19 | @staticmethod 20 | def get_mint_price(contract: Contract) -> int: 21 | price = contract.functions.mintPrice().call() 22 | 23 | return price 24 | 25 | @retry() 26 | async def mint(self) -> None: 27 | contract = self.load_contract(contracts['l2pass'], self.web3, abi_names['l2pass']) 28 | price = self.get_mint_price(contract) 29 | tx = contract.functions.mint(1).build_transaction({ 30 | 'value': price, 31 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 32 | 'from': self.account_address, 33 | "gasPrice": self.web3.eth.gas_price 34 | }) 35 | tx_hash = self.sign_transaction(tx) 36 | self.wait_until_tx_finished(tx_hash) 37 | -------------------------------------------------------------------------------- /src/modules/nft/omnisea/omnisea.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | 4 | from src.utils.wrappers.decorators import retry 5 | from src.utils.user.account import Account 6 | 7 | from src.utils.data.contracts import ( 8 | contracts, 9 | abi_names, 10 | ) 11 | 12 | 13 | class Omnisea(Account): 14 | def __init__(self, private_key: str) -> None: 15 | super().__init__(private_key) 16 | 17 | def __str__(self) -> str: 18 | return f'[{self.account_address}] | Creating NFT on Omnisea' 19 | 20 | @staticmethod 21 | def generate_collection_data() -> tuple[str, str]: 22 | title = "".join(random.sample([chr(i) for i in range(97, 123)], random.randint(5, 15))) 23 | symbol = "".join(random.sample([chr(i) for i in range(65, 91)], random.randint(3, 6))) 24 | return title, symbol 25 | 26 | @retry() 27 | async def create(self) -> None: 28 | contract = self.load_contract(contracts['omnisea'], self.web3, abi_names['omnisea']) 29 | title, symbol = self.generate_collection_data() 30 | 31 | tx = contract.functions.create([ 32 | title, 33 | symbol, 34 | "", 35 | "", 36 | 0, 37 | True, 38 | 0, 39 | int(time.time()) + 1000000] 40 | ).build_transaction({ 41 | 'value': 0, 42 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 43 | 'from': self.account_address, 44 | "gasPrice": self.web3.eth.gas_price 45 | }) 46 | 47 | tx_hash = self.sign_transaction(tx) 48 | self.wait_until_tx_finished(tx_hash) 49 | -------------------------------------------------------------------------------- /src/modules/nft/scrollcitizen/scrollcitizen.py: -------------------------------------------------------------------------------- 1 | from web3.contract import Contract 2 | from web3.types import TxParams 3 | 4 | from src.utils.data.contracts import abi_names 5 | from src.utils.base_mint import BaseMint 6 | 7 | 8 | class ScrollCitizen(BaseMint): 9 | def __init__(self, private_key: str, contract_address: str) -> None: 10 | abi_name = abi_names['scroll_citizen'] 11 | nft_name = 'ScrollCitizen' 12 | super().__init__(private_key, contract_address, abi_name, nft_name) 13 | 14 | def __repr__(self) -> str: 15 | return f'[{self.account_address}] | {self.__class__.__name__} Mint' 16 | 17 | def create_mint_tx(self, contract: Contract) -> TxParams: 18 | tx = contract.functions.mint( 19 | self.web3.to_checksum_address("0x08770dA8fE1541D771B77C9C997A0E4a085A6E59") 20 | ).build_transaction({ 21 | 'value': int(0.0001 * 10 ** 18), 22 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 23 | 'from': self.account_address, 24 | "gasPrice": self.web3.eth.gas_price 25 | }) 26 | return tx 27 | -------------------------------------------------------------------------------- /src/modules/nft/zerius/utils/data.py: -------------------------------------------------------------------------------- 1 | from web3.contract import Contract 2 | from eth_typing import Address 3 | from web3 import Web3 4 | 5 | 6 | def get_l0_fee(contract: Contract, chain_id: str, nft_id: int, account_address: Address) -> int: 7 | fee = contract.functions.estimateSendFee( 8 | chain_id, 9 | account_address, 10 | nft_id, 11 | False, 12 | "0x" 13 | ).call() 14 | 15 | return int(fee[0] * 1.2) 16 | 17 | 18 | def get_nft_id(web3: Web3, tx_hash: str) -> int: 19 | receipts = web3.eth.get_transaction_receipt(tx_hash) 20 | 21 | nft_id = int(receipts["logs"][0]["topics"][-1].hex(), 0) 22 | 23 | return nft_id 24 | -------------------------------------------------------------------------------- /src/modules/nft/zerius/zerius.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | import random 3 | 4 | from typing import ( 5 | Union, 6 | List, 7 | ) 8 | 9 | from eth_typing import HexStr 10 | 11 | from src.database.utils import DataBaseUtils 12 | from src.utils.user.account import Account 13 | from config import USE_DATABASE 14 | 15 | from src.modules.nft.zerius.utils.data import ( 16 | get_l0_fee, 17 | get_nft_id, 18 | ) 19 | 20 | 21 | class Zerius(Account): 22 | def __init__(self, private_key: str, chain_to_bridge: Union[str, List[str]]) -> None: 23 | super().__init__(private_key=private_key) 24 | 25 | self.contract = self.load_contract('0xeb22c3e221080ead305cae5f37f0753970d973cd', self.web3, 'zerius') 26 | self.chain_ids = { 27 | "arb": 110, 28 | "op": 111, 29 | "polygon": 109, 30 | "bsc": 102, 31 | "avax": 106, 32 | } 33 | if isinstance(chain_to_bridge, List): 34 | self.chain_to_bridge = random.choice(chain_to_bridge) 35 | elif isinstance(chain_to_bridge, str): 36 | self.chain_to_bridge = chain_to_bridge 37 | else: 38 | self.logger.error(f'chain_to_bridge must be str or List[str]. Got {type(chain_to_bridge)}') 39 | return 40 | self.chain_id = self.chain_ids[self.chain_to_bridge.lower()] 41 | self.db_utils = DataBaseUtils('mint') 42 | 43 | def __repr__(self) -> None: 44 | return f'{self.__class__.__name__} | {self.account_address}' 45 | 46 | def mint(self) -> HexStr: 47 | mint_fee = self.contract.functions.mintFee().call() 48 | tx = self.contract.functions.mint().build_transaction({ 49 | 'from': self.account_address, 50 | 'value': mint_fee, 51 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 52 | "gasPrice": self.web3.eth.gas_price 53 | }) 54 | 55 | tx_hash = self.sign_transaction(tx) 56 | confirmed = self.wait_until_tx_finished(tx_hash) 57 | 58 | if confirmed: 59 | self.logger.success( 60 | f'Successfully Minted NFT | TX: https://blockscout.scroll.io/tx/{tx_hash}' 61 | ) 62 | return tx_hash 63 | 64 | async def bridge(self) -> None: 65 | mint_hash = self.mint() 66 | 67 | if not mint_hash: 68 | return 69 | 70 | await sleep(10) 71 | nft_id = get_nft_id(self.web3, mint_hash) 72 | random_sleep = random.randint(20, 30) 73 | self.logger.debug(f'Sleeping {random_sleep} seconds before bridge...') 74 | await sleep(random_sleep) 75 | 76 | l0_fee = get_l0_fee(self.contract, self.chain_id, nft_id, self.account_address) 77 | base_bridge_fee = self.contract.functions.bridgeFee().call() 78 | 79 | tx = self.contract.functions.sendFrom( 80 | self.account_address, 81 | self.chain_id, 82 | self.account_address, 83 | nft_id, 84 | '0x0000000000000000000000000000000000000000', 85 | '0x0000000000000000000000000000000000000000', 86 | '0x0001000000000000000000000000000000000000000000000000000000000003d090' 87 | ).build_transaction({ 88 | 'from': self.account_address, 89 | 'value': l0_fee + base_bridge_fee, 90 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 91 | "gasPrice": self.web3.eth.gas_price 92 | }) 93 | 94 | tx_hash = self.sign_transaction(tx) 95 | confirmed = self.wait_until_tx_finished(tx_hash) 96 | 97 | if confirmed: 98 | self.logger.success( 99 | f'Successfully Bridged NFT into {self.chain_to_bridge} | TX: https://blockscout.scroll.io/tx/{tx_hash}' 100 | ) 101 | 102 | if USE_DATABASE: 103 | await self.db_utils.add_to_db(self.account_address, f'https://blockscout.scroll.io/tx/{tx_hash}', 'Zerius') 104 | -------------------------------------------------------------------------------- /src/modules/nft/zkstars/zkstars.py: -------------------------------------------------------------------------------- 1 | from web3.contract import Contract 2 | from web3.types import TxParams 3 | 4 | from src.utils.data.contracts import abi_names 5 | from src.utils.base_mint import BaseMint 6 | 7 | 8 | class ZKStars(BaseMint): 9 | def __init__(self, private_key: str, contract_address: str) -> None: 10 | abi_name = abi_names['zkstars'] 11 | nft_name = 'ZKStars' 12 | super().__init__(private_key, contract_address, abi_name, nft_name) 13 | 14 | def __repr__(self) -> str: 15 | return f'[{self.account_address}] | {self.__class__.__name__} Mint' 16 | 17 | def create_mint_tx(self, contract: Contract) -> TxParams: 18 | mint_price = contract.functions.getPrice().call() 19 | 20 | tx = contract.functions.safeMint( 21 | self.web3.to_checksum_address("0x739815d56A5FFc21950271199D2cf9E23B944F1c") 22 | ).build_transaction({ 23 | 'value': mint_price, 24 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 25 | 'from': self.account_address, 26 | "gasPrice": self.web3.eth.gas_price 27 | }) 28 | return tx 29 | -------------------------------------------------------------------------------- /src/modules/okx_withdraw/okx_withdraw.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union 2 | from asyncio import sleep 3 | import random 4 | 5 | from loguru import logger 6 | from web3 import Web3 7 | import ccxt 8 | 9 | from src.modules.okx_withdraw.utils.okx_sub_transfer import transfer_from_subaccs_to_main 10 | from src.modules.okx_withdraw.utils.data import get_withdrawal_fee 11 | from src.utils.wrappers.decorators import retry 12 | from src.utils.user.account import Account 13 | 14 | from okx_data.okx_data import ( 15 | USE_PROXY, 16 | proxy, 17 | ) 18 | 19 | from src.utils.data.chains import ( 20 | okx_chain_mapping, 21 | chain_mapping, 22 | ) 23 | 24 | 25 | class OkxWithdraw: 26 | def __init__(self, api_key: str, api_secret: str, passphrase: str, amount: Union[float, List[float]], 27 | receiver_address: str, chain: str) -> None: 28 | self.api_key = api_key 29 | self.api_secret = api_secret 30 | self.passphrase = passphrase 31 | if isinstance(amount, List): 32 | self.amount = round(random.uniform(amount[0], amount[1]), 6) 33 | elif isinstance(amount, float): 34 | self.amount = amount 35 | else: 36 | logger.error(f'amount must be List[float] or float. Got {type(amount)}') 37 | return 38 | 39 | self.receiver_address = receiver_address 40 | self.chain = chain 41 | rpc = okx_chain_mapping[chain].rpc 42 | self.web3 = Web3(Web3.HTTPProvider(rpc)) 43 | self.okex = ccxt.okx({ 44 | 'apiKey': self.api_key, 45 | 'secret': self.api_secret, 46 | 'password': self.passphrase, 47 | 'enableRateLimit': True, 48 | 'proxies': { 49 | 'http': proxy if USE_PROXY is True else None, 50 | 'https': proxy if USE_PROXY is True else None 51 | }, 52 | }) 53 | 54 | def __repr__(self) -> str: 55 | return f'Withdrawing {self.amount} ETH to {self.receiver_address} | CHAIN: {self.chain}' 56 | 57 | async def withdraw(self) -> None: 58 | try: 59 | chain_name = 'ETH' + '-' + self.chain 60 | fee = await get_withdrawal_fee('ETH', chain_name, self.okex) 61 | eth_balance_before_withdraw = self.web3.eth.get_balance( 62 | self.web3.to_checksum_address(self.receiver_address)) 63 | self.okex.withdraw('ETH', self.amount, self.receiver_address, params={ 64 | 'toAddress': self.receiver_address, 65 | 'chainName': chain_name, 66 | 'dest': 4, 67 | 'fee': fee, 68 | 'pwd': '-', 69 | 'amt': self.amount, 70 | 'network': self.chain 71 | }) 72 | 73 | logger.success( 74 | f'Successfully withdrawn {self.amount} ETH to {self.chain} for wallet {self.receiver_address}') 75 | await self.wait_for_eth(eth_balance_before_withdraw) 76 | 77 | except Exception as ex: 78 | logger.error(f'Something went wrong {ex}') 79 | return 80 | 81 | async def wait_for_eth(self, eth_balance_before_withdraw: int) -> None: 82 | logger.info(f'Waiting for ETH to arrive on Metamask...') 83 | while True: 84 | try: 85 | balance = self.web3.eth.get_balance( 86 | self.web3.to_checksum_address(self.receiver_address)) 87 | if balance > eth_balance_before_withdraw: 88 | logger.success(f'ETH has arrived | [{self.receiver_address}]') 89 | break 90 | await sleep(20) 91 | except Exception as ex: 92 | logger.error(f'Something went wrong {ex}') 93 | await sleep(10) 94 | continue 95 | 96 | 97 | class OkxDeposit(Account): 98 | def __init__(self, private_key: str, from_chain: str, amount: Union[float, List[float]], 99 | keep_value: Union[float, List[float]], withdraw_all: bool, receiver_address: str) -> None: 100 | self.from_chain = from_chain 101 | rpc = chain_mapping[from_chain.lower()].rpc 102 | super().__init__(private_key, rpc=rpc) 103 | self.scan = chain_mapping[from_chain.lower()].scan 104 | self.receiver_address = receiver_address 105 | if isinstance(amount, List): 106 | self.amount = round(random.uniform(amount[0], amount[1]), 6) 107 | elif isinstance(amount, float): 108 | self.amount = amount 109 | else: 110 | logger.error(f'amount must be List[float] or float. Got {type(amount)}') 111 | return 112 | 113 | if isinstance(keep_value, list): 114 | self.keep_value = random.uniform(keep_value[0], keep_value[1]) 115 | else: 116 | self.keep_value = keep_value 117 | self.withdraw_all = withdraw_all 118 | 119 | def __repr__(self) -> str: 120 | if self.withdraw_all: 121 | balance = self.get_wallet_balance('ETH', '...') 122 | amount = (int(balance - self.keep_value * 10 ** 18)) / 10 ** 18 123 | else: 124 | amount = self.amount 125 | return f'Withdrawing {amount} ETH from {self.account_address} to {self.receiver_address} | CHAIN: {self.from_chain}' 126 | 127 | @retry() 128 | async def deposit(self) -> None: 129 | balance = self.get_wallet_balance('ETH', '...') 130 | 131 | if balance == 0: 132 | logger.error(f'Your balance is 0 | {self.account.address}') 133 | return 134 | if self.withdraw_all is True: 135 | amount = balance - int(self.keep_value * 10 ** 18) 136 | else: 137 | amount = int(self.amount * 10 ** 18) 138 | 139 | tx = { 140 | 'chainId': self.web3.eth.chain_id, 141 | 'from': self.account_address, 142 | 'to': self.web3.to_checksum_address(self.receiver_address), 143 | 'value': amount, 144 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 145 | 'gasPrice': self.web3.eth.gas_price 146 | } 147 | gas_limit = self.web3.eth.estimate_gas(tx) 148 | tx.update({'gas': gas_limit}) 149 | 150 | tx_hash = self.sign_transaction(tx) 151 | 152 | self.logger.success( 153 | f'Successfully withdrawn {round((amount / 10 ** 18), 6)} from {self.account.address} to {self.receiver_address} TX: {self.scan}/{tx_hash}' 154 | ) 155 | self.logger.info('Sleeping 5 minutes before SubAccount => MainAccount transfer...') 156 | await sleep(300) 157 | logger.debug('Transferring from SubAccount to MainAccount') 158 | await transfer_from_subaccs_to_main() 159 | -------------------------------------------------------------------------------- /src/modules/okx_withdraw/utils/data.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from loguru import logger 4 | import ccxt 5 | 6 | 7 | async def get_withdrawal_fee(symbol_withdraw: str, chain_name: str, exchange: ccxt.okx) -> Optional[float]: 8 | currencies = exchange.fetch_currencies() 9 | for currency in currencies: 10 | if currency == symbol_withdraw: 11 | currency_info = currencies[currency] 12 | network_info = currency_info.get('networks', None) 13 | if network_info: 14 | for network in network_info: 15 | network_data = network_info[network] 16 | network_id = network_data['id'] 17 | if network_id == chain_name: 18 | withdrawal_fee = currency_info['networks'][network]['fee'] 19 | if withdrawal_fee == 0: 20 | return 0 21 | else: 22 | return withdrawal_fee 23 | 24 | logger.error(f"Can't get commission value, check symbolWithdraw and network values") 25 | return 26 | -------------------------------------------------------------------------------- /src/modules/okx_withdraw/utils/okx_sub_transfer.py: -------------------------------------------------------------------------------- 1 | from aiohttp import ClientSession 2 | from datetime import datetime 3 | from asyncio import sleep 4 | import base64 5 | import json 6 | import hmac 7 | 8 | from typing import ( 9 | Callable, 10 | Awaitable, 11 | Optional, 12 | Union, 13 | Dict, 14 | ) 15 | 16 | from loguru import logger 17 | 18 | from okx_data.okx_data import ( 19 | SECRET_KEY, 20 | PASSPHRASE, 21 | API_KEY, 22 | ) 23 | 24 | 25 | def signature(timestamp: str, method: str, url: str, body: Optional[str] 26 | ) -> Union[str, Callable[[str, str, str, Optional[str]], Awaitable[str]]]: 27 | try: 28 | if not body: 29 | body = "" 30 | message = timestamp + method.upper() + url + body 31 | mac = hmac.new( 32 | bytes(SECRET_KEY, encoding="utf-8"), 33 | bytes(message, encoding="utf-8"), 34 | digestmod="sha256", 35 | ) 36 | d = mac.digest() 37 | return base64.b64encode(d).decode("utf-8") 38 | except Exception as ex: 39 | logger.error(ex) 40 | return signature(timestamp, method, url, body) 41 | 42 | 43 | def generate_request_headers(url: str, method: str, body='' 44 | ) -> Dict[str, Union[str, Callable[[str, str, str, Optional[str]], str]]]: 45 | dt_now = datetime.utcnow() 46 | ms = str(dt_now.microsecond).zfill(6)[:3] 47 | timestamp = f"{dt_now:%Y-%m-%dT%H:%M:%S}.{ms}Z" 48 | headers = { 49 | "Content-Type": "application/json", 50 | "OK-ACCESS-KEY": API_KEY, 51 | "OK-ACCESS-SIGN": signature(timestamp, method, url, body), 52 | "OK-ACCESS-TIMESTAMP": timestamp, 53 | "OK-ACCESS-PASSPHRASE": PASSPHRASE, 54 | 'x-simulated-trading': '0' 55 | } 56 | return headers 57 | 58 | 59 | async def send_request(url: str, timeout: int, headers: dict, method: str, data='') -> Optional[str]: 60 | try: 61 | async with ClientSession(headers=headers) as session: 62 | async with session.get(url, timeout=timeout, data=data) if method == 'GET' \ 63 | else session.post(url, timeout=timeout, data=data) as response: 64 | if response.status == 200: 65 | await sleep(2) 66 | return json.loads(await response.text()) 67 | logger.error(f"Couldn't send request {url} : {await response.text()}") 68 | return False 69 | except Exception as ex: 70 | logger.error(ex) 71 | return False 72 | 73 | 74 | async def transfer_from_subaccs_to_main(token='ETH') -> None: 75 | try: 76 | headers = generate_request_headers(url='/api/v5/users/subaccount/list', 77 | method='GET') 78 | list_sub = await send_request(f"https://www.okx.com/api/v5/users/subaccount/list", 10, headers, 'GET') 79 | if not list_sub: 80 | logger.info(f"You don't have Sub Accounts!") 81 | return 82 | for sub_data in list_sub['data']: 83 | name = sub_data['subAcct'] 84 | 85 | headers = generate_request_headers( 86 | url=f"/api/v5/asset/subaccount/balances?subAcct={name}&ccy={token}", 87 | method='GET') 88 | sub_balance = await send_request( 89 | f"https://www.okx.com/api/v5/asset/subaccount/balances?subAcct={name}&ccy={token}", 10, headers, 90 | 'GET') 91 | if not sub_balance: 92 | await sleep(10) 93 | continue 94 | sub_balance = float(sub_balance['data'][0]['bal']) 95 | if sub_balance == 0: 96 | logger.info(f'Sub Account: {name} | Balance: 0') 97 | await sleep(10) 98 | continue 99 | 100 | body = {"ccy": f"{token}", "amt": str(sub_balance), "from": 6, "to": 6, "type": "2", 101 | "subAcct": name} 102 | 103 | headers = generate_request_headers(url=f"/api/v5/asset/transfer", 104 | body=str(body), method='POST') 105 | res = await send_request("https://www.okx.com/api/v5/asset/transfer", 10, headers, 'POST', str(body)) 106 | if len(res['data']) != 0: 107 | logger.success(f'Successfully transferred from {name} => MAIN: {sub_balance} {token}') 108 | await sleep(30) 109 | else: 110 | if 'Insufficient balance' in str(res): 111 | await sleep(2) 112 | continue 113 | logger.warning(f'Error - {res}') 114 | await sleep(1) 115 | 116 | except Exception as ex: 117 | logger.error(ex) 118 | await sleep(2) 119 | return 120 | -------------------------------------------------------------------------------- /src/modules/other/multi_approve/multi_approve.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | import random 3 | 4 | from src.utils.wrappers.decorators import retry 5 | from src.utils.data.contracts import contracts 6 | from src.utils.user.account import Account 7 | from src.utils.data.tokens import tokens 8 | 9 | from config import ( 10 | MIN_PAUSE, 11 | MAX_PAUSE, 12 | ) 13 | 14 | 15 | class MultiApprove(Account): 16 | def __init__(self, private_key: str) -> None: 17 | self.private_key = private_key 18 | super().__init__(private_key=private_key) 19 | 20 | def __str__(self) -> str: 21 | return f'MultiApprove | [{self.account_address}]' 22 | 23 | @retry() 24 | async def approve(self) -> None: 25 | contract_list = [ 26 | contracts['skydrome'], 27 | contracts['punkswap'], 28 | contracts['spacefi'], 29 | contracts['zebra'], 30 | contracts['syncswap'] 31 | ] 32 | token_list = list(tokens) 33 | random.shuffle(token_list) 34 | 35 | for token in token_list: 36 | contract_address = random.choice(contract_list) 37 | if token in ["ETH", "WETH"]: 38 | continue 39 | self.logger.info(f'Approving {token}...') 40 | tx_hash = await self.approve_token(0, self.private_key, tokens[token], 41 | contract_address, self.account_address, self.web3) 42 | self.logger.info(f'Approve TX: https://scrollscan.com/tx/{tx_hash}') 43 | random_sleep = random.randint(MIN_PAUSE, MAX_PAUSE) 44 | self.logger.info(f'Sleeping {random_sleep} seconds...') 45 | await sleep(random_sleep) 46 | -------------------------------------------------------------------------------- /src/modules/other/rubyscore/rubyscore.py: -------------------------------------------------------------------------------- 1 | from src.utils.wrappers.decorators import retry 2 | from src.utils.user.account import Account 3 | 4 | from src.utils.data.contracts import ( 5 | contracts, 6 | abi_names, 7 | ) 8 | 9 | 10 | class RubyScore(Account): 11 | def __init__(self, private_key: str) -> None: 12 | super().__init__(private_key) 13 | 14 | def __str__(self) -> str: 15 | return f'[{self.account_address}] Voting on RubyScore' 16 | 17 | @retry() 18 | async def vote(self) -> None: 19 | contract = self.load_contract(contracts['rubyscore'], self.web3, abi_names['rubyscore']) 20 | tx = contract.functions.vote().build_transaction({ 21 | 'value': 0, 22 | 'nonce': self.web3.eth.get_transaction_count(self.account_address), 23 | 'from': self.account_address, 24 | "gasPrice": self.web3.eth.gas_price 25 | }) 26 | 27 | tx_hash = self.sign_transaction(tx) 28 | self.wait_until_tx_finished(tx_hash) 29 | -------------------------------------------------------------------------------- /src/modules/swaps/punk_swap/punk_swap.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | # from src.utils.base_liquidity_remove import BaseLiquidityRemove 8 | from src.utils.base_liquidity_remove import BaseLiquidityRemove 9 | from src.utils.base_liquidity import BaseLiquidity 10 | from src.utils.base_swap import BaseSwap 11 | from src.utils.data.types import Types 12 | 13 | from src.utils.data.contracts import ( 14 | contracts, 15 | abi_names, 16 | ) 17 | 18 | from src.modules.swaps.punk_swap.utils.transaction_data import ( 19 | create_liquidity_remove_tx, 20 | create_liquidity_tx, 21 | get_amount_out, 22 | create_swap_tx, 23 | ) 24 | 25 | 26 | class PunkSwap(BaseSwap): 27 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], 28 | amount: Union[float, List[float]], use_percentage: bool, swap_percentage: Union[float, List[float]], 29 | swap_all_balance: bool) -> None: 30 | contract_address = contracts['punkswap'] 31 | abi_name = abi_names['punkswap'] 32 | dex_name = self.__class__.__name__ 33 | 34 | super().__init__(private_key, from_token, to_token, amount, use_percentage, swap_percentage, swap_all_balance, 35 | contract_address, abi_name, dex_name) 36 | 37 | def __repr__(self) -> str: 38 | return f'{self.__class__.__name__}: {self.account_address} | {self.from_token} => {self.to_token}' 39 | 40 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 41 | to_token_address: Address) -> Types.AmountOut: 42 | return get_amount_out(contract, amount, from_token_address, to_token_address) 43 | 44 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 45 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 46 | web3: Web3) -> Types.SwapTransaction: 47 | return create_swap_tx(from_token, to_token, contract, amount_out, from_token_address, to_token_address, 48 | account_address, amount, web3) 49 | 50 | 51 | class PunkSwapLiquidity(BaseLiquidity): 52 | def __init__(self, private_key: str, token: str, token2: str, amount: Union[float, List[float]], 53 | use_percentage: bool, liquidity_percentage: Union[float, List[float]]) -> None: 54 | contract_address = contracts['punkswap'] 55 | abi_name = abi_names['punkswap'] 56 | dex_name = self.__class__.__name__ 57 | super().__init__(private_key, token, token2, amount, use_percentage, liquidity_percentage, 58 | contract_address, abi_name, dex_name) 59 | 60 | def __repr__(self) -> str: 61 | return f'{self.__class__.__name__}: {self.account_address}' 62 | 63 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 64 | to_token_address: Address) -> Types.AmountOut: 65 | return get_amount_out(contract, amount, from_token_address, to_token_address) 66 | 67 | def create_liquidity_tx(self, from_token: str, contract: Contract, amount_out: int, from_token_address: str, 68 | to_token_address: str, account_address: Address, amount: int, web3: Web3 69 | ) -> Types.LiquidityTransaction: 70 | return create_liquidity_tx(from_token, contract, amount_out, to_token_address, 71 | account_address, amount, web3) 72 | 73 | async def get_swap_instance(self, private_key: str, token: str, token2: str, amount: int) -> PunkSwap: 74 | return PunkSwap(private_key, token, token2, amount, False, 0, False) 75 | 76 | 77 | class PunkSwapLiquidityRemove(BaseLiquidityRemove): 78 | def __init__(self, private_key: str, from_token_pair: Union[str, List[str]], remove_all: bool, 79 | removing_percentage: float, token: str = None) -> None: 80 | contract_address = contracts['punkswap'] 81 | abi_name = abi_names['punkswap'] 82 | pool_name = 'PunkSwap' 83 | super().__init__(private_key, from_token_pair, remove_all, removing_percentage, contract_address, abi_name, 84 | pool_name, token=token) 85 | 86 | def __repr__(self) -> str: 87 | return f'{self.__class__.__name__}: {self.account_address}' 88 | 89 | def create_liquidity_remove_tx(self, web3: Web3, contract: Contract, from_token_pair_address: str, 90 | amount: int, account_address: Address, token=None 91 | ) -> Types.LiquidityRemoveTransaction: 92 | return create_liquidity_remove_tx(web3, contract, from_token_pair_address, amount, account_address, token) 93 | -------------------------------------------------------------------------------- /src/modules/swaps/punk_swap/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from web3.contract import Contract 4 | from web3.types import TxParams 5 | from eth_typing import Address 6 | 7 | from config import SLIPPAGE 8 | from web3 import Web3 9 | 10 | from src.utils.data.tokens import tokens 11 | 12 | 13 | def get_amount_out(contract: Contract, amount: int, from_token_address: Address, 14 | to_token_address: Address) -> int: 15 | amount_out = contract.functions.getAmountsOut( 16 | amount, 17 | [from_token_address, to_token_address] 18 | ).call() 19 | return amount_out[1] 20 | 21 | 22 | def create_swap_tx(from_token: str, to_token: str, contract: Contract, amount_out: int, from_token_address: str, 23 | to_token_address: str, account_address: Address, amount: int, web3: Web3) -> TxParams: 24 | if from_token.lower() == 'eth': 25 | tx = contract.functions.swapExactETHForTokens( 26 | int(amount_out * (1 - SLIPPAGE)), 27 | [Web3.to_checksum_address(from_token_address), Web3.to_checksum_address(tokens['PUNK']), 28 | Web3.to_checksum_address(to_token_address)] if not to_token.upper() == 'PUNK' else 29 | [Web3.to_checksum_address(from_token_address), Web3.to_checksum_address(to_token_address)], 30 | account_address, 31 | int(time() + 1200) 32 | ).build_transaction({ 33 | 'value': amount if from_token.lower() == 'eth' else 0, 34 | 'nonce': web3.eth.get_transaction_count(account_address), 35 | 'from': account_address, 36 | 'gasPrice': web3.eth.gas_price, 37 | }) 38 | elif to_token.lower() == 'eth': 39 | tx = contract.functions.swapExactTokensForETH( 40 | amount, 41 | int(amount_out * (1 - SLIPPAGE)), 42 | [Web3.to_checksum_address(from_token_address), Web3.to_checksum_address(tokens['PUNK']), 43 | Web3.to_checksum_address(to_token_address)] if not from_token.upper() == 'PUNK' else 44 | [Web3.to_checksum_address(from_token_address), Web3.to_checksum_address(to_token_address)], 45 | account_address, 46 | int(time() + 1200) 47 | ).build_transaction({ 48 | 'value': 0, 49 | 'nonce': web3.eth.get_transaction_count(account_address), 50 | 'from': account_address, 51 | 'gasPrice': web3.eth.gas_price, 52 | }) 53 | else: 54 | tx = contract.functions.swapExactTokensForTokens( 55 | amount, 56 | int(amount_out * (1 - SLIPPAGE)), 57 | [from_token_address, to_token_address], 58 | account_address, 59 | int(time() + 1200) 60 | ).build_transaction({ 61 | 'value': amount if from_token.lower() == 'eth' else 0, 62 | 'nonce': web3.eth.get_transaction_count(account_address), 63 | 'from': account_address, 64 | 'gasPrice': web3.eth.gas_price, 65 | }) 66 | 67 | return tx 68 | 69 | 70 | def create_liquidity_tx(from_token: str, contract: Contract, amount_out: int, to_token_address: str, 71 | account_address: Address, amount: int, web3: Web3) -> TxParams: 72 | if from_token.lower() == 'eth': 73 | tx = contract.functions.addLiquidityETH( 74 | to_token_address, 75 | amount_out, 76 | int(amount_out * (1 - SLIPPAGE)), 77 | int(amount * (1 - SLIPPAGE)), 78 | account_address, 79 | int(time() + 1200) 80 | ).build_transaction({ 81 | 'value': amount if from_token.lower() == 'eth' else 0, 82 | 'nonce': web3.eth.get_transaction_count(account_address), 83 | 'from': account_address, 84 | "gasPrice": web3.eth.gas_price 85 | }) 86 | else: 87 | from_token_address = tokens[from_token] 88 | tx = contract.functions.addLiquidity( 89 | from_token_address, 90 | to_token_address, 91 | amount, 92 | amount_out, 93 | int(amount * (1 - SLIPPAGE)), 94 | int(amount_out * (1 - SLIPPAGE)), 95 | account_address, 96 | int(time() + 1200) 97 | ).build_transaction({ 98 | 'value': amount if from_token.lower() == 'eth' else 0, 99 | 'nonce': web3.eth.get_transaction_count(account_address), 100 | 'from': account_address, 101 | 'gasPrice': web3.eth.gas_price, 102 | }) 103 | 104 | return tx 105 | 106 | 107 | def create_liquidity_remove_tx(web3: Web3, contract: Contract, from_token_pair_address: str, amount: int, 108 | account_address: Address, token: str) -> TxParams: 109 | if token.lower() == 'eth': 110 | tx = contract.functions.removeLiquidityETH( 111 | from_token_pair_address, 112 | amount, 113 | 0, 114 | 0, 115 | account_address, 116 | int(time() + 1200) 117 | ).build_transaction({ 118 | 'value': 0, 119 | 'nonce': web3.eth.get_transaction_count(account_address), 120 | 'from': account_address, 121 | 'gasPrice': web3.eth.gas_price, 122 | }) 123 | else: 124 | from_token_address = tokens[token] 125 | tx = contract.functions.removeLiquidity( 126 | from_token_address, 127 | from_token_pair_address, 128 | amount, 129 | 0, 130 | 0, 131 | account_address, 132 | int(time() + 1200) 133 | ).build_transaction({ 134 | 'value': 0, 135 | 'nonce': web3.eth.get_transaction_count(account_address), 136 | 'from': account_address, 137 | 'gasPrice': web3.eth.gas_price, 138 | }) 139 | 140 | return tx 141 | -------------------------------------------------------------------------------- /src/modules/swaps/skydrome/skydrome_swap.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | from src.utils.base_liquidity_remove import BaseLiquidityRemove 8 | from src.utils.base_liquidity import BaseLiquidity 9 | from src.utils.base_swap import BaseSwap 10 | from src.utils.data.types import Types 11 | 12 | from src.utils.data.contracts import ( 13 | contracts, 14 | abi_names, 15 | ) 16 | 17 | from src.modules.swaps.skydrome.utils.transaction_data import ( 18 | create_liquidity_remove_tx, 19 | create_liquidity_tx, 20 | get_amount_out, 21 | create_swap_tx, 22 | ) 23 | 24 | 25 | class SkyDromeSwap(BaseSwap): 26 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], 27 | amount: Union[float, List[float]], use_percentage: bool, swap_percentage: Union[float, List[float]], 28 | swap_all_balance: bool) -> None: 29 | contract_address = contracts['skydrome'] 30 | abi_name = abi_names['skydrome'] 31 | dex_name = self.__class__.__name__ 32 | 33 | super().__init__(private_key, from_token, to_token, amount, use_percentage, swap_percentage, swap_all_balance, 34 | contract_address, abi_name, dex_name) 35 | 36 | def __repr__(self) -> str: 37 | return f'{self.__class__.__name__}: {self.account_address} | {self.from_token} => {self.to_token}' 38 | 39 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 40 | to_token_address: Address) -> Types.AmountOut: 41 | return get_amount_out(contract, amount, from_token_address, to_token_address) 42 | 43 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 44 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 45 | web3: Web3) -> Types.SwapTransaction: 46 | return create_swap_tx(from_token, to_token, contract, from_token_address, to_token_address, 47 | account_address, amount, web3) 48 | 49 | 50 | class SkyDromeLiquidity(BaseLiquidity): 51 | def __init__(self, private_key: str, token: str, token2: str, amount: Union[float, List[float]], 52 | use_percentage: bool, liquidity_percentage: Union[float, List[float]]) -> None: 53 | contract_address = contracts['skydrome'] 54 | abi_name = abi_names['skydrome'] 55 | dex_name = self.__class__.__name__ 56 | super().__init__(private_key, token, token2, amount, use_percentage, liquidity_percentage, 57 | contract_address, abi_name, dex_name) 58 | 59 | def __repr__(self) -> str: 60 | return f'{self.__class__.__name__}: {self.account_address}' 61 | 62 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 63 | to_token_address: Address) -> Types.AmountOut: 64 | return get_amount_out(contract, amount, from_token_address, to_token_address) 65 | 66 | def create_liquidity_tx(self, from_token: str, contract: Contract, amount_out: int, from_token_address: str, 67 | to_token_address: str, account_address: Address, amount: int, web3: Web3 68 | ) -> Types.LiquidityTransaction: 69 | return create_liquidity_tx(from_token, contract, amount_out, to_token_address, 70 | account_address, amount, web3) 71 | 72 | async def get_swap_instance(self, private_key: str, token: str, token2: str, amount: int) -> SkyDromeSwap: 73 | return SkyDromeSwap(private_key, token, token2, amount, False, 0, False) 74 | 75 | 76 | class SkyDromeLiquidityRemove(BaseLiquidityRemove): 77 | def __init__(self, private_key: str, from_token_pair: Union[str, List[str]], remove_all: bool, 78 | removing_percentage: float, token: str = None) -> None: 79 | contract_address = contracts['skydrome'] 80 | abi_name = abi_names['skydrome'] 81 | pool_name = 'SkyDrome' 82 | super().__init__(private_key, from_token_pair, remove_all, removing_percentage, contract_address, abi_name, 83 | pool_name, token=token) 84 | 85 | def __repr__(self) -> str: 86 | return f'{self.__class__.__name__}: {self.account_address}' 87 | 88 | def create_liquidity_remove_tx(self, web3: Web3, contract: Contract, from_token_pair_address: str, 89 | amount: int, account_address: Address, token: str = None 90 | ) -> Types.LiquidityRemoveTransaction: 91 | return create_liquidity_remove_tx(web3, contract, from_token_pair_address, amount, account_address, token) 92 | -------------------------------------------------------------------------------- /src/modules/swaps/skydrome/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3.types import TxParams 6 | from web3 import Web3 7 | 8 | from src.utils.data.tokens import tokens 9 | from config import SLIPPAGE 10 | 11 | 12 | def get_amounts_out(contract: Contract, amount: int, from_token_address: Address, 13 | to_token_address: Address) -> tuple[int, bool]: 14 | amount_out, swap_type = contract.functions.getAmountOut( 15 | amount, 16 | from_token_address, 17 | to_token_address 18 | ).call() 19 | return amount_out, swap_type 20 | 21 | 22 | def get_amount_out(contract: Contract, amount: int, from_token_address: Address, 23 | to_token_address: Address) -> int: 24 | swap_type = get_amounts_out(contract, amount, from_token_address, to_token_address)[1] 25 | amount_out = contract.functions.getAmountsOut( 26 | amount, 27 | [[from_token_address, to_token_address, swap_type]] 28 | ).call() 29 | return amount_out[1] 30 | 31 | 32 | def create_swap_tx(from_token: str, to_token: str, contract: Contract, from_token_address: str, 33 | to_token_address: str, account_address: Address, amount: int, web3: Web3) -> TxParams: 34 | amount_out, swap_type = get_amounts_out(contract, amount, web3.to_checksum_address(from_token_address), 35 | web3.to_checksum_address(to_token_address)) 36 | if from_token.lower() == 'eth': 37 | tx = contract.functions.swapExactETHForTokens( 38 | int(amount_out * (1 - SLIPPAGE)), 39 | [ 40 | [ 41 | web3.to_checksum_address(from_token_address), 42 | web3.to_checksum_address(to_token_address), 43 | swap_type 44 | ] 45 | ], 46 | account_address, 47 | int(time() + 1200) 48 | ).build_transaction({ 49 | 'value': amount if from_token.lower() == 'eth' else 0, 50 | 'nonce': web3.eth.get_transaction_count(account_address), 51 | 'from': account_address, 52 | 'gasPrice': web3.eth.gas_price, 53 | }) 54 | elif to_token.lower() == 'eth': 55 | tx = contract.functions.swapExactTokensForETH( 56 | amount, 57 | int(amount_out * (1 - SLIPPAGE)), 58 | [ 59 | [ 60 | web3.to_checksum_address(from_token_address), 61 | web3.to_checksum_address(to_token_address), 62 | swap_type 63 | ] 64 | ], 65 | account_address, 66 | int(time() + 1200) 67 | ).build_transaction({ 68 | 'value': amount if from_token.lower() == 'eth' else 0, 69 | 'nonce': web3.eth.get_transaction_count(account_address), 70 | 'from': account_address, 71 | 'gasPrice': web3.eth.gas_price, 72 | }) 73 | else: 74 | tx = contract.functions.swapExactTokensForTokens( 75 | amount, 76 | int(amount_out * (1 - SLIPPAGE)), 77 | [ 78 | [ 79 | web3.to_checksum_address(from_token_address), 80 | web3.to_checksum_address(to_token_address), 81 | swap_type 82 | ] 83 | ], 84 | account_address, 85 | int(time() + 1200) 86 | ).build_transaction({ 87 | 'value': amount if from_token.lower() == 'eth' else 0, 88 | 'nonce': web3.eth.get_transaction_count(account_address), 89 | 'from': account_address, 90 | 'gasPrice': web3.eth.gas_price, 91 | }) 92 | 93 | return tx 94 | 95 | 96 | def create_liquidity_tx(from_token: str, contract: Contract, amount_out: int, to_token_address: str, 97 | account_address: Address, amount: int, web3: Web3) -> TxParams: 98 | from_token_address = tokens[from_token] 99 | return contract.functions.addLiquidity( 100 | from_token_address, 101 | to_token_address, 102 | True, 103 | amount, 104 | amount_out, 105 | int(amount * (1 - SLIPPAGE)), 106 | int(amount_out * (1 - SLIPPAGE)), 107 | account_address, 108 | int(time() + 1200) 109 | ).build_transaction({ 110 | 'value': amount if from_token.lower() == 'eth' else 0, 111 | 'nonce': web3.eth.get_transaction_count(account_address), 112 | 'from': account_address, 113 | 'gasPrice': web3.eth.gas_price, 114 | }) 115 | 116 | 117 | def create_liquidity_remove_tx(web3: Web3, contract: Contract, from_token_pair_address: str, amount: int, 118 | account_address: Address, token: str) -> TxParams: 119 | from_token_address = tokens[token] 120 | tx = contract.functions.removeLiquidity( 121 | from_token_address, 122 | from_token_pair_address, 123 | True, 124 | amount, 125 | 1, 126 | 1, 127 | account_address, 128 | int(time() + 1200) 129 | ).build_transaction({ 130 | 'value': 0, 131 | 'nonce': web3.eth.get_transaction_count(account_address), 132 | 'from': account_address, 133 | 'gasPrice': web3.eth.gas_price, 134 | }) 135 | return tx 136 | -------------------------------------------------------------------------------- /src/modules/swaps/spacefi/spacefi_swap.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | from src.utils.base_liquidity_remove import BaseLiquidityRemove 8 | from src.utils.base_liquidity import BaseLiquidity 9 | from src.utils.base_swap import BaseSwap 10 | from src.utils.data.types import Types 11 | 12 | from src.utils.data.contracts import ( 13 | contracts, 14 | abi_names, 15 | ) 16 | 17 | from src.modules.swaps.spacefi.utils.transaction_data import ( 18 | create_liquidity_remove_tx, 19 | create_liquidity_tx, 20 | get_amount_out, 21 | create_swap_tx, 22 | ) 23 | 24 | 25 | class SpaceFiSwap(BaseSwap): 26 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], 27 | amount: Union[float, List[float]], use_percentage: bool, swap_percentage: Union[float, List[float]], 28 | swap_all_balance: bool) -> None: 29 | contract_address = contracts['spacefi'] 30 | abi_name = abi_names['spacefi'] 31 | dex_name = self.__class__.__name__ 32 | 33 | super().__init__(private_key, from_token, to_token, amount, use_percentage, swap_percentage, swap_all_balance, 34 | contract_address, abi_name, dex_name) 35 | 36 | def __repr__(self) -> str: 37 | return f'{self.__class__.__name__}: {self.account_address} | {self.from_token} => {self.to_token}' 38 | 39 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 40 | to_token_address: Address) -> Types.AmountOut: 41 | return get_amount_out(contract, amount, from_token_address, to_token_address) 42 | 43 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 44 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 45 | web3: Web3) -> Types.SwapTransaction: 46 | return create_swap_tx(from_token, contract, amount_out, from_token_address, to_token_address, 47 | account_address, amount, web3) 48 | 49 | 50 | class SpaceFiLiquidity(BaseLiquidity): 51 | def __init__(self, private_key: str, token: str, token2: str, amount: Union[float, List[float]], 52 | use_percentage: bool, liquidity_percentage: Union[float, List[float]]) -> None: 53 | contract_address = contracts['spacefi'] 54 | abi_name = abi_names['spacefi'] 55 | dex_name = self.__class__.__name__ 56 | super().__init__(private_key, token, token2, amount, use_percentage, liquidity_percentage, 57 | contract_address, abi_name, dex_name) 58 | 59 | def __repr__(self) -> str: 60 | return f'{self.__class__.__name__}: {self.account_address}' 61 | 62 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 63 | to_token_address: Address) -> Types.AmountOut: 64 | return get_amount_out(contract, amount, from_token_address, to_token_address) 65 | 66 | def create_liquidity_tx(self, from_token: str, contract: Contract, amount_out: int, from_token_address: str, 67 | to_token_address: str, account_address: Address, amount: int, web3: Web3 68 | ) -> Types.LiquidityTransaction: 69 | return create_liquidity_tx(from_token, contract, amount_out, to_token_address, 70 | account_address, amount, web3) 71 | 72 | async def get_swap_instance(self, private_key: str, token: str, token2: str, amount: int) -> SpaceFiSwap: 73 | return SpaceFiSwap(private_key, token, token2, amount, False, 0, False) 74 | 75 | 76 | class SpaceFiLiquidityRemove(BaseLiquidityRemove): 77 | def __init__(self, private_key: str, from_token_pair: Union[str, List[str]], remove_all: bool, 78 | removing_percentage: float, token: str) -> None: 79 | contract_address = contracts['spacefi'] 80 | abi_name = abi_names['spacefi'] 81 | pool_name = 'SpaceFi' 82 | super().__init__(private_key, from_token_pair, remove_all, removing_percentage, contract_address, abi_name, 83 | pool_name, token=token) 84 | 85 | def __repr__(self) -> str: 86 | return f'{self.__class__.__name__}: {self.account_address}' 87 | 88 | def create_liquidity_remove_tx(self, web3: Web3, contract: Contract, from_token_pair_address: str, 89 | amount: int, account_address: Address, token=None 90 | ) -> Types.LiquidityRemoveTransaction: 91 | return create_liquidity_remove_tx(web3, contract, from_token_pair_address, amount, account_address, token) 92 | -------------------------------------------------------------------------------- /src/modules/swaps/spacefi/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from web3.contract import Contract 4 | from web3.types import TxParams 5 | from eth_typing import Address 6 | 7 | from config import SLIPPAGE 8 | from web3 import Web3 9 | 10 | from src.utils.data.tokens import tokens 11 | 12 | 13 | def get_amount_out(contract: Contract, amount: int, from_token_address: Address, 14 | to_token_address: Address) -> int: 15 | amount_out = contract.functions.getAmountsOut( 16 | amount, 17 | [from_token_address, to_token_address] 18 | ).call() 19 | return amount_out[1] 20 | 21 | 22 | def create_swap_tx(from_token: str, contract: Contract, amount_out: int, from_token_address: str, 23 | to_token_address: str, account_address: Address, amount: int, web3: Web3) -> TxParams: 24 | if from_token.lower() == 'eth': 25 | tx = contract.functions.swapExactETHForTokens( 26 | int(amount_out * (1 - SLIPPAGE)), 27 | [web3.to_checksum_address(from_token_address), web3.to_checksum_address(to_token_address)], 28 | account_address, 29 | int(time() + 1200) 30 | ).build_transaction({ 31 | 'value': amount if from_token.lower() == 'eth' else 0, 32 | 'nonce': web3.eth.get_transaction_count(account_address), 33 | 'from': account_address, 34 | 'gasPrice': web3.eth.gas_price, 35 | }) 36 | else: 37 | tx = contract.functions.swapExactTokensForTokens( 38 | amount, 39 | int(amount_out * (1 - SLIPPAGE)), 40 | [web3.to_checksum_address(from_token_address), web3.to_checksum_address(to_token_address)], 41 | account_address, 42 | int(time() + 1200) 43 | ).build_transaction({ 44 | 'value': amount if from_token.lower() == 'eth' else 0, 45 | 'nonce': web3.eth.get_transaction_count(account_address), 46 | 'from': account_address, 47 | 'gasPrice': web3.eth.gas_price, 48 | }) 49 | 50 | return tx 51 | 52 | 53 | def create_liquidity_tx(from_token: str, contract: Contract, amount_out: int, to_token_address: str, 54 | account_address: Address, amount: int, web3: Web3) -> TxParams: 55 | if from_token.lower() == 'eth': 56 | tx = contract.functions.addLiquidityETH( 57 | web3.to_checksum_address(to_token_address), 58 | amount_out, 59 | int(amount_out * (1 - SLIPPAGE)), 60 | int(amount * (1 - SLIPPAGE)), 61 | account_address, 62 | int(time() + 1200) 63 | ).build_transaction({ 64 | 'value': amount if from_token.lower() == 'eth' else 0, 65 | 'nonce': web3.eth.get_transaction_count(account_address), 66 | 'from': account_address, 67 | 'gasPrice': web3.eth.gas_price, 68 | }) 69 | else: 70 | from_token_address = tokens[from_token] 71 | tx = contract.functions.addLiquidity( 72 | web3.to_checksum_address(from_token_address), 73 | to_token_address, 74 | amount, 75 | amount_out, 76 | int(amount * (1 - SLIPPAGE)), 77 | int(amount_out * (1 - SLIPPAGE)), 78 | account_address, 79 | int(time() + 1200) 80 | ).build_transaction({ 81 | 'value': amount if from_token.lower() == 'eth' else 0, 82 | 'nonce': web3.eth.get_transaction_count(account_address), 83 | 'from': account_address, 84 | 'gasPrice': web3.eth.gas_price, 85 | }) 86 | 87 | return tx 88 | 89 | 90 | def create_liquidity_remove_tx(web3: Web3, contract: Contract, from_token_pair_address: str, amount: int, 91 | account_address: Address, token: str) -> TxParams: 92 | if token.lower() == 'eth': 93 | tx = contract.functions.removeLiquidityETH( 94 | web3.to_checksum_address(from_token_pair_address), 95 | amount, 96 | 0, 97 | 0, 98 | account_address, 99 | int(time() + 1200) 100 | ).build_transaction({ 101 | 'value': 0, 102 | 'nonce': web3.eth.get_transaction_count(account_address), 103 | 'from': account_address, 104 | 'gasPrice': web3.eth.gas_price, 105 | }) 106 | else: 107 | from_token_address = tokens[token] 108 | tx = contract.functions.removeLiquidity( 109 | web3.to_checksum_address(from_token_address), 110 | web3.to_checksum_address(from_token_pair_address), 111 | amount, 112 | 0, 113 | 0, 114 | account_address, 115 | int(time() + 1200) 116 | ).build_transaction({ 117 | 'value': 0, 118 | 'nonce': web3.eth.get_transaction_count(account_address), 119 | 'from': account_address, 120 | 'gasPrice': web3.eth.gas_price, 121 | }) 122 | 123 | return tx 124 | -------------------------------------------------------------------------------- /src/modules/swaps/sync_swap/sync_swap.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | from src.utils.base_liquidity_remove import BaseLiquidityRemove 8 | from src.utils.base_liquidity import BaseLiquidity 9 | from src.utils.base_swap import BaseSwap 10 | from src.utils.data.types import Types 11 | 12 | from src.utils.data.contracts import ( 13 | contracts, 14 | abi_names, 15 | ) 16 | 17 | from src.modules.swaps.sync_swap.utils.transaction_data import ( 18 | create_liquidity_remove_tx, 19 | create_liquidity_tx, 20 | get_amount_out, 21 | create_swap_tx, 22 | ) 23 | 24 | 25 | class SyncSwapSwap(BaseSwap): 26 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], 27 | amount: Union[float, List[float]], use_percentage: bool, swap_percentage: Union[float, List[float]], 28 | swap_all_balance: bool) -> None: 29 | contract_address = contracts['syncswap'] 30 | abi_name = abi_names['syncswap'] 31 | dex_name = self.__class__.__name__ 32 | 33 | super().__init__(private_key, from_token, to_token, amount, use_percentage, swap_percentage, swap_all_balance, 34 | contract_address, abi_name, dex_name) 35 | 36 | def __repr__(self) -> str: 37 | return f'{self.__class__.__name__}: {self.account_address} | {self.from_token} => {self.to_token}' 38 | 39 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 40 | to_token_address: Address) -> Types.AmountOut: 41 | return get_amount_out(amount, from_token_address, to_token_address, self.web3, self.account_address) 42 | 43 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 44 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 45 | web3: Web3) -> Types.SwapTransaction: 46 | return create_swap_tx(from_token, contract, amount_out, from_token_address, to_token_address, 47 | account_address, amount, web3) 48 | 49 | 50 | class SyncSwapLiquidity(BaseLiquidity): 51 | def __init__(self, private_key: str, token: str, token2: str, amount: Union[float, List[float]], 52 | use_percentage: bool, liquidity_percentage: Union[float, List[float]]) -> None: 53 | contract_address = contracts['syncswap'] 54 | abi_name = abi_names['syncswap'] 55 | dex_name = self.__class__.__name__ 56 | super().__init__(private_key, token, token2, amount, use_percentage, liquidity_percentage, 57 | contract_address, abi_name, dex_name) 58 | 59 | def __repr__(self) -> str: 60 | return f'{self.__class__.__name__}: {self.account_address}' 61 | 62 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 63 | to_token_address: Address) -> Types.AmountOut: 64 | return get_amount_out(contract, amount, from_token_address, to_token_address, self.account_address) 65 | 66 | def create_liquidity_tx(self, from_token: str, contract: Contract, amount_out: int, from_token_address: str, 67 | to_token_address: str, account_address: Address, amount: int, web3: Web3 68 | ) -> Types.LiquidityTransaction: 69 | return create_liquidity_tx(from_token, contract, to_token_address, account_address, amount, 70 | web3, from_token_address) 71 | 72 | async def get_swap_instance(self, private_key: str, token: str, token2: str, amount: int) -> SyncSwapSwap: 73 | return SyncSwapSwap(private_key, token, token2, amount, False, 0, False) 74 | 75 | 76 | class SyncSwapLiquidityRemove(BaseLiquidityRemove): 77 | def __init__(self, private_key: str, from_token_pair: Union[str, List[str]], remove_all: bool, 78 | removing_percentage: float, token: str = None) -> None: 79 | contract_address = contracts['syncswap'] 80 | abi_name = abi_names['syncswap'] 81 | pool_name = 'SyncSwap' 82 | 83 | super().__init__(private_key, from_token_pair, remove_all, removing_percentage, contract_address, abi_name, 84 | pool_name, token=token) 85 | 86 | def __repr__(self) -> str: 87 | return f'{self.__class__.__name__}: {self.account_address}' 88 | 89 | def create_liquidity_remove_tx(self, web3: Web3, contract: Contract, from_token_pair_address: str, 90 | amount: int, account_address: Address, token: str = None 91 | ) -> Types.LiquidityRemoveTransaction: 92 | return create_liquidity_remove_tx(web3, contract, from_token_pair_address, amount, account_address, token) 93 | -------------------------------------------------------------------------------- /src/modules/swaps/sync_swap/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from web3.middleware import geth_poa_middleware 2 | from web3.contract import Contract 3 | from web3.types import TxParams 4 | from eth_typing import Address 5 | from eth_abi import encode 6 | from loguru import logger 7 | from web3 import Web3 8 | 9 | from src.utils.data.tokens import tokens 10 | from src.utils.user.utils import Utils 11 | from config import SLIPPAGE 12 | 13 | 14 | def get_amount_out(amount: int, from_token_address: Address, to_token_address: Address, web3: Web3, 15 | account_address: Address) -> int: 16 | 17 | pool_address = get_pool(web3, from_token_address, to_token_address) 18 | utils = Utils() 19 | pool_contract = utils.load_contract(pool_address, web3, 'classic_pool_syncswap_data') 20 | amount_out = pool_contract.functions.getAmountOut( 21 | Web3.to_checksum_address(from_token_address), 22 | amount, 23 | account_address 24 | ).call() 25 | 26 | return amount_out 27 | 28 | 29 | def get_pool(web3: Web3, from_token_address: str, to_token_address: str) -> str: 30 | if from_token_address == '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4' and \ 31 | to_token_address == '0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df' \ 32 | or from_token_address == '0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df' and \ 33 | to_token_address == '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4': 34 | factory_address = '0x5b9f21d407F35b10CbfDDca17D5D84b129356ea3' 35 | else: 36 | factory_address = '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d' 37 | classic_pool_factory = web3.eth.contract( 38 | address=Web3.to_checksum_address(factory_address), 39 | abi=Utils.load_abi('classic_pool_factory_address')) 40 | pool_address = classic_pool_factory.functions.getPool(Web3.to_checksum_address(from_token_address), 41 | Web3.to_checksum_address(to_token_address)).call() 42 | 43 | return pool_address 44 | 45 | 46 | def create_swap_tx(from_token: str, contract: Contract, amount_out: int, from_token_address: str, 47 | to_token_address: str, account_address: Address, amount: int, web3: Web3) -> TxParams: 48 | pool_address = get_pool(web3, from_token_address, to_token_address) 49 | if pool_address == "0x0000000000000000000000000000000000000000": 50 | logger.error('Pool does not exist') 51 | return 52 | 53 | web3.middleware_onion.inject(geth_poa_middleware, layer=0) 54 | swap_data = encode( 55 | ["address", "address", "uint8"], 56 | [Web3.to_checksum_address(from_token_address), account_address, 1] 57 | ) 58 | native_eth_address = "0x0000000000000000000000000000000000000000" 59 | steps = [{ 60 | "pool": pool_address, 61 | "data": swap_data, 62 | "callback": native_eth_address, 63 | "callbackData": '0x' 64 | }] 65 | paths = [{ 66 | "steps": steps, 67 | "tokenIn": Web3.to_checksum_address( 68 | from_token_address) if from_token.lower() != 'eth' else Web3.to_checksum_address( 69 | native_eth_address), 70 | "amountIn": amount, 71 | }] 72 | 73 | tx = contract.functions.swap( 74 | paths, 75 | int(amount_out * (1 - SLIPPAGE)), 76 | int(web3.eth.get_block('latest').timestamp) + 1200 77 | ).build_transaction({ 78 | 'from': account_address, 79 | 'value': amount if from_token.lower() == 'eth' else 0, 80 | 'nonce': web3.eth.get_transaction_count(account_address), 81 | 'gasPrice': web3.eth.gas_price, 82 | }) 83 | return tx 84 | 85 | 86 | def create_liquidity_tx(from_token: str, contract: Contract, to_token_address: str, 87 | account_address: Address, amount: int, web3: Web3, from_token_address) -> TxParams: 88 | pool_address = get_pool(web3, from_token_address, to_token_address) 89 | if pool_address == "0x0000000000000000000000000000000000000000": 90 | logger.error('Pool does not exist') 91 | return 92 | 93 | native_eth_address = "0x0000000000000000000000000000000000000000" 94 | 95 | callback = native_eth_address 96 | data = encode( 97 | ["address"], 98 | [account_address] 99 | ) 100 | 101 | tx = contract.functions.addLiquidity2( 102 | Web3.to_checksum_address(pool_address), 103 | [(Web3.to_checksum_address(to_token_address), 0), 104 | (Web3.to_checksum_address(callback), amount)] if from_token.lower() == 'eth' else [ 105 | (Web3.to_checksum_address(from_token_address), amount)], 106 | data, 107 | 0, 108 | callback, 109 | '0x' 110 | ).build_transaction({ 111 | 'from': account_address, 112 | 'value': amount if from_token.lower() == 'eth' else 0, 113 | 'nonce': web3.eth.get_transaction_count(account_address), 114 | 'gasPrice': web3.eth.gas_price, 115 | }) 116 | return tx 117 | 118 | 119 | def create_liquidity_remove_tx(web3: Web3, contract: Contract, from_token_pair_address: str, amount: int, 120 | account_address: Address, token: str) -> TxParams: 121 | from_token_address = tokens[token] 122 | pool_address = get_pool(web3, '0x37BAc764494c8db4e54BDE72f6965beA9fa0AC2d', from_token_address, 123 | from_token_pair_address) 124 | 125 | data = encode( 126 | ["address", "uint8"], 127 | [account_address, 1] 128 | ) 129 | 130 | tx = contract.functions.burnLiquidity( 131 | Web3.to_checksum_address(pool_address), 132 | amount, 133 | data, 134 | [0, 0], 135 | "0x0000000000000000000000000000000000000000", 136 | '0x' 137 | 138 | ).build_transaction({ 139 | 'from': account_address, 140 | 'nonce': web3.eth.get_transaction_count(account_address), 141 | 'gasPrice': web3.eth.gas_price, 142 | }) 143 | return tx 144 | -------------------------------------------------------------------------------- /src/modules/swaps/wrapper/eth_wrapper.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | import random 3 | 4 | from loguru import logger 5 | 6 | from src.modules.swaps.wrapper.transaction_data import create_wrap_tx 7 | from src.utils.wrappers.decorators import retry 8 | from src.database.utils import DataBaseUtils 9 | from src.utils.user.account import Account 10 | from src.utils.data.tokens import tokens 11 | from config import USE_DATABASE 12 | 13 | 14 | class Wrapper(Account): 15 | def __init__(self, private_key: str, action: str, amount: Union[float, List[float]], use_all_balance: bool, 16 | use_percentage: bool, percentage_to_wrap: Union[float, List[float]]) -> None: 17 | 18 | super().__init__(private_key) 19 | 20 | self.action = action 21 | if isinstance(amount, List): 22 | self.amount = round(random.uniform(amount[0], amount[1]), 7) 23 | elif isinstance(amount, float): 24 | self.amount = amount 25 | else: 26 | logger.error(f'amount must be List[float] of float. Got {type(amount)}') 27 | return 28 | 29 | self.use_all_balance = use_all_balance 30 | self.use_percentage = use_percentage 31 | 32 | if isinstance(percentage_to_wrap, List): 33 | self.percentage_to_wrap = random.uniform(percentage_to_wrap[0], percentage_to_wrap[1]) 34 | elif isinstance(percentage_to_wrap, float): 35 | self.percentage_to_wrap = percentage_to_wrap 36 | else: 37 | logger.error(f'percentage_to_wrap must be List[float] or float. Got {type(percentage_to_wrap)}') 38 | return 39 | 40 | if self.action.lower() == 'wrap': 41 | self.token = 'ETH' 42 | self.to_token = 'WETH' 43 | else: 44 | self.token = 'WETH' 45 | self.to_token = 'ETH' 46 | 47 | self.db_utils = DataBaseUtils('swap') 48 | 49 | def __repr__(self) -> str: 50 | return f'{self.__class__.__name__}: {self.account_address} | {"ETH => WETH" if self.action.lower() == "wrap" else "WETH => ETH"}' 51 | 52 | @retry() 53 | async def wrap(self) -> None: 54 | balance = self.get_wallet_balance('ETH' if self.action.lower() == 'wrap' else 'WETH', tokens['ETH']) 55 | 56 | if balance == 0: 57 | self.logger.error(f"🅾️ | Your balance is 0 | {self.account_address}") 58 | return 59 | 60 | amount = int(self.amount * 10 ** 18) 61 | 62 | if self.use_all_balance is True: 63 | if self.action.lower() == 'unwrap': 64 | amount = balance 65 | elif self.action.lower() == 'wrap': 66 | self.logger.error(f'You can not use use_all_balance = True to wrap ETH. Using amount_from, amount_to.') 67 | if self.use_percentage: 68 | amount = int(balance * self.percentage_to_wrap) 69 | 70 | tx = create_wrap_tx(self.account_address, self.token, self.web3, amount) 71 | 72 | tx_hash = self.sign_transaction(tx) 73 | 74 | self.logger.success( 75 | f'Successfully {"unwrapped" if self.action.lower() == "unwrap" else "wrapped"} {amount / 10 ** 18} {"WETH => ETH" if self.action.lower() == "unwrap" else "ETH => WETH"} | TX: https://blockscout.scroll.io/tx/{tx_hash}' 76 | ) 77 | if USE_DATABASE: 78 | await self.db_utils.add_to_db(self.account_address, tx_hash, 'Wrapping', amount * 1e-18, self.token, 79 | self.to_token) 80 | -------------------------------------------------------------------------------- /src/modules/swaps/wrapper/transaction_data.py: -------------------------------------------------------------------------------- 1 | from web3.types import TxParams 2 | from eth_typing import Address 3 | from web3 import Web3 4 | 5 | from src.utils.data.tokens import tokens 6 | from src.utils.user.utils import Utils 7 | 8 | 9 | def create_wrap_tx(account_address: Address, from_token: str, web3: Web3, amount: int) -> TxParams: 10 | contract = web3.eth.contract(address=tokens['ETH'], abi=Utils.load_abi('eth')) 11 | 12 | if from_token.lower() == 'eth': 13 | tx = contract.functions.deposit().build_transaction({ 14 | 'value': amount, 15 | 'nonce': web3.eth.get_transaction_count(account_address), 16 | 'from': account_address, 17 | 'gasPrice': web3.eth.gas_price, 18 | }) 19 | else: 20 | tx = contract.functions.withdraw(amount).build_transaction({ 21 | 'value': 0, 22 | 'nonce': web3.eth.get_transaction_count(account_address), 23 | 'from': account_address, 24 | 'gasPrice': web3.eth.gas_price, 25 | }) 26 | 27 | return tx 28 | -------------------------------------------------------------------------------- /src/modules/swaps/zebra/utils/transaction_data.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | from web3.contract import Contract 4 | from web3.types import TxParams 5 | from eth_typing import Address 6 | from web3 import Web3 7 | 8 | from config import SLIPPAGE 9 | 10 | 11 | def get_amount_out(contract: Contract, amount: int, from_token_address: Address, 12 | to_token_address: Address) -> int: 13 | amount_out = contract.functions.getAmountsOut( 14 | amount, 15 | [from_token_address, to_token_address] 16 | ).call() 17 | return amount_out[1] 18 | 19 | 20 | def create_swap_tx(from_token: str, contract: Contract, amount_out: int, from_token_address: str, 21 | to_token_address: str, account_address: Address, amount: int, web3: Web3) -> TxParams: 22 | if from_token.lower() == 'eth': 23 | tx = contract.functions.swapExactETHForTokens( 24 | int(amount_out * (1 - SLIPPAGE)), 25 | [web3.to_checksum_address(from_token_address), web3.to_checksum_address(to_token_address)], 26 | account_address, 27 | int(time() + 1200) 28 | ).build_transaction({ 29 | 'value': amount if from_token.lower() == 'eth' else 0, 30 | 'nonce': web3.eth.get_transaction_count(account_address), 31 | 'from': account_address, 32 | 'gasPrice': web3.eth.gas_price, 33 | }) 34 | else: 35 | tx = contract.functions.swapExactTokensForETH( 36 | amount, 37 | int(amount_out * (1 - SLIPPAGE)), 38 | [web3.to_checksum_address(from_token_address), web3.to_checksum_address(to_token_address)], 39 | account_address, 40 | int(time() + 1200) 41 | ).build_transaction({ 42 | 'value': amount if from_token.lower() == 'eth' else 0, 43 | 'nonce': web3.eth.get_transaction_count(account_address), 44 | 'from': account_address, 45 | 'gasPrice': web3.eth.gas_price, 46 | }) 47 | return tx 48 | -------------------------------------------------------------------------------- /src/modules/swaps/zebra/zebra.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List 2 | 3 | from web3.contract import Contract 4 | from eth_typing import Address 5 | from web3 import Web3 6 | 7 | from src.utils.base_swap import BaseSwap 8 | from src.utils.data.types import Types 9 | 10 | from src.utils.data.contracts import ( 11 | contracts, 12 | abi_names, 13 | ) 14 | 15 | from src.modules.swaps.zebra.utils.transaction_data import ( 16 | get_amount_out, 17 | create_swap_tx, 18 | ) 19 | 20 | 21 | class ZebraSwap(BaseSwap): 22 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], 23 | amount: Union[float, List[float]], use_percentage: bool, swap_percentage: Union[float, List[float]], 24 | swap_all_balance: bool) -> None: 25 | contract_address = contracts['zebra'] 26 | abi_name = abi_names['zebra'] 27 | dex_name = self.__class__.__name__ 28 | 29 | super().__init__(private_key, from_token, to_token, amount, use_percentage, swap_percentage, swap_all_balance, 30 | contract_address, abi_name, dex_name) 31 | 32 | def __repr__(self) -> str: 33 | return f'{self.__class__.__name__}: {self.account_address} | {self.from_token} => {self.to_token}' 34 | 35 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 36 | to_token_address: Address) -> Types.AmountOut: 37 | return get_amount_out(contract, amount, from_token_address, to_token_address) 38 | 39 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 40 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 41 | web3: Web3) -> Types.SwapTransaction: 42 | return create_swap_tx(from_token, contract, amount_out, from_token_address, to_token_address, 43 | account_address, amount, web3) 44 | -------------------------------------------------------------------------------- /src/utils/base_bridge.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List, Optional 2 | from abc import ABC, abstractmethod 3 | from fractions import Fraction 4 | from asyncio import sleep 5 | import random 6 | 7 | from web3.contract import Contract 8 | from eth_typing import Address 9 | from web3 import Web3 10 | 11 | from src.modules.bridges.main_bridge.utils.transaction_data import claim_eth 12 | from src.utils.data.chains import chain_mapping 13 | from src.utils.wrappers.decorators import retry 14 | from src.database.utils import DataBaseUtils 15 | from src.utils.user.account import Account 16 | from src.utils.data.types import Types 17 | from config import USE_DATABASE 18 | 19 | 20 | class BaseBridge(ABC, Account): 21 | def __init__(self, private_key: str, amount: Union[float, List[float]], use_percentage: bool, 22 | bridge_percentage: Union[float, List[float]], contract_address: Optional[str], abi_name: Optional[str], 23 | dex_name: str, rpc: Optional[str], scan: str = 'https://etherscan.io/tx', 24 | from_chain: Optional[str] = None, to_chain: Optional[str] = None, code: Optional[int] = None, 25 | claim_eth: Optional[bool] = None) -> None: 26 | 27 | super().__init__(private_key, rpc) 28 | 29 | self.private_key = private_key 30 | 31 | if isinstance(amount, List): 32 | self.amount = round(random.uniform(amount[0], amount[1]), 7) 33 | else: 34 | self.amount = amount 35 | if isinstance(bridge_percentage, List): 36 | self.bridge_percentage = random.uniform(bridge_percentage[0], bridge_percentage[1]) 37 | else: 38 | self.bridge_percentage = bridge_percentage 39 | self.use_percentage = use_percentage 40 | self.contract_address = contract_address 41 | self.abi_name = abi_name 42 | self.dex_name = dex_name 43 | self.scan = scan 44 | self.from_chain = from_chain 45 | self.to_chain = to_chain 46 | self.code = code 47 | self.claim_eth = claim_eth 48 | self.db_utils = DataBaseUtils('bridge') 49 | 50 | @retry() 51 | async def bridge(self) -> None: 52 | contract = None 53 | if not self.dex_name == 'Owlto' or not self.dex_name == 'Orbiter': 54 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 55 | balance = self.get_wallet_balance('ETH', '...') 56 | 57 | if balance == 0: 58 | self.logger.error(f'🅾️ | Your balance is 0 | {self.account_address}') 59 | return 60 | 61 | to_chain_account = Account(self.private_key, chain_mapping[self.to_chain.lower()].rpc) 62 | balance_before_bridge = to_chain_account.get_wallet_balance('ETH', '...') 63 | 64 | if self.use_percentage: 65 | amount = int(balance * self.bridge_percentage) 66 | amount = round(amount, 7) 67 | else: 68 | amount = self.create_amount('ETH', '...', self.web3, self.amount) 69 | 70 | amount = int(str(amount)[:-7] + '0000000') 71 | 72 | if self.dex_name == 'Owlto' or self.dex_name == 'Orbiter': 73 | amount = int(str(Fraction(amount))[:-4] + str(self.code)) 74 | 75 | if amount > balance: 76 | self.logger.error(f'📉 | Not enough balance for wallet {self.account_address}') 77 | return 78 | 79 | eligibility, min_limit, max_limit = self.check_eligibility(amount) 80 | if eligibility is False: 81 | self.logger.error(f'Limits error. {min_limit} < amount < {max_limit}. Got {amount / 10 ** 18} ETH') 82 | return 83 | 84 | tx = self.create_bridge_tx(contract, amount, self.web3, self.account_address) 85 | if self.dex_name == 'Orbiter' or self.dex_name == 'Owlto': 86 | gas_limit = self.web3.eth.estimate_gas(tx) 87 | tx.update({'gas': gas_limit}) 88 | 89 | tx_hash = self.sign_transaction(tx) 90 | self.logger.success( 91 | f'Successfully bridged {amount / 10 ** 18} ETH tokens | TX: {self.scan}/{tx_hash}' 92 | ) 93 | if USE_DATABASE: 94 | await self.db_utils.add_to_db(self.account_address, tx_hash, self.dex_name, amount / 10 ** 18) 95 | 96 | if self.dex_name == 'MainBridge' and self.from_chain == 'SCROLL' and self.claim_eth: 97 | self.logger.debug('Claiming ETH...') 98 | await claim_eth(tx_hash, self.private_key) 99 | 100 | await self.wait_for_bridge(to_chain_account, balance_before_bridge) 101 | 102 | async def wait_for_bridge(self, account: Account, balance_before_bridge: int) -> None: 103 | self.logger.info(f'Waiting for ETH to arrive in {self.to_chain.upper()}...') 104 | while True: 105 | try: 106 | balance = account.get_wallet_balance('ETH', '...') 107 | if balance > balance_before_bridge: 108 | self.logger.success(f'ETH has arrived | [{self.account_address}]') 109 | break 110 | await sleep(20) 111 | except Exception as ex: 112 | self.logger.error(f'Something went wrong {ex}') 113 | await sleep(10) 114 | continue 115 | 116 | @abstractmethod 117 | def create_bridge_tx(self, contract: Contract, amount: int, web3: Web3, account_address: Address 118 | ) -> Types.BridgeTransaction: 119 | """Create a transaction for bridge""" 120 | 121 | @abstractmethod 122 | def check_eligibility(self, amount: int) -> Union[bool, tuple[bool, float, int]]: 123 | """Check if eligible for bridge""" 124 | -------------------------------------------------------------------------------- /src/utils/base_liquidity.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from asyncio import sleep 3 | import random 4 | 5 | from typing import ( 6 | Union, 7 | List, 8 | Any, 9 | ) 10 | 11 | from web3.contract import Contract 12 | from eth_typing import Address 13 | from web3 import Web3 14 | 15 | from src.database.utils import DataBaseUtils 16 | from src.utils.wrappers.decorators import retry 17 | from src.utils.user.account import Account 18 | from src.utils.data.tokens import tokens 19 | from src.utils.data.types import Types 20 | 21 | from config import USE_DATABASE 22 | 23 | 24 | class BaseLiquidity(ABC, Account): 25 | def __init__(self, private_key: str, token: str, token2: str, amount: Union[float, List[float]], 26 | use_percentage: bool, liquidity_percentage: Union[float, List[float]], 27 | contract_address: str, abi_name: str, dex_name: str) -> None: 28 | 29 | super().__init__(private_key) 30 | 31 | self.private_key = private_key 32 | self.token = token 33 | 34 | self.token2 = token2 35 | if isinstance(token2, list): 36 | self.token2 = random.choice(token2) 37 | 38 | if isinstance(amount, List): 39 | self.amount = round(random.uniform(amount[0], amount[1]), 7) 40 | elif isinstance(amount, float): 41 | self.amount = amount 42 | else: 43 | self.logger.error(f'amount must be float or List[float]. Got {type(amount)}') 44 | return 45 | self.use_percentage = use_percentage 46 | if isinstance(liquidity_percentage, List): 47 | self.liquidity_percentage = random.uniform(liquidity_percentage[0], liquidity_percentage[1]) 48 | elif isinstance(liquidity_percentage, float): 49 | self.liquidity_percentage = liquidity_percentage 50 | else: 51 | self.logger.error(f'liquidity_percentage must be float or List[float]. Got {type(liquidity_percentage)}') 52 | return 53 | self.contract_address = contract_address 54 | self.abi_name = abi_name 55 | self.dex_name = dex_name 56 | self.db_utils = DataBaseUtils('liquidity') 57 | 58 | @retry() 59 | async def add_liquidity(self) -> None: 60 | from_token_address, to_token_address = tokens[self.token.upper()], tokens[self.token2.upper()] 61 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 62 | amount = self.create_amount(self.token, from_token_address, self.web3, self.amount) 63 | balance = self.get_wallet_balance(self.token, from_token_address) 64 | 65 | if self.use_percentage: 66 | amount = int(balance * self.liquidity_percentage) 67 | 68 | if amount > balance: 69 | self.logger.error(f'Not enough balance for wallet | {self.account_address}') 70 | return 71 | 72 | await self.approve_token(amount, self.private_key, from_token_address, self.contract_address, 73 | self.account_address, self.web3) 74 | 75 | await self.approve_token(amount, self.private_key, to_token_address, self.contract_address, 76 | self.account_address, self.web3) 77 | 78 | amount_out = 0 79 | if self.dex_name != 'SyncSwapLiquidity': 80 | while True: 81 | stable_balance = self.get_wallet_balance(self.token2, to_token_address) 82 | amount_out = self.get_amount_out(contract, amount, Web3.to_checksum_address(from_token_address), 83 | Web3.to_checksum_address(to_token_address)) 84 | if amount_out > stable_balance: 85 | self.logger.error(f'Not enough {self.token2.upper()} balance for wallet | {self.account_address}') 86 | self.logger.info(f'Swapping {amount / 10 ** 18} ETH => {self.token2.upper()}') 87 | swap = await self.get_swap_instance(self.private_key, self.token, self.token2, amount / 10 ** 18) 88 | await swap.swap() 89 | sleep_time = random.randint(45, 75) 90 | self.logger.info(f'Sleeping {sleep_time} seconds...') 91 | await sleep(sleep_time) 92 | continue 93 | break 94 | 95 | tx = self.create_liquidity_tx(self.token, contract, amount_out, from_token_address, to_token_address, 96 | self.account_address, amount, self.web3) 97 | 98 | tx_hash = self.sign_transaction(tx) 99 | 100 | self.logger.success( 101 | f'Successfully added liquidity with {amount / 10 ** 18} {self.token}, {amount_out / 10 ** 18} {self.token2.upper()} | TX: https://blockscout.scroll.io/tx/{tx_hash}' 102 | ) 103 | if USE_DATABASE: 104 | if self.token.lower() == 'eth': 105 | value_amount = self.amount 106 | elif self.token2.lower() == 'eth': 107 | value_amount = amount_out * 1e-18 108 | else: 109 | value_amount = self.get_amount_out(contract, amount, Web3.to_checksum_address(from_token_address), 110 | Web3.to_checksum_address( 111 | '0x5300000000000000000000000000000000000004')) * 1e-18 112 | 113 | await self.db_utils.add_to_db(self.account_address, f'https://blockscout.scroll.io/tx/{tx_hash}', 114 | self.dex_name, 115 | value_amount * 2 if self.dex_name != 'SyncSwap' else value_amount, 116 | self.token, self.token2) 117 | 118 | @abstractmethod 119 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 120 | to_token_address: Address) -> Types.AmountOut: 121 | """Get the stable amount for liquidity.""" 122 | 123 | @abstractmethod 124 | def create_liquidity_tx(self, from_token: str, contract: Contract, amount_out: int, from_token_address: str, 125 | to_token_address: str, account_address: Address, amount: int, 126 | web3: Web3) -> Types.LiquidityTransaction: 127 | """Create a transaction for liquidity.""" 128 | 129 | @abstractmethod 130 | async def get_swap_instance(self, private_key: str, token: str, token2: str, amount: float) -> Any: 131 | """Get an instance for swap if not enough stable balance for liquidity.""" 132 | -------------------------------------------------------------------------------- /src/utils/base_liquidity_remove.py: -------------------------------------------------------------------------------- 1 | import random 2 | from abc import ABC, abstractmethod 3 | from typing import Union, List 4 | 5 | from web3.contract import Contract 6 | from eth_typing import Address 7 | from loguru import logger 8 | from web3 import Web3 9 | 10 | from src.utils.data.types import Types 11 | from src.utils.user.account import Account 12 | from src.utils.wrappers.decorators import retry 13 | 14 | from src.utils.data.tokens import ( 15 | liquidity_tokens, 16 | tokens, 17 | ) 18 | 19 | 20 | class BaseLiquidityRemove(ABC, Account): 21 | def __init__(self, private_key: str, from_token_pair: Union[str, List[str]], remove_all: bool, 22 | removing_percentage: float, contract_address: str, abi_name: str, pool_name: str, 23 | token: str = None) -> None: 24 | 25 | super().__init__(private_key) 26 | 27 | self.private_key = private_key 28 | 29 | if isinstance(from_token_pair, List): 30 | self.from_token_pair = random.choice(from_token_pair) 31 | elif isinstance(from_token_pair, str): 32 | self.from_token_pair = from_token_pair 33 | else: 34 | self.logger.error(f'from_token_pair must be str or list[str]. Got {type(from_token_pair)}') 35 | 36 | self.remove_all = remove_all 37 | self.removing_percentage = removing_percentage 38 | self.contract_address = contract_address 39 | self.abi_name = abi_name 40 | self.pool_name = pool_name 41 | self.token = token 42 | 43 | @retry() 44 | async def remove_liquidity(self) -> None: 45 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 46 | liquidity_token_address = liquidity_tokens[self.pool_name]['ETH' if self.token is None else self.token.upper()][ 47 | self.from_token_pair.upper()] 48 | balance = self.get_wallet_balance('...', liquidity_token_address) 49 | 50 | if balance == 0: 51 | logger.debug("You don't have any tokens to withdraw") 52 | return 53 | 54 | amount = self.create_liquidity_remove_amount(balance, self.remove_all, self.removing_percentage) 55 | 56 | await self.approve_token(amount, self.private_key, liquidity_token_address, self.contract_address, 57 | self.account_address, self.web3) 58 | 59 | tx = self.create_liquidity_remove_tx(self.web3, contract, tokens[self.from_token_pair.upper()], 60 | amount, self.account_address, token=self.token) 61 | 62 | tx_hash = self.sign_transaction(tx) 63 | 64 | logger.success( 65 | f'Removed {"all" if self.remove_all else f"{int(self.removing_percentage * 100)}%"} {self.from_token_pair} tokens from {self.pool_name} pool | TX: https://blockscout.scroll.io/tx/{tx_hash}' 66 | ) 67 | 68 | @abstractmethod 69 | def create_liquidity_remove_tx(self, web3: Web3, contract: Contract, from_token_pair_address: str, 70 | amount: int, account_address: Address, token: str = None 71 | ) -> Types.LiquidityRemoveTransaction: 72 | """Create a transaction for liquidity removal.""" 73 | -------------------------------------------------------------------------------- /src/utils/base_mint.py: -------------------------------------------------------------------------------- 1 | from abc import ( 2 | abstractmethod, 3 | ABC, 4 | ) 5 | 6 | from web3.contract import Contract 7 | 8 | from src.utils.wrappers.decorators import retry 9 | from src.database.utils import DataBaseUtils 10 | from src.utils.user.account import Account 11 | from src.utils.data.types import Types 12 | from config import USE_DATABASE 13 | 14 | 15 | class BaseMint(ABC, Account): 16 | def __init__(self, private_key: str, contract_address: str, abi_name: str, nft_name: str, 17 | amount: float = None) -> None: 18 | super().__init__(private_key) 19 | self.db_utils = DataBaseUtils('mint') 20 | self.contract_address = contract_address 21 | self.abi_name = abi_name 22 | self.nft_name = nft_name 23 | self.amount = amount 24 | 25 | @retry() 26 | async def mint(self) -> None: 27 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 28 | balance = self.get_wallet_balance('ETH', '...') 29 | 30 | if balance == 0: 31 | self.logger.error(f'🅾️ | Your balance is 0 | {self.account_address}') 32 | return 33 | 34 | tx = self.create_mint_tx(contract) 35 | if tx is None: 36 | return 37 | 38 | tx_hash = self.sign_transaction(tx) 39 | confirmed = self.wait_until_tx_finished(tx_hash) 40 | 41 | if confirmed: 42 | self.logger.success( 43 | f'Successfully minted NFT | TX: https://scrollscan.com/tx/{tx_hash}' 44 | ) 45 | if USE_DATABASE: 46 | await self.db_utils.add_to_db(self.account_address, f'https://scrollscan.com/tx/{tx_hash}', 47 | self.nft_name, self.amount) 48 | 49 | @abstractmethod 50 | def create_mint_tx(self, contract: Contract) -> Types.MintTransaction: 51 | """Create a transaction for mint.""" 52 | -------------------------------------------------------------------------------- /src/utils/base_swap.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Union, List, Optional 3 | import random 4 | 5 | from web3.contract import Contract 6 | from eth_typing import Address 7 | from web3 import Web3 8 | 9 | from src.utils.wrappers.decorators import retry 10 | from src.database.utils import DataBaseUtils 11 | from src.utils.user.account import Account 12 | from src.utils.data.tokens import tokens 13 | from src.utils.data.types import Types 14 | from config import USE_DATABASE 15 | 16 | 17 | class BaseSwap(ABC, Account): 18 | def __init__(self, private_key: str, from_token: str, to_token: Union[str, List[str]], amount: Union[float, List[float]], 19 | use_percentage: bool, swap_percentage: Union[float, List[float]], swap_all_balance: bool, 20 | contract_address: str, abi_name: str, dex_name: str) -> None: 21 | 22 | super().__init__(private_key) 23 | 24 | self.private_key = private_key 25 | self.from_token = from_token 26 | if isinstance(to_token, List): 27 | self.to_token = random.choice(to_token) 28 | elif isinstance(to_token, str): 29 | self.to_token = to_token 30 | else: 31 | self.logger.error(f'to_token must be str or list[str]. Got {type(to_token)}') 32 | return 33 | if isinstance(amount, List): 34 | self.amount = round(random.uniform(amount[0], amount[1]), 7) 35 | else: 36 | self.amount = amount 37 | self.use_percentage = use_percentage 38 | if isinstance(swap_percentage, List): 39 | self.swap_percentage = random.uniform(swap_percentage[0], swap_percentage[1]) 40 | else: 41 | self.swap_percentage = swap_percentage 42 | self.swap_all_balance = swap_all_balance 43 | self.contract_address = contract_address 44 | self.abi_name = abi_name 45 | self.dex_name = dex_name 46 | self.db_utils = DataBaseUtils('swap') 47 | if self.use_percentage and self.swap_all_balance: 48 | self.logger.warning('You set use_percentage and swap_all_balance both True. Using swap_percentage.') 49 | 50 | @retry() 51 | async def swap(self) -> Optional[Union[bool, str]]: 52 | from_token_address, to_token_address = tokens[self.from_token.upper()], tokens[self.to_token.upper()] 53 | contract = self.load_contract(self.contract_address, self.web3, self.abi_name) 54 | balance = self.get_wallet_balance(self.from_token, from_token_address) 55 | amount = self.create_amount(self.from_token, from_token_address, self.web3, self.amount) 56 | 57 | if balance == 0: 58 | self.logger.error(f'🅾️ | Your balance is 0 | {self.account_address}') 59 | return 'ZeroBalance' 60 | 61 | if self.swap_all_balance is True and self.from_token.lower() == 'eth': 62 | self.logger.error( 63 | "You can't use swap_all_balance = True with ETH token. Using amount_from, amount_to") 64 | if self.swap_all_balance is True and self.from_token.lower() != 'eth': 65 | amount = balance 66 | 67 | if self.use_percentage is True: 68 | amount = int(balance * self.swap_percentage) 69 | 70 | if amount > balance: 71 | self.logger.error(f'📉 | Not enough balance for wallet {self.account_address}') 72 | return 73 | 74 | amount_out = self.get_amount_out(contract, amount, self.web3.to_checksum_address(from_token_address), 75 | self.web3.to_checksum_address(to_token_address)) 76 | 77 | if self.from_token.lower() != 'eth': 78 | await self.approve_token(amount, self.private_key, from_token_address, self.contract_address, 79 | self.account_address, self.web3) 80 | 81 | tx = self.create_swap_tx(self.from_token, self.to_token, contract, amount_out, from_token_address, 82 | to_token_address, self.account_address, amount, self.web3) 83 | 84 | if tx is None: 85 | return 86 | 87 | try: 88 | tx_hash = self.sign_transaction(tx) 89 | confirmed = self.wait_until_tx_finished(tx_hash) 90 | except Exception as ex: 91 | self.logger.error(f'Something went wrong {ex}') 92 | return False 93 | 94 | if confirmed: 95 | self.logger.success( 96 | f'Successfully swapped {"all" if self.swap_all_balance is True and self.from_token.lower() != "eth" and self.use_percentage is False else f"{int(self.swap_percentage * 100)}%" if self.use_percentage is True else self.amount} {self.from_token} tokens => {self.to_token} | TX: https://blockscout.scroll.io/tx/{tx_hash}' 97 | ) 98 | 99 | if USE_DATABASE: 100 | if self.from_token.lower() == 'eth': 101 | value_amount = self.amount 102 | elif self.to_token.lower() == 'eth': 103 | value_amount = amount_out * 1e-18 104 | else: 105 | value_amount = self.get_amount_out(contract, amount, Web3.to_checksum_address(from_token_address), 106 | Web3.to_checksum_address( 107 | '0x5300000000000000000000000000000000000004')) * 1e-18 108 | 109 | await self.db_utils.add_to_db(self.account_address, f'https://blockscout.scroll.io/tx/{tx_hash}', 110 | self.dex_name, value_amount, self.from_token, self.to_token) 111 | return True 112 | else: 113 | return False 114 | 115 | @abstractmethod 116 | def get_amount_out(self, contract: Contract, amount: int, from_token_address: Address, 117 | to_token_address: Address) -> Types.AmountOut: 118 | """Get the amount after swap.""" 119 | 120 | @abstractmethod 121 | def create_swap_tx(self, from_token: str, to_token: str, contract: Contract, amount_out: int, 122 | from_token_address: str, to_token_address: str, account_address: Address, amount: int, 123 | web3: Web3) -> Types.SwapTransaction: 124 | """Create a transaction for swap.""" 125 | -------------------------------------------------------------------------------- /src/utils/data/chains.py: -------------------------------------------------------------------------------- 1 | class Chain: 2 | def __init__(self, chain_id: int, rpc: str, scan: str) -> None: 3 | self.chain_id = chain_id 4 | self.rpc = rpc 5 | self.scan = scan 6 | 7 | 8 | ETH = Chain( 9 | chain_id=1, 10 | rpc='https://rpc.ankr.com/eth', 11 | scan='https://etherscan.io/tx' 12 | ) 13 | 14 | BASE = Chain( 15 | chain_id=8453, 16 | rpc='https://base.blockpi.network/v1/rpc/public', 17 | scan='https://basescan.org//tx' 18 | ) 19 | 20 | ARB = Chain( 21 | chain_id=42161, 22 | rpc='https://arb1.arbitrum.io/rpc', 23 | scan='https://arbiscan.io/tx' 24 | ) 25 | 26 | ERA = Chain( 27 | chain_id=324, 28 | rpc='https://1rpc.io/zksync2-era', 29 | scan='https://explorer.zksync.io/tx' 30 | ) 31 | 32 | SCROLL = Chain( 33 | chain_id=534352, 34 | rpc='https://rpc.scroll.io', 35 | scan='https://blockscout.scroll.io/tx' 36 | ) 37 | 38 | OP = Chain( 39 | chain_id=10, 40 | rpc='https://optimism.blockpi.network/v1/rpc/public', 41 | scan='https://optimistic.etherscan.io/tx' 42 | ) 43 | 44 | LINEA = Chain( 45 | chain_id=59144, 46 | rpc='https://linea.blockpi.network/v1/rpc/public', 47 | scan='https://lineascan.build/tx' 48 | ) 49 | 50 | chain_mapping = { 51 | 'base': BASE, 52 | 'eth': ETH, 53 | 'era': ERA, 54 | 'arb': ARB, 55 | 'scroll': SCROLL, 56 | 'op': OP, 57 | 'linea': LINEA 58 | } 59 | 60 | okx_chain_mapping = { 61 | 'ERC20': ETH, 62 | 'Arbitrum One': ARB, 63 | 'Optimism': OP, 64 | 'zkSync Era': ERA, 65 | 'Base': BASE, 66 | 'Linea': LINEA 67 | } 68 | -------------------------------------------------------------------------------- /src/utils/data/contracts.py: -------------------------------------------------------------------------------- 1 | contracts = { 2 | 'skydrome': '0xAA111C62cDEEf205f70E6722D1E22274274ec12F', 3 | 'punkswap': '0x26cB8660EeFCB2F7652e7796ed713c9fB8373f8e', 4 | 'spacefi': '0x18b71386418A9FCa5Ae7165E31c385a5130011b6', 5 | 'zebra': '0x0122960d6e391478bfe8fb2408ba412d5600f621', 6 | 'syncswap': '0x80e38291e06339d10AAB483C65695D004dBD5C69', 7 | 'dmail': '0x47fbe95e981c0df9737b6971b451fb15fdc989d9', 8 | 'layerbank': '0xec53c830f4444a8a56455c6836b5d2aa794289aa', 9 | 'aave': { 10 | 'aave_contract': '0xff75a4b698e3ec95e608ac0f22a03b8368e05f5d', 11 | 'aave_weth': '0xf301805be1df81102c957f6d4ce29d2b8c056b2a' 12 | }, 13 | 'l2pass': '0x0000049f63ef0d60abe49fdd8bebfa5a68822222', 14 | 'omnisea': '0x46ce46951d12710d85bc4fe10bb29c6ea5012077', 15 | 'rubyscore': '0xe10Add2ad591A7AC3CA46788a06290De017b9fB4', 16 | 'scroll_citizen': [ 17 | "0xc519d9d47c003b6274e20cfe21d58fee1efa7a0e", 18 | "0x1249e38bb84aa3cbe6f4506a85b6831ef49ed48f", 19 | "0xcde5e31d0c7348161b76579a6e25b8874de91434", 20 | "0xd4eac5a09e5e8ac8e94f62e46b5d9e808e577d2e", 21 | "0x51c8b85aedb821712c7115f36d252018951c4b16", 22 | "0x6982d37e2bc0de66ce744a65a79c57926f11a947", 23 | "0xf4647c674e32506809f77cf3236ed8034e817cc9", 24 | "0x6b4772a613a63cbdb15c767bd604e9f5ecf60fcd", 25 | "0x4395df30ef87a2c23ab393fe0bf1f2d2ef6eefc1", 26 | "0x36c9724d98dc3f46676bf213da318e556bcc3d16", 27 | "0x80151e432f5c6d6c89427bceee6738bcc61e3fa6", 28 | "0xd4215d6aff866151c2df3ebed8ff0cc084b7d2cf", 29 | "0xde2fea1c76d1d08b0055b8ae6bc4ce8a31403192", 30 | "0xbdb2cd55421ecd520a04be90b6dee93689a203de", 31 | "0x65665e3275e2a122c61f953929ca13c1bb5a593b", 32 | "0x07e2f41b117b34dda4c7044242e903053a7ea025", 33 | "0x6de8a54d6771325e53e53e85aaf614392839caff", 34 | "0x9efd036f4f30d9802d4dc1b7ece292d2ef896883", 35 | "0x57324f9d28d0b89ec980b0b0c6a432c761faf6b2", 36 | "0x2ab5a55aac0df0087fb2b352372fe19e84f46041" 37 | ], 38 | 'zk_stars': [ 39 | "0x609c2f307940b8f52190b6d3d3a41c762136884e", 40 | "0x16c0baa8a2aa77fab8d0aece9b6947ee1b74b943", 41 | "0xc5471e35533e887f59df7a31f7c162eb98f367f7", 42 | "0xf861f5927c87bc7c4781817b08151d638de41036", 43 | "0x954e8ac11c369ef69636239803a36146bf85e61b", 44 | "0xa576ac0a158ebdcc0445e3465adf50e93dd2cad8", 45 | "0x17863384c663c5f95e4e52d3601f2ff1919ac1aa", 46 | "0x4c2656a6d1c0ecac86f5024e60d4f04dbb3d1623", 47 | "0x4e86532cedf07c7946e238bd32ba141b4ed10c12", 48 | "0x6b9db0ffcb840c3d9119b4ff00f0795602c96086", 49 | "0x10d4749bee6a1576ae5e11227bc7f5031ad351e4", 50 | "0x373148e566e4c4c14f4ed8334aba3a0da645097a", 51 | "0xdacbac1c25d63b4b2b8bfdbf21c383e3ccff2281", 52 | "0x2394b22b3925342f3216360b7b8f43402e6a150b", 53 | "0xf34f431e3fc0ad0d2beb914637b39f1ecf46c1ee", 54 | "0x6f1e292302dce99e2a4681be4370d349850ac7c2", 55 | "0xa21fac8b389f1f3717957a6bb7d5ae658122fc82", 56 | "0x1b499d45e0cc5e5198b8a440f2d949f70e207a5d", 57 | "0xec9bef17876d67de1f2ec69f9a0e94de647fcc93", 58 | "0x5e6c493da06221fed0259a49beac09ef750c3de1" 59 | ] 60 | } 61 | 62 | abi_names = { 63 | 'skydrome': 'skydrome', 64 | 'punkswap': 'punkswap', 65 | 'syncswap': 'syncswap', 66 | 'spacefi': 'spacefi', 67 | 'dmail': 'dmail', 68 | 'layerbank': 'layerbank', 69 | 'aave': 'aave_abi', 70 | 'l2pass': 'l2pass_abi', 71 | 'omnisea': 'omnisea_abi', 72 | 'rubyscore': 'rubyscore_abi', 73 | 'scroll_citizen': 'scroll_citizen_abi', 74 | 'zkstars': 'zkstars_abi', 75 | 'zebra': 'zebra_abi' 76 | } 77 | -------------------------------------------------------------------------------- /src/utils/data/helper.py: -------------------------------------------------------------------------------- 1 | from colorama import Fore 2 | from web3 import Web3 3 | 4 | from src.utils.data.mappings import module_handlers 5 | 6 | with open('config.py', 'r', encoding='utf-8-sig') as file: 7 | module_config = file.read() 8 | 9 | exec(module_config) 10 | 11 | with open('wallets.txt', 'r', encoding='utf-8-sig') as file: 12 | private_keys = [line.strip() for line in file] 13 | 14 | patterns = {} 15 | web3 = Web3(Web3.HTTPProvider('https://rpc.scroll.io')) 16 | 17 | for module in module_handlers: 18 | if globals().get(module): 19 | patterns[module] = 'On' 20 | else: 21 | patterns[module] = 'Off' 22 | 23 | print(Fore.BLUE + f'Loaded {len(private_keys)} wallets:') 24 | print('\033[39m') 25 | 26 | for private_key in private_keys: 27 | print(web3.eth.account.from_key(private_key).address) 28 | 29 | print(f'----------------------------------------Modules--------------------------------------------') 30 | 31 | for pattern, value in patterns.items(): 32 | if value == 'Off': 33 | print("\033[31m {}".format(f'{pattern} = {value}')) 34 | else: 35 | print("\033[32m {}".format(f'{pattern} = {value}')) 36 | print('\033[39m') 37 | 38 | print('Created by | https://t.me/cryptoscripts') 39 | print('Donations (Any EVM) | 0x763cDEa4a54991Cd85bFAd1FD47E9c175f53090B') 40 | active_module = [module for module, value in patterns.items() if value == 'On'] 41 | -------------------------------------------------------------------------------- /src/utils/data/mappings.py: -------------------------------------------------------------------------------- 1 | from src.utils.runner import * 2 | 3 | module_handlers = { 4 | 'main_bridge': process_main_bridge, 5 | 'owl_bridge': process_owl_bridge, 6 | 'orbiter_bridge': process_orbiter_bridge, 7 | 'skydrome_swap': process_skydrome_swap, 8 | 'skydrome_liquidity': process_skydrome_liquidity, 9 | 'skydrome_liquidity_remove': process_skydrome_liquidity_remove, 10 | 'specefi_swap': process_spacefi_swap, 11 | 'spacefi_liquidity': process_spacefi_liquidity, 12 | 'spacefi_liquidity_remove': process_spacefi_liquidity_remove, 13 | 'punk_swap': process_punk_swap, 14 | 'punk_swap_liquidity': process_punk_swap_liquidity, 15 | 'punk_swap_liquidity_remove': process_punk_swap_liquidity_remove, 16 | 'syncswap_swap': process_sync_swap_swap, 17 | 'syncswap_liquidity': process_sync_swap_liquidity, 18 | 'syncswap_liquidity_remove': process_sync_swap_liquidity_remove, 19 | 'swap_all_to_eth': process_swap_all_to_eth, 20 | 'random_dex_swap': process_random_dex_swap, 21 | 'wrapper': process_wrapper, 22 | 'okx_withdraw': process_okx_withdrawal, 23 | 'okx_deposit': process_okx_deposit, 24 | 'dmail': process_dmail, 25 | 'zerius': process_zerius_mint_bridge, 26 | 'deploy_contract': process_deploy, 27 | 'layerbank_deposit': process_layerbank_deposit, 28 | 'layerbank_withdraw': process_layerbank_withdraw, 29 | 'aave_deposit': process_deposit_aave, 30 | 'aave_withdraw': process_withdraw_aave, 31 | 'l2pass': process_l2pass_mint, 32 | 'omnisea': process_omnisea_create, 33 | 'rubyscore': process_rubyscore_voting, 34 | 'scroll_citizen': process_scroll_citizen_mint, 35 | 'zk_stars': process_zkstars_mint, 36 | 'zebra_swap': process_zebra_swap, 37 | 'multi_approve': process_multi_approve, 38 | 'random_add_liquidity': process_random_liquidity 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/data/tokens.py: -------------------------------------------------------------------------------- 1 | tokens = { 2 | 'ETH': '0x5300000000000000000000000000000000000004', 3 | 'WETH': '0x5300000000000000000000000000000000000004', 4 | 'USDC': '0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A4', 5 | 'USDT': '0xf55BEC9cafDbE8730f096Aa55dad6D22d44099Df', 6 | 'PUNK': '0xDdEB23905F6987d5f786A93C00bBED3d97Af1ccc', 7 | 'DAI': '0xca77eb3fefe3725dc33bccb54edefc3d9f764f97', 8 | 'WBTC': '0x3c1bca5a656e69edcd0d4e36bebb3fcdaca60cf1', 9 | 'SYS': '0x1467b62a6ae5cdcb10a6a8173cfe187dd2c5a136', 10 | 'ITP': '0x2b1d36f5b61addaf7da7ebbd11b35fd8cfb0de31', 11 | 'LUSD': '0xedeabc3a1e7d21fe835ffa6f83a710c70bb1a051', 12 | 'wstETH': '0xf610a9dfb7c89644979b4a0f27063e9e7d7cda32', 13 | 'rETH': '0x53878b874283351d26d206fa512aece1bef6c0dd', 14 | 'wrsETH': '0xa25b25548b4c98b0c7d3d27dca5d5ca743d68b7f', 15 | 'pxETH': '0x9e0d7d79735e1c63333128149c7b616a0dc0bbdb' 16 | } 17 | 18 | liquidity_tokens = { 19 | 'PunkSwap': { 20 | 'ETH': { 21 | 'USDC': '0x6562e87944e4D6cCF9839C662db32E6b19f72cDe', 22 | 'USDT': '0xB12abB5bcB50c2aA6f1B54447046640010B33933', 23 | }, 24 | 'USDC': { 25 | 'USDT': '0x2307DBafED1464605E5CfC7fbC7aE761Aa527f45', 26 | } 27 | }, 28 | 'SkyDrome': { 29 | 'USDC': { 30 | 'USDT': '0xa631B2A2C3469aa1bF5dc49977207F378D16d7d8', 31 | } 32 | }, 33 | 'SyncSwap': { 34 | 'ETH': { 35 | 'USDT': '0x78ea8E533c834049dE625e05F0B4DeFfe9DB5f6e', 36 | 'USDC': '0x814A23B053FD0f102AEEda0459215C2444799C70', 37 | }, 38 | 'USDC': { 39 | 'ETH': '0x814A23B053FD0f102AEEda0459215C2444799C70', 40 | } 41 | }, 42 | 'SpaceFi': { 43 | 'ETH': { 44 | 'USDC': '0x6905C59Be1a7Ea32d1F257E302401eC9a1401C52', 45 | }, 46 | 'USDC': { 47 | 'USDT': '0x663864d52C38741001A73D270F4Da50005c647fA', 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/data/types.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from web3.contract import Contract 4 | from web3.types import TxParams 5 | from eth_typing import Address 6 | from web3 import Web3 7 | 8 | 9 | class Types: 10 | AmountOut = Callable[[str, Contract, int, str, Address, int, Web3], TxParams] 11 | SwapTransaction = Callable[[str, str, Contract, int, str, str, Address, int, Web3], TxParams] 12 | LiquidityTransaction = Callable[[str, Contract, int, str, Address, int, Web3], TxParams] 13 | LiquidityRemoveTransaction = Callable[[Web3, Contract, str, int, Address], TxParams] 14 | MintTransaction = Callable[[Web3, Contract, Address], TxParams] 15 | BridgeTransaction = Callable[[Contract, int, Web3], TxParams] 16 | -------------------------------------------------------------------------------- /src/utils/errors/exceptions.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from config import SLIPPAGE 4 | 5 | 6 | class CustomError(Exception): 7 | def __init__(self, error_type: str, error_message: str) -> None: 8 | self.error = f'{error_type}: {error_message}' 9 | super().__init__(self.error) 10 | 11 | 12 | class ErrorHandler: 13 | def __init__(self, ex: Exception, *args: Any) -> None: 14 | self.__ex = ex 15 | self.__name = args[0] 16 | 17 | def handle(self) -> CustomError: 18 | if 'execution reverted' in str(self.__ex) and 'MainBridge' in str(self.__name): 19 | error_type = 'NotEnoughMoneyError' 20 | error_message = 'Not enough money for MainBridge transaction' 21 | elif 'execution reverted' in str(self.__ex) and 'PunkSwap' in str(self.__name): 22 | error_type = 'PriceImpactTooHigh' 23 | error_message = f"Can't execute transaction with slippage {SLIPPAGE * 100}%" 24 | 25 | else: 26 | error_type = 'Unknown Error' 27 | error_message = str(self.__ex) 28 | 29 | return CustomError(error_type=error_type, error_message=error_message) 30 | -------------------------------------------------------------------------------- /src/utils/gas_checker.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | 3 | from loguru import logger 4 | from web3 import Web3 5 | 6 | from config import MAX_GWEI 7 | 8 | 9 | def get_gas() -> int: 10 | try: 11 | w3 = Web3(Web3.HTTPProvider('https://rpc.ankr.com/eth')) 12 | gas_price = w3.eth.gas_price 13 | gwei = w3.from_wei(gas_price, 'gwei') 14 | 15 | return gwei 16 | except Exception as error: 17 | logger.error(f"Error while getting gas price: {error}") 18 | 19 | 20 | def wait_gas() -> None: 21 | while True: 22 | gas = get_gas() 23 | 24 | if gas > MAX_GWEI: 25 | logger.info(f'Current GWEI: {gas} > {MAX_GWEI}') 26 | sleep(60) 27 | else: 28 | break 29 | -------------------------------------------------------------------------------- /src/utils/logger.py: -------------------------------------------------------------------------------- 1 | import loguru 2 | 3 | 4 | class Logger: 5 | def __init__(self, logger: loguru.logger) -> None: 6 | self.__logger = logger 7 | 8 | def info(self, __message: str, *args: object, **kwargs: object) -> None: 9 | self.__logger.info(f'{__message}', *args, **kwargs) 10 | 11 | def debug(self, __message: str, *args: object, **kwargs: object) -> None: 12 | self.__logger.debug(f'{__message}', *args, **kwargs) 13 | 14 | def error(self, __message: str, *args: object, **kwargs: object) -> None: 15 | self.__logger.error(f'{__message}', *args, **kwargs) 16 | 17 | def success(self, __message: str, *args: object, **kwargs: object) -> None: 18 | self.__logger.success(f'{__message}', *args, **kwargs) 19 | 20 | def warning(self, __message: str, *args: object, **kwargs: object) -> None: 21 | self.__logger.warning(f'{__message}', *args, **kwargs) 22 | -------------------------------------------------------------------------------- /src/utils/user/account.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from time import sleep, time 3 | 4 | from web3.exceptions import TransactionNotFound 5 | from web3.types import TxParams 6 | from web3 import Web3 7 | from eth_typing import ( 8 | Address, 9 | HexStr, 10 | ) 11 | 12 | from src.utils.data.chains import SCROLL 13 | from src.utils.user.utils import Utils 14 | 15 | 16 | @dataclass 17 | class Wallet: 18 | address: Address 19 | private_key: str 20 | 21 | 22 | class Account(Utils): 23 | def __init__(self, private_key: str, rpc: str = SCROLL.rpc) -> None: 24 | self.web3 = Web3(Web3.HTTPProvider(rpc)) 25 | self.account = self.web3.eth.account.from_key(private_key) 26 | self.account_address = self.account.address 27 | 28 | self.__wallet = Wallet(self.account_address, private_key) 29 | 30 | super().__init__() 31 | 32 | def get_wallet_balance(self, token: str, stable_address: str) -> float: 33 | if token.lower() != 'eth': 34 | contract = self.web3.eth.contract(address=Web3.to_checksum_address(stable_address), 35 | abi=self.load_abi('erc20')) 36 | balance = contract.functions.balanceOf(self.__wallet.address).call() 37 | else: 38 | balance = self.web3.eth.get_balance(self.__wallet.address) 39 | 40 | return balance 41 | 42 | def sign_transaction(self, tx: TxParams) -> HexStr: 43 | signed_tx = self.web3.eth.account.sign_transaction(tx, self.__wallet.private_key) 44 | raw_tx_hash = self.web3.eth.send_raw_transaction(signed_tx.rawTransaction) 45 | tx_hash = self.web3.to_hex(raw_tx_hash) 46 | return tx_hash 47 | 48 | def wait_until_tx_finished(self, tx_hash: HexStr, max_wait_time=180) -> bool: 49 | start_time = time() 50 | while True: 51 | try: 52 | receipts = self.web3.eth.get_transaction_receipt(tx_hash) 53 | status = receipts.get("status") 54 | if status == 1: 55 | self.logger.success(f"Transaction confirmed! {tx_hash}") 56 | return True 57 | elif status is None: 58 | sleep(0.3) 59 | else: 60 | self.logger.error(f"Transaction failed! {tx_hash}") 61 | return False 62 | except TransactionNotFound: 63 | if time() - start_time > max_wait_time: 64 | print(f'FAILED TX: {hash}') 65 | return False 66 | sleep(1) 67 | -------------------------------------------------------------------------------- /src/utils/user/utils.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Callable, 3 | Optional, 4 | Union, 5 | ) 6 | 7 | from random import uniform 8 | from asyncio import sleep 9 | import json 10 | 11 | from web3.contract import Contract 12 | from web3.types import TxParams 13 | from web3 import Web3 14 | import loguru 15 | 16 | from eth_typing import ( 17 | Address, 18 | HexStr, 19 | ) 20 | 21 | from src.utils.logger import Logger 22 | 23 | 24 | class Utils: 25 | def __init__(self) -> None: 26 | self.logger = Logger(loguru.logger) 27 | 28 | def load_contract(self, address: str, web3: Web3, abi_name: str) -> Optional[Contract]: 29 | if address is None: 30 | return 31 | 32 | address = web3.to_checksum_address(address) 33 | return web3.eth.contract(address=address, abi=self.load_abi(abi_name)) 34 | 35 | def get_decimals(self, contract_address: Contract, web3: Web3) -> int: 36 | contract = self.load_contract(contract_address, web3, 'erc20') 37 | decimals = contract.functions.decimals().call() 38 | return decimals 39 | 40 | async def approve_token(self, amount: float, private_key: str, from_token_address: str, spender: str, 41 | address_wallet: Address, web3: Web3) -> Optional[HexStr]: 42 | try: 43 | spender = web3.to_checksum_address(spender) 44 | contract = self.load_contract(from_token_address, web3, 'erc20') 45 | allowance_amount = self.check_allowance(web3, from_token_address, address_wallet, spender) 46 | 47 | if amount >= allowance_amount: 48 | self.logger.debug('🛠️ | Approving token...') 49 | tx = contract.functions.approve( 50 | spender, 51 | int(amount * 1.5) 52 | ).build_transaction({ 53 | 'chainId': web3.eth.chain_id, 54 | 'from': address_wallet, 55 | 'nonce': web3.eth.get_transaction_count(address_wallet), 56 | "gasPrice": web3.eth.gas_price, 57 | }) 58 | 59 | signed_tx = web3.eth.account.sign_transaction(tx, private_key=private_key) 60 | raw_tx_hash = web3.eth.send_raw_transaction(signed_tx.rawTransaction) 61 | tx_receipt = web3.eth.wait_for_transaction_receipt(raw_tx_hash) 62 | while tx_receipt is None: 63 | await sleep(1) 64 | tx_receipt = web3.eth.get_transaction_receipt(raw_tx_hash) 65 | tx_hash = web3.to_hex(raw_tx_hash) 66 | self.logger.success(f'✔️ | Token approved') 67 | await sleep(5) 68 | return tx_hash 69 | 70 | except Exception as ex: 71 | self.logger.error(f'Something went wrong | {ex}') 72 | 73 | def check_allowance(self, web3: Web3, from_token_address: str, address_wallet: Address, spender: str 74 | ) -> Optional[int]: 75 | try: 76 | contract = web3.eth.contract(address=web3.to_checksum_address(from_token_address), 77 | abi=self.load_abi('erc20')) 78 | amount_approved = contract.functions.allowance(address_wallet, spender).call() 79 | return amount_approved 80 | 81 | except Exception as ex: 82 | self.logger.error(f'Something went wrong | {ex}') 83 | 84 | def add_gas_price(self, web3: Web3) -> Optional[int]: 85 | try: 86 | gas_price = web3.eth.gas_price 87 | gas_price = int(gas_price * uniform(1.01, 1.02)) 88 | return gas_price 89 | except Exception as ex: 90 | self.logger.error(f'Something went wrong | {ex}') 91 | 92 | def setup_decimals(self, from_token: str, from_token_address: str, web3: Web3 93 | ) -> Union[int, Callable[[str, Web3], int]]: 94 | if from_token.lower() == 'eth' or from_token.lower() == 'weth': 95 | return 18 96 | else: 97 | return self.get_decimals(from_token_address, web3) 98 | 99 | def create_amount(self, from_token: str, from_token_address: str, web3: Web3, amount: float) -> int: 100 | decimals = self.setup_decimals(from_token, from_token_address, web3) 101 | amount = int(amount * 10 ** decimals) 102 | return amount 103 | 104 | @staticmethod 105 | def create_liquidity_remove_amount(balance: int, remove_all: bool, removing_percentage: float) -> int: 106 | if remove_all is True: 107 | amount = balance 108 | else: 109 | amount = int(balance * removing_percentage) 110 | 111 | return amount 112 | 113 | @staticmethod 114 | def add_gas_limit(web3: Web3, tx: TxParams) -> int: 115 | tx['value'] = 0 116 | gas_limit = web3.eth.estimate_gas(tx) 117 | return gas_limit 118 | 119 | @staticmethod 120 | def load_abi(name: str) -> str: 121 | with open(f'./assets/abi/{name}.json') as f: 122 | abi: str = json.load(f) 123 | return abi 124 | -------------------------------------------------------------------------------- /src/utils/wrappers/decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from asyncio import sleep 3 | 4 | from typing import ( 5 | Callable, 6 | Optional, 7 | Any, 8 | ) 9 | 10 | from loguru import logger 11 | 12 | from src.utils.errors.exceptions import ErrorHandler 13 | from src.utils.gas_checker import wait_gas 14 | 15 | from config import ( 16 | PAUSE_BETWEEN_RETRIES, 17 | CHECK_GWEI, 18 | RETRIES, 19 | ) 20 | 21 | 22 | def check_gas(func) -> Callable: 23 | def wrapper(*args, **kwargs) -> Callable: 24 | if CHECK_GWEI: 25 | wait_gas() 26 | return func(*args, **kwargs) 27 | 28 | return wrapper 29 | 30 | 31 | def retry(retries: int = RETRIES, delay: int = PAUSE_BETWEEN_RETRIES, backoff: float = 1.5) -> Callable: 32 | def decorator_retry(func: Callable) -> Callable: 33 | @wraps(func) 34 | async def wrapped(*args: Optional[Any], **kwargs) -> Optional[Callable]: 35 | for i in range(retries + 1): 36 | try: 37 | return await func(*args, **kwargs) 38 | except Exception as ex: 39 | if i == retries: 40 | logger.error(ErrorHandler(ex, *args).handle()) 41 | else: 42 | await sleep(delay * (backoff ** i)) 43 | 44 | return wrapped 45 | 46 | return decorator_retry 47 | -------------------------------------------------------------------------------- /wallets.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nftscripts/scroll/c8cadeb3a914effba7a0fb3cb085a20a181aea16/wallets.txt --------------------------------------------------------------------------------