Skip to content

Instantly share code, notes, and snippets.

@normanlmfung
Last active April 13, 2022 09:50
Show Gist options
  • Save normanlmfung/125e5fe5bd9f589018250b2bb5dfd9af to your computer and use it in GitHub Desktop.
Save normanlmfung/125e5fe5bd9f589018250b2bb5dfd9af to your computer and use it in GitHub Desktop.
web3 cronos PART 2 Swap SINGLE for CRO
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