mookim

mookim

mookim.eth

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:

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

The implementation:

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"]}

# 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

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:

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() 

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.