How to use Pyth without SDK?

1. How to get Pyth Price ID?

Human: https://pyth.network/developers/price-feed-ids

API: https://benchmarks.pyth.network/v1/price_feeds/

For example BTC=0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, ETH=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace

2. How to fetch latest price?

https://hermes.pyth.network/api/latest_price_feeds?ids[]=0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43&ids[]=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace

To make it more easier for Python usage:

Copy
>>> from simplebase import *
>>> PYTHP.eth
3141.0950000000003
>>> PYTHP.btc
64786.00749999

The implementation:

Copy
class class_PYTHPRICE():
def load_conf(self, refresh=False):
if not self.conf:
if (not refresh) and os.path.isfile("/tmp/pyth.json"):
self.conf = json.load(open("/tmp/pyth.json"))
else:
self.conf = sess.get("https://benchmarks.pyth.network/v1/price_feeds/").json()
open("/tmp/pyth.json", "w").write(json.dumps(self.conf))
return self.conf

def __init__(self, cachetime=60, refresh=False):
self.conf = None
self.cache = {}
self.cachetime = cachetime
if refresh:
self.load_conf(refresh=True)

def __getattr__(self, token):
if token in self.cache and time.time()-self.cache[token][0]<self.cachetime:
return self.cache[token][1]
conf = self.load_conf()
pythid = [i for i in conf if i["attributes"]["generic_symbol"]==token.upper()+"USD"][0]["id"]
x = sess.get("https://hermes.pyth.network/api/latest_price_feeds?ids[]="+pythid)
p = x.json()[0]["price"]
price = float(p["price"])*10**(p["expo"])
self.cache[token] = [p["publish_time"], price]
return price
PYTHP = class_PYTHPRICE()

3. How to subscribe to real-time price feed?

websocket connect to wss://hermes.pyth.network/ws, send the message: {"type":"subscribe","ids":["0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"]}

Copy
# proxychains wscat --connect wss://hermes.pyth.network/ws
Connected (press CTRL+C to quit)
> {"type":"subscribe","ids":["0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"]}
< {"type":"response","status":"success"}
< {"type":"price_update","price_feed":{"id":"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace","price":{"price":"314558126642","conf":"204820667","expo":-8,"publish_time":1713754685},"ema_price":{"price":"314640770000","conf":"271902490","expo":-8,"publish_time":1713754685}}}
< {"type":"price_update","price_feed":{"id":"e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43","price":{"price":"6478190154574","conf":"3512845425","expo":-8,"publish_time":1713754685},"ema_price":{"price":"6482023900000","conf":"3546467800","expo":-8,"publish_time":1713754685}}}

4. How to get on-chain price?

Note: On-chain price wont be updated if nobody is pushing updates, different than ChainLink. Be careful of reading a stale price.

Contract address: https://docs.pyth.network/price-feeds/contract-addresses/evm

Copy
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}

function getPriceUnsafe(
bytes32 id
) external returns (Price memory price);

5. How to push price on-chain?

https://github.com/pyth-network/pyth-crosschain/tree/main/apps/price_pusher

Here is part of my implementation:

Copy
import websocket, sys
from math import fabs
from simplebase import *
... # more imports

pythids = [assetId2asset[i]["id"].replace("0x","") for i in tokenIds]

BAR1=4.8
BAR2=3

oldnonce = None
@lru_cache(10)
def get_onchain_prices(ts=None):
global oldnonce, olddata
nonce = eth_getNonce(RPC, MYADDR)
if nonce==oldnonce:
return olddata
oldnonce = nonce
print("fetch")
onchain_prices = batch_callfunction_decode(RPC, [[PYTH, "getPriceUnsafe(bytes32)", ec(["bytes32"], [bd(i)]) ] for i in pythids], ["(uint256,uint256,int256,uint256)"])
olddata = dict(zip(pythids, [i[0] for i in onchain_prices]))
return olddata

def process_push():
global oldnonce
oc = get_onchain_prices(int(time.time()))
prices = getprices(tokenIds, "https://hermes.pyth.network")
tosend = []
tmp = []
for myid, price, _, _ in prices:
token = assetId2asset[myid]
pythid = token["id"][2:]
onchainprice = oc[pythid]
diff = (price-onchainprice)/onchainprice*100
if diff>BAR2 or diff<-BAR2:
print("tosend:", myid, token["symbol"], diff)
tosend.append(pythid)
else:
tmp.append([pythid, fabs(diff)])
if len(tosend)<5:
tmp.sort(key=lambda i:i[1], reverse=True)
tosend += [i[0] for i in tmp[:5-len(tosend)]]
x = sess.get("https://hermes.pyth.network/api/latest_vaas?ids[]="+"&ids[]=".join(tosend))
sys.x = x
vaas = x.json()
updatefee = callfunction(RPC, PYTH, "getUpdateFee(bytes[])", ec(["bytes[]"], [[b64decode(i) for i in vaas]]))
print("updatefee:", updatefee)
tx = maketx(PYTH, "0xef9e5e28"+ec(["bytes[]"], [[b64decode(i) for i in vaas]]), oldnonce, 5*10**8, sendamount=updatefee, showgas=True, writetx=False)
waittx(tx)
oldnonce = 0

def on_message(wsapp, message):
oc = get_onchain_prices(int(time.time()))
try:
message = json.loads(message)
if message["type"]=="price_update":
pythid = message["price_feed"]["id"]
symbol = pythid2token[pythid]["pythSymbol"]
#ts = message["price_feed"]["price"]["publish_time"]
price = int(message["price_feed"]["price"]["price"])
oldprice = oc[pythid]
diff = (price-oldprice)/oldprice*100
if diff>BAR1 or diff<-BAR1:
print(symbol, price, diff)
process_push()
except:
traceback.print_exc()
raise

def on_open(wsapp):
print("open")
wsapp.send(json.dumps({"type":"subscribe","ids":pythids})+"\r\n")

if 1:
wsapp = websocket.WebSocketApp("wss://hermes.pyth.network/ws", on_message=on_message, on_open=on_open)
wsapp.run_forever()