Last active
April 13, 2022 09:50
-
-
Save normanlmfung/125e5fe5bd9f589018250b2bb5dfd9af to your computer and use it in GitHub Desktop.
web3 cronos PART 2 Swap SINGLE for CRO
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import datetime | |
import json | |
from web3 import Web3 | |
my_wallet_address = "xxx" | |
wallet_address = Web3.toChecksumAddress(my_wallet_address) | |
private_key = "xxx" | |
cronos_mainnet_rpc = "https://evm.cronos.org" | |
w3 = Web3(Web3.HTTPProvider(cronos_mainnet_rpc)) | |
chain_id = w3.eth.chainId | |
router_address = Web3.toChecksumAddress("0x145863Eb42Cf62847A6Ca784e6416C1682b1b2Ae") | |
router_abi = json.loads('[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factory","internalType":"address"},{"type":"address","name":"_WETH","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"amountADesired","internalType":"uint256"},{"type":"uint256","name":"amountBDesired","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"},{"type":"uint256","name":"liquidity","internalType":"uint256"}],"name":"addLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountTokenDesired","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factory","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"getAmountIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"getAmountOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"reserveIn","internalType":"uint256"},{"type":"uint256","name":"reserveOut","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsIn","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"getAmountsOut","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"quote","inputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"reserveA","internalType":"uint256"},{"type":"uint256","name":"reserveB","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidity","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETH","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountToken","internalType":"uint256"},{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountETH","internalType":"uint256"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountTokenMin","internalType":"uint256"},{"type":"uint256","name":"amountETHMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"amountA","internalType":"uint256"},{"type":"uint256","name":"amountB","internalType":"uint256"}],"name":"removeLiquidityWithPermit","inputs":[{"type":"address","name":"tokenA","internalType":"address"},{"type":"address","name":"tokenB","internalType":"address"},{"type":"uint256","name":"liquidity","internalType":"uint256"},{"type":"uint256","name":"amountAMin","internalType":"uint256"},{"type":"uint256","name":"amountBMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bool","name":"approveMax","internalType":"bool"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapETHForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactETHForTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForETH","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapExactTokensForTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactETH","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256[]","name":"amounts","internalType":"uint256[]"}],"name":"swapTokensForExactTokens","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"deadline","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]') | |
''' | |
Exception has occurred: KeyError | |
'name' | |
File "D:\dev\samples\fiverr\delivery_01_liquidity_and_swap (1)\delivery_01_liquidity_and_swap\simple_swap.py", line 57, in <module> | |
in_quantity = router_contract.functions.getAmountsIn(out_quantity, path).call()[0] | |
''' | |
i : int = 0 | |
for entry in router_abi: | |
if 'name' not in entry: | |
entry['name'] = f"dummy_name_{i}" | |
i = i+1 | |
router_contract = w3.eth.contract(address=router_address, abi=router_abi) | |
''' | |
You can take any ERC20 token contract ABI. For example https://etherscan.io/address/0xc3761eb917cd790b30dad99f6cc5b4ff93c4f9ea#code | |
Uses are ... | |
a. "decimals" | |
b. transfer - Make sure for "transfer", payable set to true!!! (This is for withdrawal/transfers!) | |
''' | |
EIP20_ABI = '[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]' | |
wcro_address = Web3.toChecksumAddress("0x5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23") | |
single_address = Web3.toChecksumAddress("0x0804702a4E749d39A35FDe73d1DF0B1f1D6b8347") | |
usdc_address = Web3.toChecksumAddress("0xc21223249CA28397B4B6541dfFaEcC539BfF0c59") | |
usdt_address = Web3.toChecksumAddress("0x66e428c3f67a68878562e79A0234c1F83c208770") | |
wcro_contract = w3.eth.contract(address=wcro_address, abi=EIP20_ABI) | |
single_contract = w3.eth.contract(address=single_address, abi=EIP20_ABI) | |
usdc_contract = w3.eth.contract(address=usdc_address, abi=EIP20_ABI) | |
usdt_contract = w3.eth.contract(address=usdt_address, abi=EIP20_ABI) | |
wcro_decimals = wcro_contract.functions.decimals().call() | |
single_decimals = single_contract.functions.decimals().call() | |
usdc_decimals = usdc_contract.functions.decimals().call() | |
usdt_decimals = usdt_contract.functions.decimals().call() | |
ONE_BILLION : int = 1000000000 | |
approval_amount_in_usdt : int = 100 | |
cro_usdt = 0.42 # rate on 20220413 | |
approval_amount_in_cro : int = int(approval_amount_in_usdt * cro_usdt) | |
default_gas_limit = 800_000 | |
gas_price = w3.eth.gasPrice | |
''' | |
If you're interacting with CRONOS chain, | |
allowance_amount_in_wei = approval_amount_in_cro * ONE_BILLION * ONE_BILLION | |
If you're interacting with Etherum chain, | |
allowance_amount_in_wei = approval_amount_in_eth * ONE_BILLION * ONE_BILLION | |
''' | |
allowance_amount_in_wei = approval_amount_in_cro * ONE_BILLION * ONE_BILLION | |
# STEP 1. Approval transaction | |
''' | |
wallet_address gives WCRO allowance to router_address. | |
"wad" argument of "approve" function, is it Wei or Gwei? https://ethereum.stackexchange.com/questions/126085/smart-contract-approve-function-wad | |
''' | |
approval_transaction = wcro_contract.functions.approve( # If you want to swap USDT to SINGLE, change here. | |
router_address, | |
allowance_amount_in_wei, | |
) | |
nonce = w3.eth.getTransactionCount(wallet_address) | |
approval_trx_params = approval_transaction.buildTransaction({ | |
'from': wallet_address, | |
'gas': default_gas_limit, # here we use the default gas limit. See the swap trx below for estimated gas | |
'gasPrice': gas_price, | |
'nonce': nonce, | |
}) | |
signed = w3.eth.account.sign_transaction(approval_trx_params, private_key) | |
approval_trx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) | |
receipt = w3.eth.waitForTransactionReceipt(approval_trx_hash) | |
assert receipt["status"] == 1 # trx success | |
assert wcro_contract.functions.allowance(wallet_address, router_address).call() == allowance_amount_in_wei # If you change from_ccy, change the contract!!! | |
# STEP 2. The actual transaction: Buy 100 SINGLE with WCRO | |
# There is no SINGLE/WCRO pool, so we'll use WCRO/USDC and SINGLE/USDC. | |
# path = [usdt_address, usdc_address, single_address] # If you want to swap USDT to SINGLE, change here. | |
path = [wcro_address, usdc_address, single_address] | |
out_quantity = int(100 * 10**single_decimals) | |
in_quantity = router_contract.functions.getAmountsIn(out_quantity, path).call()[0] | |
print(f"in_quantity: {in_quantity}, out_quantity: {out_quantity}") | |
print(f"Number of WCRO tokens needed: {in_quantity / 10**wcro_decimals}, to get {out_quantity / 10**single_decimals} SINGLE tokens") | |
# Let say we want a max slippage of 0.5% | |
slippage = 0.5 / 100 | |
max_in_quantity = int(in_quantity * (1 + slippage)) | |
print(f"WCRO Max in Quantity: {max_in_quantity}") | |
# Build transaction | |
deadline = int(datetime.now().timestamp() + 180) # 3 mn for this example | |
function_params = router_contract.functions.swapTokensForExactTokens( | |
out_quantity, | |
max_in_quantity, | |
path, | |
wallet_address, | |
deadline, | |
) | |
nonce = w3.eth.getTransactionCount(wallet_address) | |
trx_params = function_params.buildTransaction( | |
{ | |
'from': wallet_address, | |
'gas': default_gas_limit, | |
'gasPrice': gas_price, | |
'nonce': nonce, | |
'chainId': chain_id, | |
'value': 0, | |
} | |
) | |
''' | |
estimateGas returns Wei or Gwei? https://ethereum.stackexchange.com/questions/99763/whats-exactly-ether-unit-return-web3-eth-estimategas-wei-gwei/126027#126027 | |
''' | |
ONE_BILLION : int = 1000000000 | |
estimate_gas_per_unit = w3.eth.estimateGas(trx_params) # return in "unit" | |
gas_price = w3.eth.gasPrice # return in "Wei per unit" | |
gas_estimate_in_wei = estimate_gas_per_unit * gas_price | |
gas_price_in_coin = gas_estimate_in_wei / (ONE_BILLION * ONE_BILLION) # "coin" for CRONOS chain is "CRO". On Ethereum Mainnet, that's "ETH"! It's different. | |
print(f"gas_price_in_coin: {gas_price_in_coin}") | |
# Adding a security margin of 10% | |
gas_limit = int(estimate_gas_per_unit * 1.1) | |
print(f"gas_limit with additional buffer: {gas_limit}") | |
# And updating the transaction | |
trx_params["gas"] = gas_limit | |
print(f"Transaction parameters: {trx_params}") | |
# Sign and send the trx | |
signed = w3.eth.account.sign_transaction(trx_params, private_key) | |
trx_hash = w3.eth.sendRawTransaction(signed.rawTransaction) | |
print(f"trx_hash: {trx_hash.hex()}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment