web3py with 0x API - python

I have been trying to perform a swap on Ropsten using the 0x API to get the best quote but every time I try to perform a swap the transaction fails without any feedback(at least from Etherscan) while the approve tx goes through fine.
I am probably missing something here, but I'm getting blind to keep staring at my code.
import requests
import json
import time
from web3 import Web3
walletAddress = 'mywalletaddress'
wpk = 'myprivatekey'
# Setup Ropsten provider
w3 = Web3(Web3.HTTPProvider('https://ropsten.infura.io/v3/'))
# Get the nonce to prevent from sending the transaction twice
nonce = w3.eth.getTransactionCount(walletAddress)
print('Nonce:', nonce)
# Store the generic ERC20 abi for later use
ABI = json.load(open('abi.json'))
# ROPSTEN ADDRESSES
WETH = Web3.toChecksumAddress('0xc778417e063141139fce010982780140aa0cd5ab')
DAI = Web3.toChecksumAddress('0xad6d458402f60fd3bd25163575031acdce07538d')
USDC = Web3.toChecksumAddress('0x07865c6e87b9f70255377e024ace6630c1eaa37f')
def calcAmount(initialAmount, contractAddress):
contract = w3.eth.contract(address=contractAddress, abi=ABI)
decimals = contract.functions.decimals().call()
amount = int(initialAmount * 10**decimals)
return amount
print(decimals, amount)
def getTokenSymbol(contractAddress):
contract = w3.eth.contract(address=contractAddress, abi=ABI)
tokenSymbol = contract.functions.symbol().call()
return tokenSymbol
print('Symbol:', tokenSymbol)
# Define query parameters
initialAmount = 100
slippage = '0.01' # 0.01 = 1% slippage
sellAmount = calcAmount(initialAmount, USDC)
print('Sell:', sellAmount)
def approve(token, spenderAddress, walletAddress, wpk):
token = Web3.toChecksumAddress(token)
contract = w3.eth.contract(address=token, abi=ABI)
spender = Web3.toChecksumAddress(spenderAddress)
max_amount = Web3.toWei(2**64 - 1, 'ether')
nonce = w3.eth.getTransactionCount(walletAddress)
tx = contract.functions.approve(spender, max_amount).buildTransaction({
'from': walletAddress,
'nonce': nonce
})
signed_tx = w3.eth.account.signTransaction(tx, wpk)
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
print("Approve", w3.toHex(tx_hash))
w3.eth.wait_for_transaction_receipt(tx_hash)
def getQuote(buyToken, slippage, sellToken, sellAmount):
# Make the query https://ropsten.api.0x.org/
query = 'https://ropsten.api.0x.org/swap/v1/quote?buyToken={}&slippage={}&sellToken={}&sellAmount={}'.format(buyToken, slippage, sellToken, sellAmount)
# Get the query and store the response in data
response = requests.get(query)
data = response.json()
# Print a prettiefied version of the response (optional)
print(json.dumps(data, indent=2))
# Convert the buyAmmount to integer
buyAmount = int(data['buyAmount'])
# Convert the buyAmount to a readable number
finalPrice = calcAmount(buyAmount, buyToken)
# Get the symbol of the 2 tokens
buyTokenSymbol = getTokenSymbol(buyToken)
sellTokenSymbol = getTokenSymbol(sellToken)
print(sellTokenSymbol, '->', buyTokenSymbol)
approve(data['sellTokenAddress'], data['allowanceTarget'], walletAddress, wpk)
tx = {
"from": walletAddress,
"to": Web3.toChecksumAddress(data['to']),
"data": data['data'],
"value": 0,
"gas": int(data['gas']),
"gasPrice": int(data['gasPrice']),
"nonce": nonce + 1,
"chainId": data['chainId']
}
# sign the transaction
signed_tx = w3.eth.account.sign_transaction(tx, wpk)
#
# send transaction
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
#
# get transaction hash
print(w3.toHex(tx_hash))
# Use only for testing the query
getQuote(DAI, slippage, USDC, sellAmount)
As you can see I am trying to swap USDC for DAI using the 0x API.
The first tx generated is approved
Etherscan approval tx
But the actual swap fails without any feedback
Etherscan swap tx
I honestly don't know what to try anymore...I've been digging both documentation and reading anything I could find on Stackoverflow without any luck.
Any help is greatly appreciate.

Gas Limit & Usage by Txn: 134,523 | 132,823 (98.74%)
Im 99,9% sure it is because ur running out of gas, try to increase it

Related

web3 python getAmountsOut execution reverted

I managed to set up this function that returns the token value in USD of a contract, everything works fine in some contracts, but in other contracts I get the error "execution reverted" when calling the function "getAmountsOut" does anyone have any idea what it can be? I get the contracts from the same place in bscscan and for some it works and for some it doesn't.
bsc = 'https://bsc-dataseed.binance.org/'
web3 = Web3(Web3.HTTPProvider(bsc))
panRouterContractAddress = '0x10ED43C718714eb63d5aA57B78B54704E256024E'
panabi = '[{...}]'
contractbuy = web3.eth.contract(address=panRouterContractAddress, abi=panabi) # PANCAKESWAP
def get_price(self):
try:
# Base currency token instantiate and ABI
baseCurrency = web3.toChecksumAddress("0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56") # BUSD
# get symbol token
sellTokenContract = web3.eth.contract(self.token_to_buy, abi=sellAbi)
symbol = sellTokenContract.functions.symbol().call()
# Selling token instance and contract
tokenToSell = web3.toChecksumAddress(self.token_to_buy) # TOKEN PRICE
sellAmount = web3.toWei(1, 'ether')
# Calculate minimum amount of tokens to receive
amountOut = contractbuy.functions.getAmountsOut(sellAmount, [tokenToSell, baseCurrency]).call() # Error here
amountOutMin = web3.fromWei(int(amountOut[1]), 'ether')
print("Valor do token: ", str(amountOutMin))
str_format = "{:." + self.decs_usd + "f}"
return float(str_format.format(amountOutMin)), symbol
except Exception as ex:
print("ERRO:ver_preco:", ex)
return "", ""
contracts ok:
https://bscscan.com/address/0x5649e392a1bac3e21672203589adf8f6c99f8db3
https://bscscan.com/address/0x00e1656e45f18ec6747f5a8496fd39b50b38396d
contracts error:
https://bscscan.com/address/0x9376e6b29b5422f38391a2a315623cb853c3c68e
https://bscscan.com/address/0xe786d5a4b985bfe5e371f8e94413cfb440f6618a
if anyone can explain to me why this error occurs in some tokens and not others, and how I can get error message details besides "execution reverted"
The latter specified token addresses fail with the error because there is no existing Pancakeswap pair contract for the specified address combination.
Another way of checking the existence of a pair contract is by calling the factory's getPair() function.
// returns the existing pair contract address 0x5f7A2a0A32C0616898cA7957C1D58BC92a7E2f6f
getPair(
// ZDC token specified in your question as OK
"0x5649e392A1BAC3e21672203589aDF8f6C99f8dB3",
// BUSD
"0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"
)
// returns the zero address 0x0000000000000000000000000000000000000000
// because there's no pair contract for these specified tokens
getPair(
// DGZV token specified in your question as NOTOK
"0x9376E6B29b5422f38391A2a315623cB853c3c68e",
// BUSD
"0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56"
)

Web3 Signing transaction issue Non-hexadecimal digit found (Python)

I am trying to complete a transaction of buying a token in binance smart chain and I am getting an error at sign transaction part which I am unable to resolve.
Code:
PancakeABI = open('pancakeABI','r').read().replace('\n','')
bsc="https://bsc-dataseed.binance.org/"
web3 = Web3(Web3.HTTPProvider(bsc))
contract_id = web3.toChecksumAddress(tokaddr)
#Checking balance for test
balance = web3.eth.get_balance(sender_address)
humanReadable = web3.fromWei(balance,'ether')
print(humanReadable)
#Setup the contract
contract = web3.eth.contract(address=router_address, abi=PancakeABI)
nonce = web3.eth.get_transaction_count(sender_address)
start = time.time()
print(web3.toWei('0.02','ether'))
#Send transaction
pancakeswap2_txn = contract.functions.swapExactETHForTokens(
0, # here setup the minimum destination token you want to have, you can do some math, or you can put a 0 if you don't want to care
[spend,contract_id],
sender_address,
(int(time.time()) + 1000000)
).buildTransaction({
'from': sender_address,
'value': web3.toWei(0.02,'ether'),#This is the Token(BNB) amount you want to Swap from
'gas': 250000,
'gasPrice': web3.toWei('5','gwei'),
'nonce': nonce,
})
#Sign Transaction
signed_txn = web3.eth.account.sign_transaction(pancakeswap2_txn, private_key=private)
tx_token = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
print(web3.toHex(tx_token))
Here's the error that I am recieving:
Issue Resolved:
Was using seed phrase as the private key.
The actual private key can be obtained this way:
go to metamask, click the 3 buttons in the corner and click account
details and export key.

How to continuously keep track of balance of a particular token in my wallet

I'm trying to create a bot which keeps a track of a particular token in my wallet. As soon as it detects the token, it should send the token to another address. I've written the code but i don't know why my while loop doesn't work. The code kind of skips the while loop and creates the transaction at the end anyway which results in an error since there is no token to transfer. The script should be stuck in a loop until there is some token balance but it isn't happening.I'm running this on a VS Code terminal.
from web3 import Web3
import json
bsc = "https://bsc-dataseed.binance.org/"
web3 = Web3(Web3.HTTPProvider(bsc))
print(web3.isConnected())
main_address = "wallet to be tracked"
contract_address = "token contract address"
abi = json.loads('the abi')
contract = web3.eth.contract(address=contract_address, abi = abi)
balanceOfToken = contract.functions.balanceOf(main_address).call()
print(web3.fromWei(balanceOfToken, 'ether'))
while(True):
balanceOfToken = contract.functions.balanceOf(main_address).call()
print(balanceOfToken)
if(balanceOfToken > web3.fromWei(0.5, 'ether')):
break
continue
second_address = "the other wallet address"
main_key = "private key of first wallet"
nonce = web3.eth.getTransactionCount(main_address)
token_tx = contract.functions.transfer(my_address, balanceOfToken).buildTransaction({
'chainId':56, 'gas': 90000, 'gasPrice': web3.toWei('5', 'gwei'), 'nonce':nonce
})
signed_tx = web3.eth.account.signTransaction(token_tx, main_key)
web3.eth.sendRawTransaction(signed_tx.rawTransaction)
print(contract.functions.balanceOf(my_address).call() + " " + contract.functions.name().call())
The value of web3.fromWei(0.5, 'ether') is Decimal('5E-19') (from trying out the API myself).
The value of balanceOfToken is 10^-18 (from discussion in comments).
Since 10^-18 is bigger then 5 * 10^-19, the condition if(balanceOfToken > web3.fromWei(0.5, 'ether')) evaluates to True and the loop exits.

REST request to App Store Connect API response with '401' | 'NOT_AUTHORIZED'

we launched an iOS-App and I want to grab some Information (e.g. Installations, Updates, Reviews) from the App Store Connect API.
I create an JSON Web Token as described in the official Apple documentation: Link
Afterwards I make a request with the token in the header. Now I get an '401' | 'NOT_AUTHORIZED' each time as an answer, see the following picture:
REST Response
In the following snippets you can see my python code (I tried to solve it in Python and R, but the result is always the same).
First, I create an JWT:
from datetime import datetime, timedelta
from jose import jwt, jws
import ecdsa
KEY_ID = "XXXXXXXXXX"
ISSUER_ID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
PRIVATE_KEY = open('AuthKey_XXXXXXXXXX.p8', 'r').read()
TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp() * 1000)
claim = {"iss" : ISSUER_ID,
"exp" : TIMESTAMP,
"aud" : "appstoreconnect-v1"}
header = {
"alg": "ES256",
"kid": KEY_ID,
"typ": "JWT"
}
# Create the JWT
encoded = jwt.encode(claim, PRIVATE_KEY, algorithm='ES256', headers=header)
Now when I print encoded, I get to following JWT (looks valid for me):
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlhYWFhYWFhYWFgifQ.eyJpc3MiOiJYWFhYWFhYWC1YWFhYLVhYWFgtWFhYWC1YWFhYWFhYWFhYWFgiLCJleHAiOjE1NDUzOTc1MTQ1ODAsImF1ZCI6ImFwcHN0b3JlY29ubmVjdC12MSJ9.eTl6iaAW-Gp67FNmITrWCpLTtJzVdLYXIl5_KKgqaNgzwyGo7npBOBo9_u5PtLNnssQFEwJWbPND-6Ww5ACgEg'
Even if I decode the first two parts of the JWT via Base64 I get the right Header (it also contains the right algorithm for encoding: 'alg': 'ES256') and Claim:
from jose.utils import base64url_decode
print(base64url_decode(b'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IlhYWFhYWFhYWFgifQ'))
print(base64url_decode(b'eyJpc3MiOiJYWFhYWFhYWC1YWFhYLVhYWFgtWFhYWC1YWFhYWFhYWFhYWFgiLCJleHAiOjE1NDUzOTc1MTQ1ODAsImF1ZCI6ImFwcHN0b3JlY29ubmVjdC12MSJ9'))
See the following picture: Output Base64 Decoding
So now, that I'm think that the JWT-Object is ready I send the request to the API:
import requests
JWT = 'Bearer ' + encoded
URL = 'https://api.appstoreconnect.apple.com/v1/apps'
HEAD = {'Authorization': JWT}
print(HEAD)
R = requests.get(URL, headers=HEAD)
R.json()
And now we can see my problem, see the picture: Header | REST Response
Please note that I have hidden the KEY_ID, ISSUER_ID and PRIVATE_KEY for the example.
Your token contains an expiry time
"exp": 1545397514580,
which equals September 12th, 50941.
When I delete the last three digits
"exp": 1545397514,
I get December 21st, 2018
which makes much more sense.
Change that line
TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp() * 1000)
to
TIMESTAMP = int( (datetime.now() - timedelta(minutes = 45)).timestamp())
exp is a timestamp that is defined as seconds since 01.01.1970 00:00
See also here
First of all, please don't open files without context managers. This string:
PRIVATE_KEY = open('AuthKey_XXXXXXXXXX.p8', 'r').read()
should be:
with open('AuthKey_XXXXXXXXXX.p8', 'r') as f:
PRIVATE_KEY = f.read()
It save you from many problems with unclosed files in future.
Then, check what token you've read from file. Is it correct?
The next problem I see is timestamp. "The token's expiration time, in Unix epoch time;" You provide it in milliseconds, I guess.
Here is working solution for me. Without delay it return 401 error
KEY_ID = "xxxxx"
ISSUER_ID = "xxxxx"
EXPIRATION_TIME = int(round(time.time() + (20.0 * 60.0))) # 20 minutes timestamp
PATH_TO_KEY = '../credentials/AuthKey_xxxxx.p8'
with open(PATH_TO_KEY, 'r') as f:
PRIVATE_KEY = f.read()
header = {
"alg": "ES256",
"kid": KEY_ID,
"typ": "JWT"
}
payload = {
"iss": ISSUER_ID,
"exp": EXPIRATION_TIME,
"aud": "appstoreconnect-v1"
}
# Create the JWT
token = jwt.encode(header, payload, PRIVATE_KEY)
JWT = 'Bearer ' + token.decode()
HEAD = {'Authorization': JWT}
# Without delay I got 401 error
time.sleep(5)
URL = 'https://api.appstoreconnect.apple.com/v1/apps';
r = requests.get(URL, params={'limit': 200}, headers=HEAD)

Python 3, Ethereum - how to send ERC20 Tokens?

i have some script using to sending Ethers from address to addres. Im using Parity, and Python 3.6. It is using Flask looks like:
from flask import Flask, render_template, json, request
import urllib
import requests
import binascii
from decimal import *
app = Flask(__name__)
def Eth(method,params=[]):
data = {"method":method,"params":params,"id":1,"jsonrpc":"2.0"}
headers = {'Content-type': 'application/json'}
r = requests.post(ethrpc, data=json.dumps(data), headers=headers)
r = r.text
response = json.loads(r)
return(response)
hot = str("XXXXXXX")
#app.route('/')
def index():
ethnumbers = int(10)**int(18)
hot = str("XXXXX")
balance = Eth("eth_getBalance",[hot,'latest'])
balance = balance["result"]
balance = int(balance, 16)
balance = float(balance)
balance = balance / ethnumbers
balance = str(balance)
return render_template('index.html',hot = hot,balance=balance)
#app.route('/send/',methods=['POST','GET'])
def send():
getcontext().prec = 28
ethnumbers = Decimal(10)**Decimal(18)
print(ethnumbers)
if request.method == "POST":
_myaddress = request.form['_myaddress']
_youraddress = request.form['_youraddress']
_amount = request.form['_amount']
_gas = request.form['_gas']
_gas = hex(int(_gas))
passy = str("XXXXXXXXX")
getcontext().prec = 28
_amount = Decimal(_amount)
getcontext().prec = 28
_amount = _amount * ethnumbers
getcontext().prec = 28
_amount = int(_amount)
_amount = hex(_amount)
r = [{'from':_myaddress,"to":_youraddress,"value":_amount,"gas":_gas,},str("XXXXXXXXXX!")]
print(r)
json.dumps(r)
resultio = Eth("personal_sendTransaction",r)
try:
resultio["result"]
return render_template('sent.html',resultio=resultio["result"])
except: KeyError
return render_template('sent.html',resultio=resultio["error"]["message"])
else:
return render_template('index.html')
if __name__ == "__main__":
app.run()
Im pretty sure, that i have to use "data" to do this, but i have no idea how to send via this script ERC20 tokens. Structure of tokens transaction looks like "my address -> token address -> token receiver".
Any ideas?
Guys it was simpler than it looks like.
You just have to put:
contract address
as receiver and make a long "data" field, it represents string as:
// method name, its constans in erc20
0xa9059cbb
//receiver address (you have to do it without "0x" because its needed only when
//you have to tell "im using hexadecimal". You did it above, in method field.
//so, receiver address:
5b7b3b499fb69c40c365343cb0dc842fe8c23887
// and fill it with zeros, it have to be lenght 64. So fill rest of address
0000000000000000000000005b7b3b499fb69c40c365343cb0dc842fe8c23887
// then you need amount, please convert it to hexadecimal, delete "0x" and
// remember, you need to integer first, so if token has 18 "decimals" it need
// to be amount / 10**18 first!!
//1e27786570c272000 and fill that with zeros, like above:
000000000000000000000000000000000000000000000001e27786570c272000
//just add string, to string, to string, and you have data field:
0xa9059cbb0000000000000000000000005b7b3b499fb69c40c365343cb0dc842fe8c23887000000000000000000000000000000000000000000000001e27786570c272000
Sample transaction: https://etherscan.io/tx/0x9c27df8af24e06edb819a8d7a380f548fad637de5edddd6155d15087d1619964
web3.py is definitely the way to go. If you want to do it by hand and you just want to call the standard ERC-20 transfer method, the from address should stay the same, the to address should be the token contract, and then the data should be the following concatenated together and formatted as hexadecimal:
The first 4 bytes of the keccak256 hash of "transfer(address,uint256)", which is the function's signature.
The address of the recipient, left-zero-padded to be 32 bytes.
The amount to be transferred. (Be sure to take into account the token's decimals... 1 token is often 10**18, but the number of decimal places varies from token to token and can be retrieved by calling the decimals() function.) This should also be formatted as a 32-byte number (so left-zero-padded).
web3.py would be much easier. :-) Something like:
web3.eth.contract(address, abi=standard_token_abi).sendTransaction({
'from': from_address
}).transfer(to_address, amount)

Categories